Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / corlib / System.Text / Encoding.cs
1 /*
2          * Encoding.cs - Implementation of the "System.Text.Encoding" class.
3  *
4  * Copyright (c) 2001, 2002  Southern Storm Software, Pty Ltd
5  * Copyright (c) 2002, Ximian, Inc.
6  * Copyright (c) 2003, 2004 Novell, Inc.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  * OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 namespace System.Text
28 {
29
30 using System;
31 using System.Reflection;
32 using System.Globalization;
33 using System.Security;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36
37 [Serializable]
38 [ComVisible (true)]
39 public abstract class Encoding : ICloneable
40 {
41         // Code page used by this encoding.
42         internal int codePage;
43         internal int windows_code_page;
44         bool is_readonly = true;
45         
46         protected static readonly byte[] empty = new byte[0];
47
48         // Constructor.
49         protected Encoding ()
50         {
51         }
52
53 #if ECMA_COMPAT
54         protected internal
55 #else
56         protected
57 #endif
58         Encoding (int codePage)
59         {
60                 this.codePage = windows_code_page = codePage;
61
62                 switch (codePage) {
63                 default:
64                         // MS has "InternalBestFit{Decoder|Encoder}Fallback
65                         // here, but we dunno what they are for.
66                         decoder_fallback = DecoderFallback.ReplacementFallback;
67                         encoder_fallback = EncoderFallback.ReplacementFallback;
68                         break;
69                 case 20127: // ASCII
70                 case 54936: // GB18030
71                         decoder_fallback = DecoderFallback.ReplacementFallback;
72                         encoder_fallback = EncoderFallback.ReplacementFallback;
73                         break;
74                 case 1200: // UTF16
75                 case 1201: // UTF16
76                 case 12000: // UTF32
77                 case 12001: // UTF32
78                 case 65000: // UTF7
79                 case 65001: // UTF8
80                         decoder_fallback = DecoderFallback.StandardSafeFallback;
81                         encoder_fallback = EncoderFallback.StandardSafeFallback;
82                         break;
83                 }
84         }
85
86         // until we change the callers:
87         internal static string _ (string arg) {
88                 return arg;
89         }
90
91         DecoderFallback decoder_fallback;
92         EncoderFallback encoder_fallback;
93
94         [ComVisible (false)]
95         public bool IsReadOnly {
96                 get { return is_readonly; }
97         }
98
99         [ComVisible (false)]
100         public virtual bool IsSingleByte {
101                 get { return false; }
102         }
103
104         [ComVisible (false)]
105         public DecoderFallback DecoderFallback {
106                 get { return decoder_fallback; }
107                 set {
108                         if (IsReadOnly)
109                                 throw new InvalidOperationException ("This Encoding is readonly.");
110                         if (value == null)
111                                 throw new ArgumentNullException ();
112                         decoder_fallback = value;
113                 }
114         }
115
116         [ComVisible (false)]
117         public EncoderFallback EncoderFallback {
118                 get { return encoder_fallback; }
119                 set {
120                         if (IsReadOnly)
121                                 throw new InvalidOperationException ("This Encoding is readonly.");
122                         if (value == null)
123                                 throw new ArgumentNullException ();
124                         encoder_fallback = value;
125                 }
126         }
127
128         internal void SetFallbackInternal (EncoderFallback e, DecoderFallback d)
129         {
130                 if (e != null)
131                         encoder_fallback = e;
132                 if (d != null)
133                         decoder_fallback = d;
134         }
135
136         // Convert between two encodings.
137         public static byte[] Convert (Encoding srcEncoding, Encoding dstEncoding,
138                                                                  byte[] bytes)
139         {
140                 if (srcEncoding == null) {
141                         throw new ArgumentNullException ("srcEncoding");
142                 }
143                 if (dstEncoding == null) {
144                         throw new ArgumentNullException ("dstEncoding");
145                 }
146                 if (bytes == null) {
147                         throw new ArgumentNullException ("bytes");
148                 }
149                 return dstEncoding.GetBytes (srcEncoding.GetChars (bytes, 0, bytes.Length));
150         }
151         public static byte[] Convert (Encoding srcEncoding, Encoding dstEncoding,
152                                                                  byte[] bytes, int index, int count)
153         {
154                 if (srcEncoding == null) {
155                         throw new ArgumentNullException ("srcEncoding");
156                 }
157                 if (dstEncoding == null) {
158                         throw new ArgumentNullException ("dstEncoding");
159                 }
160                 if (bytes == null) {
161                         throw new ArgumentNullException ("bytes");
162                 }
163                 if (index < 0 || index > bytes.Length) {
164                         throw new ArgumentOutOfRangeException
165                                 ("index", _("ArgRange_Array"));
166                 }
167                 if (count < 0 || (bytes.Length - index) < count) {
168                         throw new ArgumentOutOfRangeException
169                                 ("count", _("ArgRange_Array"));
170                 }
171                 return dstEncoding.GetBytes (srcEncoding.GetChars (bytes, index, count));
172         }
173
174         // Determine if two Encoding objects are equal.
175         public override bool Equals (Object value)
176         {
177                 Encoding enc = (value as Encoding);
178                 if (enc != null) {
179                         return codePage == enc.codePage &&
180                                 DecoderFallback.Equals (enc.DecoderFallback) &&
181                                 EncoderFallback.Equals (enc.EncoderFallback);
182                 } else {
183                         return false;
184                 }
185         }
186
187         // Get the number of characters needed to encode a character buffer.
188         public abstract int GetByteCount (char[] chars, int index, int count);
189
190         // Convenience wrappers for "GetByteCount".
191         public virtual int GetByteCount (String s)
192         {
193                 if (s == null)
194                         throw new ArgumentNullException ("s");
195
196                 if (s.Length == 0)
197                         return 0;
198                 unsafe {
199                         fixed (char* cptr = s) {
200                                 return GetByteCount (cptr, s.Length);
201                         }
202                 }
203         }
204         public virtual int GetByteCount (char[] chars)
205         {
206                 if (chars != null) {
207                         return GetByteCount (chars, 0, chars.Length);
208                 } else {
209                         throw new ArgumentNullException ("chars");
210                 }
211         }
212
213         // Get the bytes that result from encoding a character buffer.
214         public abstract int GetBytes (char[] chars, int charIndex, int charCount,
215                                                                  byte[] bytes, int byteIndex);
216
217         // Convenience wrappers for "GetBytes".
218         public virtual int GetBytes (String s, int charIndex, int charCount,
219                                                                 byte[] bytes, int byteIndex)
220         {
221                 if (s == null)
222                         throw new ArgumentNullException ("s");
223                 if (charIndex < 0 || charIndex > s.Length)
224                         throw new ArgumentOutOfRangeException ("charIndex", _("ArgRange_Array"));
225                 if (charCount < 0 || charIndex > (s.Length - charCount))
226                         throw new ArgumentOutOfRangeException ("charCount", _("ArgRange_Array"));
227                 if (byteIndex < 0 || byteIndex > bytes.Length)
228                         throw new ArgumentOutOfRangeException ("byteIndex", _("ArgRange_Array"));
229
230                 if (charCount == 0 || bytes.Length == byteIndex)
231                         return 0;
232                 unsafe {
233                         fixed (char* cptr = s) {
234                                 fixed (byte* bptr = bytes) {
235                                         return GetBytes (cptr + charIndex,
236                                                 charCount,
237                                                 bptr + byteIndex,
238                                                 bytes.Length - byteIndex);
239                                 }
240                         }
241                 }
242         }
243         public virtual byte[] GetBytes (String s)
244         {
245                 if (s == null)
246                         throw new ArgumentNullException ("s");
247
248                 if (s.Length == 0)
249                         return empty;
250                 int byteCount = GetByteCount (s);
251                 if (byteCount == 0)
252                         return empty;
253                 unsafe {
254                         fixed (char* cptr = s) {
255                                 byte [] bytes = new byte [byteCount];
256                                 fixed (byte* bptr = bytes) {
257                                         GetBytes (cptr, s.Length,
258                                                 bptr, byteCount);
259                                         return bytes;
260                                 }
261                         }
262                 }
263         }
264
265         public virtual byte[] GetBytes (char[] chars, int index, int count)
266         {
267                 int numBytes = GetByteCount (chars, index, count);
268                 byte[] bytes = new byte [numBytes];
269                 GetBytes (chars, index, count, bytes, 0);
270                 return bytes;
271         }
272         public virtual byte[] GetBytes (char[] chars)
273         {
274                 int numBytes = GetByteCount (chars, 0, chars.Length);
275                 byte[] bytes = new byte [numBytes];
276                 GetBytes (chars, 0, chars.Length, bytes, 0);
277                 return bytes;
278         }
279
280         // Get the number of characters needed to decode a byte buffer.
281         public abstract int GetCharCount (byte[] bytes, int index, int count);
282
283         // Convenience wrappers for "GetCharCount".
284         public virtual int GetCharCount (byte[] bytes)
285         {
286                 if (bytes == null) {
287                         throw new ArgumentNullException ("bytes");
288                 }
289                 return GetCharCount (bytes, 0, bytes.Length);
290         }
291
292         // Get the characters that result from decoding a byte buffer.
293         public abstract int GetChars (byte[] bytes, int byteIndex, int byteCount,
294                                                                  char[] chars, int charIndex);
295
296         // Convenience wrappers for "GetChars".
297         public virtual char[] GetChars (byte[] bytes, int index, int count)
298         {
299                 int numChars = GetCharCount (bytes, index, count);
300                 char[] chars = new char [numChars];
301                 GetChars (bytes, index, count, chars, 0);
302                 return chars;
303         }
304         public virtual char[] GetChars (byte[] bytes)
305         {
306                 if (bytes == null) {
307                         throw new ArgumentNullException ("bytes");
308                 }
309                 int numChars = GetCharCount (bytes, 0, bytes.Length);
310                 char[] chars = new char [numChars];
311                 GetChars (bytes, 0, bytes.Length, chars, 0);
312                 return chars;
313         }
314
315         // Get a decoder that forwards requests to this object.
316         public virtual Decoder GetDecoder ()
317         {
318                 return new ForwardingDecoder (this);
319         }
320
321         // Get an encoder that forwards requests to this object.
322         public virtual Encoder GetEncoder ()
323         {
324                 return new ForwardingEncoder (this);
325         }
326
327         // Loaded copy of the "I18N" assembly.  We need to move
328         // this into a class in "System.Private" eventually.
329         private static Assembly i18nAssembly;
330         private static bool i18nDisabled;
331
332         // Invoke a specific method on the "I18N" manager object.
333         // Returns NULL if the method failed.
334         private static Object InvokeI18N (String name, params Object[] args)
335         {
336                 lock (lockobj) {
337                         // Bail out if we previously detected that there
338                         // is insufficent engine support for I18N handling.
339                         if (i18nDisabled) {
340                                 return null;
341                         }
342
343                         // Find or load the "I18N" assembly.
344                         if (i18nAssembly == null) {
345                                 try {
346                                         try {
347                                                 i18nAssembly = Assembly.Load (Consts.AssemblyI18N);
348                                         } catch (NotImplementedException) {
349                                                 // Assembly loading unsupported by the engine.
350                                                 i18nDisabled = true;
351                                                 return null;
352                                         }
353                                         if (i18nAssembly == null) {
354                                                 return null;
355                                         }
356                                 } catch (SystemException) {
357                                         return null;
358                                 }
359                         }
360
361                         // Find the "I18N.Common.Manager" class.
362                         Type managerClass;
363                         try {
364                                 managerClass = i18nAssembly.GetType ("I18N.Common.Manager");
365                         } catch (NotImplementedException) {
366                                 // "GetType" is not supported by the engine.
367                                 i18nDisabled = true;
368                                 return null;
369                         }
370                         if (managerClass == null) {
371                                 return null;
372                         }
373
374                         // Get the value of the "PrimaryManager" property.
375                         Object manager;
376                         try {
377                                 manager = managerClass.InvokeMember
378                                                 ("PrimaryManager",
379                                                  BindingFlags.GetProperty |
380                                                         BindingFlags.Static |
381                                                         BindingFlags.Public,
382                                                  null, null, null, null, null, null);
383                                 if (manager == null) {
384                                         return null;
385                                 }
386                         } catch (MissingMethodException) {
387                                 return null;
388                         } catch (SecurityException) {
389                                 return null;
390                         } catch (NotImplementedException) {
391                                 // "InvokeMember" is not supported by the engine.
392                                 i18nDisabled = true;
393                                 return null;
394                         }
395
396                         // Invoke the requested method on the manager.
397                         try {
398                                 return managerClass.InvokeMember
399                                                 (name,
400                                                  BindingFlags.InvokeMethod |
401                                                         BindingFlags.Instance |
402                                                         BindingFlags.Public,
403                                                  null, manager, args, null, null, null);
404                         } catch (MissingMethodException) {
405                                 return null;
406                         } catch (SecurityException) {
407                                 return null;
408                         }
409                 }
410         }
411
412         // Get an encoder for a specific code page.
413 #if ECMA_COMPAT
414         private
415 #else
416         public
417 #endif
418         static Encoding GetEncoding (int codepage)
419         {
420                 if (codepage < 0 || codepage > 0xffff)
421                         throw new ArgumentOutOfRangeException ("codepage", 
422                                 "Valid values are between 0 and 65535, inclusive.");
423
424                 // Check for the builtin code pages first.
425                 switch (codepage) {
426                         case 0: return Default;
427
428                         case ASCIIEncoding.ASCII_CODE_PAGE:
429                                 return ASCII;
430
431                         case UTF7Encoding.UTF7_CODE_PAGE:
432                                 return UTF7;
433
434                         case UTF8Encoding.UTF8_CODE_PAGE:
435                                 return UTF8;
436
437                         case UTF32Encoding.UTF32_CODE_PAGE:
438                                 return UTF32;
439
440                         case UTF32Encoding.BIG_UTF32_CODE_PAGE:
441                                 return BigEndianUTF32;
442
443                         case UnicodeEncoding.UNICODE_CODE_PAGE:
444                                 return Unicode;
445
446                         case UnicodeEncoding.BIG_UNICODE_CODE_PAGE:
447                                 return BigEndianUnicode;
448
449 #if !MOONLIGHT
450                         case Latin1Encoding.ISOLATIN_CODE_PAGE:
451                                 return ISOLatin1;
452 #endif
453                         default: break;
454                 }
455 #if !MOONLIGHT
456                 // Try to obtain a code page handler from the I18N handler.
457                 Encoding enc = (Encoding)(InvokeI18N ("GetEncoding", codepage));
458                 if (enc != null) {
459                         enc.is_readonly = true;
460                         return enc;
461                 }
462
463                 // Build a code page class name.
464                 String cpName = "System.Text.CP" + codepage.ToString ();
465
466                 // Look for a code page converter in this assembly.
467                 Assembly assembly = Assembly.GetExecutingAssembly ();
468                 Type type = assembly.GetType (cpName);
469                 if (type != null) {
470                         enc = (Encoding)(Activator.CreateInstance (type));
471                         enc.is_readonly = true;
472                         return enc;
473                 }
474
475                 // Look in any assembly, in case the application
476                 // has provided its own code page handler.
477                 type = Type.GetType (cpName);
478                 if (type != null) {
479                         enc = (Encoding)(Activator.CreateInstance (type));
480                         enc.is_readonly = true;
481                         return enc;
482                 }
483 #endif // !NET_2_1
484                 // We have no idea how to handle this code page.
485                 throw new NotSupportedException
486                         (String.Format ("CodePage {0} not supported", codepage.ToString ()));
487         }
488
489 #if !ECMA_COMPAT
490
491         [ComVisible (false)]
492         public virtual object Clone ()
493         {
494                 Encoding e = (Encoding) MemberwiseClone ();
495                 e.is_readonly = false;
496                 return e;
497         }
498
499 #if !MOONLIGHT
500
501         public static Encoding GetEncoding (int codepage,
502                 EncoderFallback encoderFallback, DecoderFallback decoderFallback)
503         {
504                 if (encoderFallback == null)
505                         throw new ArgumentNullException ("encoderFallback");
506                 if (decoderFallback == null)
507                         throw new ArgumentNullException ("decoderFallback");
508
509                 Encoding e = GetEncoding (codepage).Clone () as Encoding;
510                 e.is_readonly = false;
511                 e.encoder_fallback = encoderFallback;
512                 e.decoder_fallback = decoderFallback;
513                 return e;
514         }
515
516         public static Encoding GetEncoding (string name,
517                 EncoderFallback encoderFallback, DecoderFallback decoderFallback)
518         {
519                 if (encoderFallback == null)
520                         throw new ArgumentNullException ("encoderFallback");
521                 if (decoderFallback == null)
522                         throw new ArgumentNullException ("decoderFallback");
523
524                 Encoding e = GetEncoding (name).Clone () as Encoding;
525                 e.is_readonly = false;
526                 e.encoder_fallback = encoderFallback;
527                 e.decoder_fallback = decoderFallback;
528                 return e;
529         }
530
531 #endif // !NET_2_1
532
533         static EncodingInfo [] encoding_infos;
534
535         // FIXME: As everyone would agree, this implementation is so *hacky*
536         // and could be very easily broken. But since there is a test for
537         // this method to make sure that this method always returns
538         // the same number and content of encoding infos, this won't
539         // matter practically.
540         public static EncodingInfo[] GetEncodings ()
541         {
542                 if (encoding_infos == null) {
543                         int [] codepages = new int [] {
544                                 37, 437, 500, 708,
545                                 850, 852, 855, 857, 858, 860, 861, 862, 863, 
546                                 864, 865, 866, 869, 870, 874, 875,
547                                 932, 936, 949, 950,
548                                 1026, 1047, 1140, 1141, 1142, 1143, 1144,
549                                 1145, 1146, 1147, 1148, 1149,
550                                 1200, 1201, 1250, 1251, 1252, 1253, 1254,
551                                 1255, 1256, 1257, 1258,
552                                 10000, 10079, 12000, 12001,
553                                 20127, 20273, 20277, 20278, 20280, 20284,
554                                 20285, 20290, 20297, 20420, 20424, 20866,
555                                 20871, 21025, 21866, 28591, 28592, 28593,
556                                 28594, 28595, 28596, 28597, 28598, 28599,
557                                 28605, 38598,
558                                 50220, 50221, 50222, 51932, 51949, 54936,
559                                 57002, 57003, 57004, 57005, 57006, 57007,
560                                 57008, 57009, 57010, 57011,
561                                 65000, 65001};
562
563                         encoding_infos = new EncodingInfo [codepages.Length];
564                         for (int i = 0; i < codepages.Length; i++)
565                                 encoding_infos [i] = new EncodingInfo (codepages [i]);
566                 }
567                 return encoding_infos;
568         }
569
570 #if !MOONLIGHT
571         [ComVisible (false)]
572         public bool IsAlwaysNormalized ()
573         {
574                 return IsAlwaysNormalized (NormalizationForm.FormC);
575         }
576
577         [ComVisible (false)]
578         public virtual bool IsAlwaysNormalized (NormalizationForm form)
579         {
580                 // umm, ASCIIEncoding should have overriden this method, no?
581                 return form == NormalizationForm.FormC && this is ASCIIEncoding;
582         }
583 #endif // NET_2_1
584
585         // Get an encoding object for a specific web encoding name.
586         public static Encoding GetEncoding (string name)
587         {
588                 // Validate the parameters.
589                 if (name == null) {
590                         throw new ArgumentNullException ("name");
591                 }
592                 
593                 string converted = name.ToLowerInvariant ().Replace ('-', '_');
594                 
595                 // Builtin web encoding names and the corresponding code pages.
596                 switch (converted) {
597                 case "ascii":
598                 case "us_ascii":
599                 case "us":
600                 case "ansi_x3.4_1968":
601                 case "ansi_x3.4_1986":
602                 case "cp367":
603                 case "csascii":
604                 case "ibm367":
605                 case "iso_ir_6":
606                 case "iso646_us":
607                 case "iso_646.irv:1991":
608                         return GetEncoding (ASCIIEncoding.ASCII_CODE_PAGE);
609
610                 case "utf_7":
611                 case "csunicode11utf7":
612                 case "unicode_1_1_utf_7":
613                 case "unicode_2_0_utf_7":
614                 case "x_unicode_1_1_utf_7":
615                 case "x_unicode_2_0_utf_7":
616                         return GetEncoding (UTF7Encoding.UTF7_CODE_PAGE);
617                 
618                 case "utf_8":
619                 case "unicode_1_1_utf_8":
620                 case "unicode_2_0_utf_8":
621                 case "x_unicode_1_1_utf_8":
622                 case "x_unicode_2_0_utf_8":
623                         return GetEncoding (UTF8Encoding.UTF8_CODE_PAGE);
624                 
625                 case "utf_16":
626                 case "utf_16le":
627                 case "ucs_2":
628                 case "unicode":
629                 case "iso_10646_ucs2":
630                         return GetEncoding (UnicodeEncoding.UNICODE_CODE_PAGE);
631
632                 case "unicodefffe":
633                 case "utf_16be":
634                         return GetEncoding (UnicodeEncoding.BIG_UNICODE_CODE_PAGE);
635                 
636                 case "utf_32":
637                 case "utf_32le":
638                 case "ucs_4":
639                         return GetEncoding (UTF32Encoding.UTF32_CODE_PAGE);
640
641                 case "utf_32be":
642                         return GetEncoding (UTF32Encoding.BIG_UTF32_CODE_PAGE);
643
644 #if !MOONLIGHT
645                 case "iso_8859_1":
646                 case "latin1":
647                         return GetEncoding (Latin1Encoding.ISOLATIN_CODE_PAGE);
648 #endif
649                 }
650                 
651 #if !MOONLIGHT
652                 // Try to obtain a web encoding handler from the I18N handler.
653                 Encoding enc = (Encoding)(InvokeI18N ("GetEncoding", name));
654                 if (enc != null) {
655                         return enc;
656                 }
657
658                 // Build a web encoding class name.
659                 String encName = "System.Text.ENC" + converted;
660                                                  
661
662                 // Look for a code page converter in this assembly.
663                 Assembly assembly = Assembly.GetExecutingAssembly ();
664                 Type type = assembly.GetType (encName);
665                 if (type != null) {
666                         return (Encoding)(Activator.CreateInstance (type));
667                 }
668
669                 // Look in any assembly, in case the application
670                 // has provided its own code page handler.
671                 type = Type.GetType (encName);
672                 if (type != null) {
673                         return (Encoding)(Activator.CreateInstance (type));
674                 }
675 #endif
676                 // We have no idea how to handle this encoding name.
677                 throw new ArgumentException (String.Format ("Encoding name '{0}' not "
678                         + "supported", name), "name");
679         }
680
681 #endif // !ECMA_COMPAT
682
683         // Get a hash code for this instance.
684         public override int GetHashCode ()
685         {
686                 return DecoderFallback.GetHashCode () << 24 + EncoderFallback.GetHashCode () << 16 + codePage;
687         }
688
689         // Get the maximum number of bytes needed to encode a
690         // specified number of characters.
691         public abstract int GetMaxByteCount (int charCount);
692
693         // Get the maximum number of characters needed to decode a
694         // specified number of bytes.
695         public abstract int GetMaxCharCount (int byteCount);
696
697         // Get the identifying preamble for this encoding.
698         public virtual byte[] GetPreamble ()
699         {
700                 return empty;
701         }
702
703         // Decode a buffer of bytes into a string.
704         public virtual String GetString (byte[] bytes, int index, int count)
705         {
706                 return new String (GetChars(bytes, index, count));
707         }
708         public virtual String GetString (byte[] bytes)
709         {
710                 if (bytes == null)
711                         throw new ArgumentNullException ("bytes");
712
713                 return GetString (bytes, 0, bytes.Length);
714         }
715
716 #if !ECMA_COMPAT
717
718         internal bool is_mail_news_display;
719         internal bool is_mail_news_save;
720         internal bool is_browser_save = false;
721         internal bool is_browser_display = false;
722         internal string body_name;
723         internal string encoding_name;
724         internal string header_name;
725         internal string web_name;
726
727         // Get the mail body name for this encoding.
728         public virtual String BodyName
729         {
730                 get {
731                         return body_name;
732                 }
733         }
734
735         // Get the code page represented by this object.
736         public virtual int CodePage
737         {
738                 get {
739                         return codePage;
740                 }
741         }
742
743         // Get the human-readable name for this encoding.
744         public virtual String EncodingName
745         {
746                 get {
747                         return encoding_name;
748                 }
749         }
750
751         // Get the mail agent header name for this encoding.
752         public virtual String HeaderName
753         {
754                 get {
755                         return header_name;
756                 }
757         }
758
759         // Determine if this encoding can be displayed in a Web browser.
760         public virtual bool IsBrowserDisplay
761         {
762                 get {
763                         return is_browser_display;
764                 }
765         }
766
767         // Determine if this encoding can be saved from a Web browser.
768         public virtual bool IsBrowserSave
769         {
770                 get {
771                         return is_browser_save;
772                 }
773         }
774
775         // Determine if this encoding can be displayed in a mail/news agent.
776         public virtual bool IsMailNewsDisplay
777         {
778                 get {
779                         return is_mail_news_display;
780                 }
781         }
782
783         // Determine if this encoding can be saved from a mail/news agent.
784         public virtual bool IsMailNewsSave
785         {
786                 get {
787                         return is_mail_news_save;
788                 }
789         }
790
791         // Get the IANA-preferred Web name for this encoding.
792         public virtual String WebName
793         {
794                 get {
795                         return web_name;
796                 }
797         }
798
799         // Get the Windows code page represented by this object.
800         public virtual int WindowsCodePage
801         {
802                 get {
803                         // We make no distinction between normal and
804                         // Windows code pages in this implementation.
805                         return windows_code_page;
806                 }
807         }
808
809 #endif // !ECMA_COMPAT
810
811         // Storage for standard encoding objects.
812         static volatile Encoding asciiEncoding;
813         static volatile Encoding bigEndianEncoding;
814         static volatile Encoding defaultEncoding;
815         static volatile Encoding utf7Encoding;
816         static volatile Encoding utf8EncodingWithMarkers;
817         static volatile Encoding utf8EncodingWithoutMarkers;
818         static volatile Encoding unicodeEncoding;
819         static volatile Encoding isoLatin1Encoding;
820         static volatile Encoding utf8EncodingUnsafe;
821         static volatile Encoding utf32Encoding;
822         static volatile Encoding bigEndianUTF32Encoding;
823
824         static readonly object lockobj = new object ();
825
826         // Get the standard ASCII encoding object.
827         public static Encoding ASCII
828         {
829                 get {
830                         if (asciiEncoding == null) {
831                                 lock (lockobj) {
832                                         if (asciiEncoding == null) {
833                                                 asciiEncoding = new ASCIIEncoding ();
834 //                                              asciiEncoding.is_readonly = true;
835                                         }
836                                 }
837                         }
838
839                         return asciiEncoding;
840                 }
841         }
842
843         // Get the standard big-endian Unicode encoding object.
844         public static Encoding BigEndianUnicode
845         {
846                 get {
847                         if (bigEndianEncoding == null) {
848                                 lock (lockobj) {
849                                         if (bigEndianEncoding == null) {
850                                                 bigEndianEncoding = new UnicodeEncoding (true, true);
851 //                                              bigEndianEncoding.is_readonly = true;
852                                         }
853                                 }
854                         }
855
856                         return bigEndianEncoding;
857                 }
858         }
859
860         [MethodImpl (MethodImplOptions.InternalCall)]
861         extern internal static string InternalCodePage (ref int code_page);
862         
863         // Get the default encoding object.
864         public static Encoding Default
865         {
866                 get {
867                         if (defaultEncoding == null) {
868                                 lock (lockobj) {
869                                         if (defaultEncoding == null) {
870                                                 // See if the underlying system knows what
871                                                 // code page handler we should be using.
872                                                 int code_page = 1;
873                                                 
874                                                 string code_page_name = InternalCodePage (ref code_page);
875                                                 try {
876                                                         if (code_page == -1)
877                                                                 defaultEncoding = GetEncoding (code_page_name);
878                                                         else {
879                                                                 // map the codepage from internal to our numbers
880                                                                 code_page = code_page & 0x0fffffff;
881                                                                 switch (code_page){
882                                                                 case 1: code_page = ASCIIEncoding.ASCII_CODE_PAGE; break;
883                                                                 case 2: code_page = UTF7Encoding.UTF7_CODE_PAGE; break;
884                                                                 case 3: code_page = UTF8Encoding.UTF8_CODE_PAGE; break;
885                                                                 case 4: code_page = UnicodeEncoding.UNICODE_CODE_PAGE; break;
886                                                                 case 5: code_page = UnicodeEncoding.BIG_UNICODE_CODE_PAGE; break;
887 #if !MOONLIGHT
888                                                                 case 6: code_page = Latin1Encoding.ISOLATIN_CODE_PAGE; break;
889 #endif
890                                                                 }
891                                                                 defaultEncoding = GetEncoding (code_page);
892                                                         }
893                                                 } catch (NotSupportedException) {
894 #if MOONLIGHT
895                                                         defaultEncoding = UTF8;
896 #else
897                                                         // code_page is not supported on underlying platform
898                                                         defaultEncoding = UTF8Unmarked;
899 #endif
900                                                 } catch (ArgumentException) {
901                                                         // code_page_name is not a valid code page, or is 
902                                                         // not supported by underlying OS
903 #if MOONLIGHT
904                                                         defaultEncoding = UTF8;
905 #else
906                                                         defaultEncoding = UTF8Unmarked;
907 #endif
908                                                 }
909                                                 defaultEncoding.is_readonly = true;
910                                         }
911                                 }
912                         }
913
914                         return defaultEncoding;
915                 }
916         }
917
918 #if !MOONLIGHT
919
920         // Get the ISO Latin1 encoding object.
921         private static Encoding ISOLatin1
922         {
923                 get {
924                         if (isoLatin1Encoding == null) {
925                                 lock (lockobj) {
926                                         if (isoLatin1Encoding == null) {
927                                                 isoLatin1Encoding = new Latin1Encoding ();
928 //                                              isoLatin1Encoding.is_readonly = true;
929                                         }
930                                 }
931                         }
932
933                         return isoLatin1Encoding;
934                 }
935         }
936
937 #endif
938
939         // Get the standard UTF-7 encoding object.
940 #if ECMA_COMPAT
941         private
942 #else
943         public
944 #endif
945         static Encoding UTF7
946         {
947                 get {
948                         if (utf7Encoding == null) {
949                                 lock (lockobj) {
950                                         if (utf7Encoding == null) {
951                                                 utf7Encoding = new UTF7Encoding ();
952 //                                              utf7Encoding.is_readonly = true;
953                                         }
954                                 }
955                         }
956
957                         return utf7Encoding;
958                 }
959         }
960
961         // Get the standard UTF-8 encoding object.
962         public static Encoding UTF8
963         {
964                 get {
965                         if (utf8EncodingWithMarkers == null) {
966                                 lock (lockobj) {
967                                         if (utf8EncodingWithMarkers == null) {
968                                                 utf8EncodingWithMarkers = new UTF8Encoding (true);
969 //                                              utf8EncodingWithMarkers.is_readonly = true;
970                                         }
971                                 }
972                         }
973
974                         return utf8EncodingWithMarkers;
975                 }
976         }
977
978         //
979         // Only internal, to be used by the class libraries: Unmarked and non-input-validating
980         //
981         internal static Encoding UTF8Unmarked {
982                 get {
983                         if (utf8EncodingWithoutMarkers == null) {
984                                 lock (lockobj){
985                                         if (utf8EncodingWithoutMarkers == null){
986                                                 utf8EncodingWithoutMarkers = new UTF8Encoding (false, false);
987 //                                              utf8EncodingWithoutMarkers.is_readonly = true;
988                                         }
989                                 }
990                         }
991
992                         return utf8EncodingWithoutMarkers;
993                 }
994         }
995         
996         //
997         // Only internal, to be used by the class libraries: Unmarked and non-input-validating
998         //
999         internal static Encoding UTF8UnmarkedUnsafe {
1000                 get {
1001                         if (utf8EncodingUnsafe == null) {
1002                                 lock (lockobj){
1003                                         if (utf8EncodingUnsafe == null){
1004                                                 utf8EncodingUnsafe = new UTF8Encoding (false, false);
1005                                                 utf8EncodingUnsafe.is_readonly = false;
1006                                                 utf8EncodingUnsafe.DecoderFallback = new DecoderReplacementFallback (String.Empty);
1007                                                 utf8EncodingUnsafe.is_readonly = true;
1008                                         }
1009                                 }
1010                         }
1011
1012                         return utf8EncodingUnsafe;
1013                 }
1014         }
1015         
1016         // Get the standard little-endian Unicode encoding object.
1017         public static Encoding Unicode
1018         {
1019                 get {
1020                         if (unicodeEncoding == null) {
1021                                 lock (lockobj) {
1022                                         if (unicodeEncoding == null) {
1023                                                 unicodeEncoding = new UnicodeEncoding (false, true);
1024 //                                              unicodeEncoding.is_readonly = true;
1025                                         }
1026                                 }
1027                         }
1028
1029                         return unicodeEncoding;
1030                 }
1031         }
1032
1033         // Get the standard little-endian UTF-32 encoding object.
1034         public static Encoding UTF32
1035         {
1036                 get {
1037                         if (utf32Encoding == null) {
1038                                 lock (lockobj) {
1039                                         if (utf32Encoding == null) {
1040                                                 utf32Encoding = new UTF32Encoding (false, true);
1041 //                                              utf32Encoding.is_readonly = true;
1042                                         }
1043                                 }
1044                         }
1045
1046                         return utf32Encoding;
1047                 }
1048         }
1049
1050         // Get the standard big-endian UTF-32 encoding object.
1051         internal static Encoding BigEndianUTF32
1052         {
1053                 get {
1054                         if (bigEndianUTF32Encoding == null) {
1055                                 lock (lockobj) {
1056                                         if (bigEndianUTF32Encoding == null) {
1057                                                 bigEndianUTF32Encoding = new UTF32Encoding (true, true);
1058 //                                              bigEndianUTF32Encoding.is_readonly = true;
1059                                         }
1060                                 }
1061                         }
1062
1063                         return bigEndianUTF32Encoding;
1064                 }
1065         }
1066
1067         // Forwarding decoder implementation.
1068         private sealed class ForwardingDecoder : Decoder
1069         {
1070                 private Encoding encoding;
1071
1072                 // Constructor.
1073                 public ForwardingDecoder (Encoding enc)
1074                 {
1075                         encoding = enc;
1076                         DecoderFallback fallback = encoding.DecoderFallback;
1077                         if (fallback != null)
1078                                 Fallback = fallback;
1079                 }
1080
1081                 // Override inherited methods.
1082                 public override int GetCharCount (byte[] bytes, int index, int count)
1083                 {
1084                         return encoding.GetCharCount (bytes, index, count);
1085                 }
1086                 public override int GetChars (byte[] bytes, int byteIndex,
1087                                                                          int byteCount, char[] chars,
1088                                                                          int charIndex)
1089                 {
1090                         return encoding.GetChars (bytes, byteIndex, byteCount, chars, charIndex);
1091                 }
1092
1093         } // class ForwardingDecoder
1094
1095         // Forwarding encoder implementation.
1096         private sealed class ForwardingEncoder : Encoder
1097         {
1098                 private Encoding encoding;
1099
1100                 // Constructor.
1101                 public ForwardingEncoder (Encoding enc)
1102                 {
1103                         encoding = enc;
1104                         EncoderFallback fallback = encoding.EncoderFallback;
1105                         if (fallback != null)
1106                                 Fallback = fallback;
1107                 }
1108
1109                 // Override inherited methods.
1110                 public override int GetByteCount (char[] chars, int index, int count, bool flush)
1111                 {
1112                         return encoding.GetByteCount (chars, index, count);
1113                 }
1114                 public override int GetBytes (char[] chars, int charIndex,
1115                                                                          int charCount, byte[] bytes,
1116                                                                          int byteCount, bool flush)
1117                 {
1118                         return encoding.GetBytes (chars, charIndex, charCount, bytes, byteCount);
1119                 }
1120
1121         } // class ForwardingEncoder
1122
1123         [CLSCompliantAttribute(false)]
1124         [ComVisible (false)]
1125         public unsafe virtual int GetByteCount (char *chars, int count)
1126         {
1127                 if (chars == null)
1128                         throw new ArgumentNullException ("chars");
1129                 if (count < 0)
1130                         throw new ArgumentOutOfRangeException ("count");
1131                 char [] c = new char [count];
1132
1133                 for (int p = 0; p < count; p++)
1134                         c [p] = chars [p];
1135
1136                 return GetByteCount (c);
1137         }
1138
1139         [CLSCompliantAttribute(false)]
1140         [ComVisible (false)]
1141         public unsafe virtual int GetCharCount (byte *bytes, int count)
1142         {
1143                 if (bytes == null)
1144                         throw new ArgumentNullException ("bytes");
1145                 if (count < 0)
1146                         throw new ArgumentOutOfRangeException ("count");
1147                 
1148                 byte [] ba = new byte [count];
1149                 for (int i = 0; i < count; i++)
1150                         ba [i] = bytes [i];
1151                 return GetCharCount (ba, 0, count);
1152         }
1153
1154         [CLSCompliantAttribute(false)]
1155         [ComVisible (false)]
1156         public unsafe virtual int GetChars (byte *bytes, int byteCount, char *chars, int charCount)
1157         {
1158                 if (bytes == null)
1159                         throw new ArgumentNullException ("bytes");
1160                 if (chars == null)
1161                         throw new ArgumentNullException ("chars");
1162                 if (charCount < 0)
1163                         throw new ArgumentOutOfRangeException ("charCount");
1164                 if (byteCount < 0)
1165                         throw new ArgumentOutOfRangeException ("byteCount");
1166                 
1167                 byte [] ba = new byte [byteCount];
1168                 for (int i = 0; i < byteCount; i++)
1169                         ba [i] = bytes [i];
1170                 char [] ret = GetChars (ba, 0, byteCount);
1171                 int top = ret.Length;
1172
1173                 if (top > charCount)
1174                         throw new ArgumentException ("charCount is less than the number of characters produced", "charCount");
1175                 
1176                 for (int i = 0; i < top; i++)
1177                         chars [i] = ret [i];
1178                 return top;
1179         }
1180
1181         [CLSCompliantAttribute(false)]
1182         [ComVisible (false)]
1183         public unsafe virtual int GetBytes (char *chars, int charCount, byte *bytes, int byteCount)
1184         {
1185                 if (bytes == null)
1186                         throw new ArgumentNullException ("bytes");
1187                 if (chars == null)
1188                         throw new ArgumentNullException ("chars");
1189                 if (charCount < 0)
1190                         throw new ArgumentOutOfRangeException ("charCount");
1191                 if (byteCount < 0)
1192                         throw new ArgumentOutOfRangeException ("byteCount");
1193                 
1194                 char [] c = new char [charCount];
1195                 
1196                 for (int i = 0; i < charCount; i++)
1197                         c [i] = chars [i];
1198
1199                 byte [] b = GetBytes (c, 0, charCount);
1200                 int top = b.Length;
1201                 if (top > byteCount)
1202                         throw new ArgumentException ("byteCount is less that the number of bytes produced", "byteCount");
1203
1204                 for (int i = 0; i < top; i++)
1205                         bytes [i] = b [i];
1206                 
1207                 return b.Length;
1208         }
1209 }; // class Encoding
1210
1211 }; // namespace System.Text