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