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