A little more work of CorCompare work:
[mono.git] / mcs / class / corlib / System.Text / UTF7Encoding.cs
1 /*
2  * UTF7Encoding.cs - Implementation of the
3  *              "System.Text.UTF7Encoding" class.
4  *
5  * Copyright (c) 2002  Southern Storm Software, Pty Ltd
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.Runtime.InteropServices;
32
33 [Serializable]
34 #if NET_2_0
35 [ComVisible (true)]
36 #endif
37 [MonoTODO ("Serialization format not compatible with .NET")]
38 #if ECMA_COMPAT
39 internal
40 #else
41 public
42 #endif
43 class UTF7Encoding : Encoding
44 {
45         // Magic number used by Windows for UTF-7.
46         internal const int UTF7_CODE_PAGE = 65000;
47
48         // Internal state.
49         private bool allowOptionals;
50
51         // Encoding rule table for 0x00-0x7F.
52         // 0 - full encode, 1 - direct, 2 - optional, 3 - encode plus.
53         private static readonly byte[] encodingRules = {
54                 0, 0, 0, 0, 0, 0, 0, 0,   0, 1, 1, 0, 0, 1, 0, 0,       // 00
55                 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,       // 10
56                 1, 2, 2, 2, 2, 2, 2, 1,   1, 1, 2, 3, 1, 1, 1, 1,       // 20
57                 1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 2, 2, 2, 2, 1,       // 30
58
59                 2, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,       // 40
60                 1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 2, 0, 2, 2, 2,       // 50
61                 2, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,       // 60
62                 1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 2, 2, 2, 0, 0,       // 70
63         };
64
65         // Characters to use to encode 6-bit values in base64.
66         private const String base64Chars =
67                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
68
69         // Map bytes in base64 to 6-bit values.
70         private static readonly sbyte[] base64Values = {
71                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // 00
72                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // 10
73                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, 62, -1, -1, 63, // 20
74                 52, 53, 54, 55, 56, 57, 58, 59,   60, 61, -1, -1, -1, -1, -1, -1, // 30
75
76                 -1,  0,  1,  2,  3,  4,  5,  6,    7,  8,  9, 10, 11, 12, 13, 14, // 40
77                 15, 16, 17, 18, 19, 20, 21, 22,   23, 24, 25, -1, -1, -1, -1, -1, // 50
78                 -1, 26, 27, 28, 29, 30, 31, 32,   33, 34, 35, 36, 37, 38, 39, 40, // 60
79                 41, 42, 43, 44, 45, 46, 47, 48,   49, 50, 51, -1, -1, -1, -1, -1, // 70
80
81                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // 80
82                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // 90
83                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // A0
84                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // B0
85
86                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // C0
87                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // D0
88                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // E0
89                 -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1, // F0
90         };
91
92         // Constructors.
93         public UTF7Encoding ()
94         : this (false)
95         {
96         }
97         
98         public UTF7Encoding (bool allowOptionals)
99         : base (UTF7_CODE_PAGE)
100         {
101                 this.allowOptionals = allowOptionals;
102                 
103                 body_name = "utf-7";
104                 encoding_name = "Unicode (UTF-7)";
105                 header_name = "utf-7";
106                 is_mail_news_display = true;
107                 is_mail_news_save = true;
108                 web_name = "utf-7";
109                 windows_code_page = UnicodeEncoding.UNICODE_CODE_PAGE;
110         }
111
112 #if NET_2_0
113         public override int GetHashCode ()
114         {
115                 int basis = base.GetHashCode ();
116                 return allowOptionals ? -basis : basis;
117         }
118
119         [ComVisible(true)]
120         public override bool Equals (object other)
121         {
122                 UTF7Encoding e = other as UTF7Encoding;
123                 if (e == null)
124                         return false;
125                 return allowOptionals == e.allowOptionals &&
126                         EncoderFallback.Equals (e.EncoderFallback) &&
127                         DecoderFallback.Equals (e.DecoderFallback);
128         }
129 #endif
130
131         // Internal version of "GetByteCount" that can handle
132         // a rolling state between calls.
133         private static int InternalGetByteCount
134                                 (char[] chars, int index, int count, bool flush,
135                                  int leftOver, bool isInShifted, bool allowOptionals)
136         {
137                 // Validate the parameters.
138                 if (chars == null) {
139                         throw new ArgumentNullException ("chars");
140                 }
141                 if (index < 0 || index > chars.Length) {
142                         throw new ArgumentOutOfRangeException ("index", _("ArgRange_Array"));
143                 }
144                 if (count < 0 || count > (chars.Length - index)) {
145                         throw new ArgumentOutOfRangeException ("count", _("ArgRange_Array"));
146                 }
147
148                 // Determine the length of the output.
149                 int length = 0;
150                 int leftOverSize = (leftOver >> 8);
151                 byte[] rules = encodingRules;
152                 int ch, rule;
153                 while (count > 0) {
154                         ch = (int)(chars[index++]);
155                         --count;
156                         if (ch < 0x0080) {
157                                 rule = rules[ch];
158                         } else {
159                                 rule = 0;
160                         }
161                         switch (rule) {
162                         case 0:
163                                 // Handle characters that must be fully encoded.
164                                 if ( !isInShifted ) {
165                                         ++length;
166                                     leftOverSize = 0;
167                                         isInShifted = true;
168                                 }
169                                 leftOverSize += 16;
170                                 while (leftOverSize >= 6) {
171                                         ++length;
172                                         leftOverSize -= 6;
173                                 }
174                                 break;
175                         case 1:
176                                 // The character is encoded as itself.
177                                 if (isInShifted) {
178                                         if (leftOverSize != 0) {
179                                                 // Flush the previous encoded sequence.
180                                                 ++length;
181                                                 leftOverSize = 0;
182                                         }
183                                         // Count the "-" (sequence terminator)
184                                         ++length;
185                                         isInShifted = false;
186                                 }
187                                 ++length;
188                                 break;
189                         case 2:
190                                 // The character may need to be encoded.
191                                 if (allowOptionals) {
192                                         goto case 1;
193                                 } else {
194                                         goto case 0;
195                                 }
196                         // Not reached.
197                         case 3:
198                                 // Encode the plus sign as "+-".
199                                 if (isInShifted) {
200                                         if (leftOverSize != 0) {
201                                                 // Flush the previous encoded sequence.
202                                                 ++length;
203                                                 leftOverSize = 0;
204                                         }
205                                         // Count the "-" (sequence terminator)
206                                         ++length;
207                                         isInShifted = false;
208                                 }
209                                 length += 2;
210                                 break;
211                         }
212                 }
213                 if (isInShifted && flush) {
214                         if (leftOverSize != 0) 
215                         {
216                                 // Flush the previous encoded sequence.
217                                 ++length;
218                         }
219                         // Count the "-" (sequence terminator)
220                         ++length;
221                 }
222
223                 // Return the length to the caller.
224                 return length;
225         }
226
227         // Get the number of bytes needed to encode a character buffer.
228         public override int GetByteCount (char[] chars, int index, int count)
229         {
230                 return InternalGetByteCount (chars, index, count, true, 0, false, allowOptionals);
231         }
232
233         // Internal version of "GetBytes" that can handle a
234         // rolling state between calls.
235         private static int InternalGetBytes
236                                 (char[] chars, int charIndex, int charCount,
237                                  byte[] bytes, int byteIndex, bool flush,
238                                  ref int leftOver, ref bool isInShifted, bool allowOptionals)
239         {
240                 // Validate the parameters.
241                 if (chars == null) {
242                         throw new ArgumentNullException ("chars");
243                 }
244                 if (bytes == null) {
245                         throw new ArgumentNullException ("bytes");
246                 }
247                 if (charIndex < 0 || charIndex > chars.Length) {
248                         throw new ArgumentOutOfRangeException ("charIndex", _("ArgRange_Array"));
249                 }
250                 if (charCount < 0 || charCount > (chars.Length - charIndex)) {
251                         throw new ArgumentOutOfRangeException ("charCount", _("ArgRange_Array"));
252                 }
253                 if (byteIndex < 0 || byteIndex > bytes.Length) {
254                         throw new ArgumentOutOfRangeException ("byteIndex", _("ArgRange_Array"));
255                 }
256
257                 // Convert the characters.
258                 int posn = byteIndex;
259                 int byteLength = bytes.Length;
260                 int leftOverSize = (leftOver >> 8);
261                 int leftOverBits = (leftOver & 0xFF);
262                 byte[] rules = encodingRules;
263                 String base64 = base64Chars;
264                 int ch, rule;
265                 while (charCount > 0) {
266                         ch = (int)(chars[charIndex++]);
267                         --charCount;
268                         if (ch < 0x0080) {
269                                 rule = rules[ch];
270                         } else {
271                                 rule = 0;
272                         }
273                         switch (rule) {
274                         case 0:
275                                 // Handle characters that must be fully encoded.
276                                 if (!isInShifted) {
277                                         if (posn >= byteLength) {
278                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
279                                         }
280                                         // Start the sequence
281                                         bytes[posn++] = (byte)'+';
282                                         isInShifted = true;
283                                         leftOverSize = 0;
284                                 }
285                                 leftOverBits = ((leftOverBits << 16) | ch);
286                                 leftOverSize += 16;
287                                 while (leftOverSize >= 6) {
288                                         if (posn >= byteLength) {
289                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
290                                         }
291                                         leftOverSize -= 6;
292                                         bytes[posn++] = (byte)(base64 [leftOverBits >> leftOverSize]);
293                                         leftOverBits &= ((1 << leftOverSize) - 1);
294                                 }
295                                 break;
296                         case 1:
297                                 // The character is encoded as itself.
298                                 if (isInShifted) {
299                                         if (leftOverSize != 0) {
300                                                 // Flush the previous encoded sequence.
301                                                 if ((posn + 1) > byteLength) {
302                                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
303                                                 }
304                                                 bytes[posn++] = (byte)(base64 [leftOverBits << (6 - leftOverSize)]);
305                                         }
306                                         if ((posn + 1) > byteLength) {
307                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
308                                         }
309                                         // Terminate the sequence
310                                         bytes[posn++] = (byte)'-';
311                                         isInShifted = false;
312                                         leftOverSize = 0;
313                                         leftOverBits = 0;
314                                 }
315                                 if (posn >= byteLength) {
316                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
317                                 }
318                                 bytes[posn++] = (byte)ch;
319                                 break;
320                         case 2:
321                                 // The character may need to be encoded.
322                                 if (allowOptionals) {
323                                         goto case 1;
324                                 } else {
325                                         goto case 0;
326                                 }
327                                 // Not reached.
328                         case 3:
329                                 // Encode the plus sign as "+-".
330                                 if (isInShifted) {
331                                         if (leftOverSize != 0) {
332                                                 // Flush the previous encoded sequence.
333                                                 if ((posn + 1) > byteLength) {
334                                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
335                                                 }
336                                                 bytes[posn++] = (byte)(base64 [leftOverBits << (6 - leftOverSize)]);
337                                         }
338                                         if ((posn + 1) > byteLength) {
339                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
340                                         }
341                                         // Terminate the sequence
342                                         bytes[posn++] = (byte)'-';
343                                         isInShifted = false;
344                                         leftOverSize = 0;
345                                         leftOverBits = 0;
346                                 }
347                                 if ((posn + 2) > byteLength) {
348                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
349                                 }
350                                 bytes[posn++] = (byte)'+';
351                                 bytes[posn++] = (byte)'-';
352                                 break;
353                         }
354                 }
355                 if (isInShifted && flush) {
356                         // Flush the previous encoded sequence.
357                         if (leftOverSize != 0) {
358                                 if ((posn + 1) > byteLength) {
359                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "bytes");
360                                 }
361                                 bytes[posn++] = (byte)(base64 [leftOverBits << (6 - leftOverSize)]);
362                         }
363                         // Terminate the sequence
364                         bytes[posn++] = (byte)'-';
365                         leftOverSize = 0;
366                         leftOverBits = 0;
367                         isInShifted = false;
368                 }
369                 leftOver = ((leftOverSize << 8) | leftOverBits);
370
371                 // Return the length to the caller.
372                 return posn - byteIndex;
373         }
374
375         // Get the bytes that result from encoding a character buffer.
376         public override int GetBytes (char[] chars, int charIndex, int charCount,
377                                                                  byte[] bytes, int byteIndex)
378         {
379                 int leftOver = 0;
380                 bool isInShifted = false;
381                 return InternalGetBytes (chars, charIndex, charCount, bytes, byteIndex, true,
382                                                                 ref leftOver, ref isInShifted, allowOptionals);
383         }
384
385         // Internal version of "GetCharCount" that can handle
386         // a rolling state between call.s
387         private static int InternalGetCharCount
388                                         (byte[] bytes, int index, int count, int leftOver)
389         {
390                 // Validate the parameters.
391                 if (bytes == null) {
392                         throw new ArgumentNullException ("bytes");
393                 }
394                 if (index < 0 || index > bytes.Length) {
395                         throw new ArgumentOutOfRangeException ("index", _("ArgRange_Array"));
396                 }
397                 if (count < 0 || count > (bytes.Length - index)) {
398                         throw new ArgumentOutOfRangeException ("count", _("ArgRange_Array"));
399                 }
400
401                 // Determine the length of the result.
402                 int length = 0;
403                 int byteval;
404                 bool normal = ((leftOver & 0x01000000) == 0);
405                 bool prevIsPlus = ((leftOver & 0x02000000) != 0);
406                 int leftOverSize = ((leftOver >> 16) & 0xFF);
407                 sbyte[] base64 = base64Values;
408                 while (count > 0) {
409                         byteval = (int)(bytes[index++]);
410                         --count;
411                         if (normal) {
412                                 if (byteval != '+') {
413                                         // Directly-encoded character.
414                                         ++length;
415                                 } else {
416                                         // Start of a base64-encoded character.
417                                         normal = false;
418                                         prevIsPlus = true;
419                                 }
420                         } else {
421                                 // Process the next byte in a base64 sequence.
422                                 if (byteval == (int)'-') {
423                                         // End of a base64 sequence.
424                                         if (prevIsPlus) {
425                                                 ++length;
426                                                 leftOverSize = 0;
427                                         }
428                                         normal = true;
429                                 } else if (base64 [byteval] != -1) {
430                                         // Extra character in a base64 sequence.
431                                         leftOverSize += 6;
432                                         if (leftOverSize >= 16) {
433                                                 ++length;
434                                                 leftOverSize -= 16;
435                                         }
436                                 } else {
437                                         ++length;
438                                         normal = true;
439                                         leftOverSize = 0;
440                                 }
441                                 prevIsPlus = false;
442                         }
443                 }
444
445                 // Return the final length to the caller.
446                 return length;
447         }
448
449         // Get the number of characters needed to decode a byte buffer.
450         public override int GetCharCount (byte[] bytes, int index, int count)
451         {
452                 return InternalGetCharCount (bytes, index, count, 0);
453         }
454
455         // Internal version of "GetChars" that can handle a
456         // rolling state between calls.
457         private static int InternalGetChars (byte[] bytes, int byteIndex, int byteCount,
458                                  char[] chars, int charIndex, ref int leftOver)
459         {
460                 // Validate the parameters.
461                 if (bytes == null) {
462                         throw new ArgumentNullException ("bytes");
463                 }
464                 if (chars == null) {
465                         throw new ArgumentNullException ("chars");
466                 }
467                 if (byteIndex < 0 || byteIndex > bytes.Length) {
468                         throw new ArgumentOutOfRangeException ("byteIndex", _("ArgRange_Array"));
469                 }
470                 if (byteCount < 0 || byteCount > (bytes.Length - byteIndex)) {
471                         throw new ArgumentOutOfRangeException ("byteCount", _("ArgRange_Array"));
472                 }
473                 if (charIndex < 0 || charIndex > chars.Length) {
474                         throw new ArgumentOutOfRangeException ("charIndex", _("ArgRange_Array"));
475                 }
476
477                 // Convert the bytes into characters.
478                 int posn = charIndex;
479                 int charLength = chars.Length;
480                 int byteval, b64value;
481                 bool normal = ((leftOver & 0x01000000) == 0);
482                 bool prevIsPlus = ((leftOver & 0x02000000) != 0);
483                 bool afterHighSurrogate = ((leftOver & 0x04000000) != 0);
484                 int leftOverSize = ((leftOver >> 16) & 0xFF);
485                 int leftOverBits = (leftOver & 0xFFFF);
486                 sbyte[] base64 = base64Values;
487                 while (byteCount > 0) {
488                         byteval = (int)(bytes[byteIndex++]);
489                         --byteCount;
490                         if (normal) {
491                                 if (byteval != '+') {
492                                         // Directly-encoded character.
493                                         if (posn >= charLength) {
494                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "chars");
495                                         }
496                                         if (afterHighSurrogate) {
497                                                 throw new ArgumentException (_("Arg_InvalidUTF7"), "chars");
498                                         }
499                                         chars[posn++] = (char)byteval;
500                                 } else {
501                                         // Start of a base64-encoded character.
502                                         normal = false;
503                                         prevIsPlus = true;
504                                 }
505                         } else {
506                                 // Process the next byte in a base64 sequence.
507                                 if (byteval == (int)'-') {
508                                         // End of a base64 sequence.
509                                         if (prevIsPlus) {
510                                                 if (posn >= charLength) {
511                                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "chars");
512                                                 }
513                                                 if (afterHighSurrogate) {
514                                                         throw new ArgumentException (_("Arg_InvalidUTF7"), "chars");
515                                                 }
516                                                 chars[posn++] = '+';
517                                         }
518                                         // RFC1642 Rule #2
519                                         // When decoding, any bits at the end of the Modified Base64 sequence that 
520                                         // do not constitute a complete 16-bit Unicode character are discarded. 
521                                         // If such discarded bits are non-zero the sequence is ill-formed.
522                                         normal = true;
523                                         leftOverSize = 0;
524                                         leftOverBits = 0;
525                                 } 
526                                 else if ((b64value = base64[byteval]) != -1) 
527                                 {
528                                         // Extra character in a base64 sequence.
529                                         leftOverBits = (leftOverBits << 6) | b64value;
530                                         leftOverSize += 6;
531                                         if (leftOverSize >= 16) {
532                                                 if (posn >= charLength) {
533                                                         throw new ArgumentException (_("Arg_InsufficientSpace"), "chars");
534                                                 }
535                                                 leftOverSize -= 16;
536                                                 char nextChar = (char)(leftOverBits >> leftOverSize);
537                                                 if ((nextChar & 0xFC00) == 0xD800) {
538                                                   afterHighSurrogate = true;
539                                                 }
540                                                 else if ((nextChar & 0xFC00) == 0xDC00) {
541                                                         if (!afterHighSurrogate) {
542                                                                 throw new ArgumentException (_("Arg_InvalidUTF7"), "chars");
543                                                         }
544                                                         afterHighSurrogate = false;
545                                                 }
546                                                 chars[posn++] = nextChar;
547                                                 leftOverBits &= ((1 << leftOverSize) - 1);
548                                         }
549                                 } else {
550                                         if (posn >= charLength) {
551                                                 throw new ArgumentException (_("Arg_InsufficientSpace"), "chars");
552                                         }
553                                         if (afterHighSurrogate) {
554                                                 throw new ArgumentException (_("Arg_InvalidUTF7"), "chars");
555                                         }
556                                         chars[posn++] = (char)byteval;
557                                         normal = true;
558                                         leftOverSize = 0;
559                                         leftOverBits = 0;
560                                 }
561                                 prevIsPlus = false;
562                         }
563                 }
564                 leftOver = (leftOverBits | (leftOverSize << 16) |
565                                     (normal ? 0 : 0x01000000) |
566                                     (prevIsPlus ? 0x02000000 : 0) |
567                                     (afterHighSurrogate ? 0x04000000 : 0));
568
569                 // Return the final length to the caller.
570                 return posn - charIndex;
571         }
572
573         // Get the characters that result from decoding a byte buffer.
574         public override int GetChars (byte[] bytes, int byteIndex, int byteCount,
575                                                                  char[] chars, int charIndex)
576         {
577                 int leftOver = 0;
578                 int amount = InternalGetChars (bytes, byteIndex, byteCount, chars, charIndex, ref leftOver);
579                 if ((leftOver & 0x04000000) != 0) {
580                         throw new ArgumentException (_("Arg_InvalidUTF7"), "chars");
581                 }
582                 return amount;
583         }
584
585         // Get the maximum number of bytes needed to encode a
586         // specified number of characters.
587         public override int GetMaxByteCount (int charCount)
588         {
589                 if (charCount < 0) {
590                         throw new ArgumentOutOfRangeException ("charCount", _("ArgRange_NonNegative"));
591                 }
592                 if (charCount == 0)
593                         return 0;
594                 return 8 * (int) (charCount / 3) + (charCount % 3) * 3 + 2;
595         }
596
597         // Get the maximum number of characters needed to decode a
598         // specified number of bytes.
599         public override int GetMaxCharCount (int byteCount)
600         {
601                 if (byteCount < 0) {
602                         throw new ArgumentOutOfRangeException ("byteCount", _("ArgRange_NonNegative"));
603                 }
604                 return byteCount;
605         }
606
607         // Get a UTF7-specific decoder that is attached to this instance.
608         public override Decoder GetDecoder ()
609         {
610                 return new UTF7Decoder ();
611         }
612
613         // Get a UTF7-specific encoder that is attached to this instance.
614         public override Encoder GetEncoder ()
615         {
616                 return new UTF7Encoder (allowOptionals);
617         }
618
619         // UTF-7 decoder implementation.
620         private sealed class UTF7Decoder : Decoder
621         {
622                 // Internal state.
623                 private int leftOver;
624
625                 // Constructor.
626                 public UTF7Decoder ()
627                 {
628                         leftOver = 0;
629                 }
630
631                 // Override inherited methods.
632                 public override int GetCharCount (byte[] bytes, int index, int count)
633                 {
634                         return InternalGetCharCount (bytes, index, count, leftOver);
635                 }
636                 public override int GetChars (byte[] bytes, int byteIndex,
637                                                                          int byteCount, char[] chars,
638                                                                          int charIndex)
639                 {
640                         return InternalGetChars (bytes, byteIndex, byteCount, chars, charIndex, ref leftOver);
641                 }
642
643         } // class UTF7Decoder
644
645         // UTF-7 encoder implementation.
646         private sealed class UTF7Encoder : Encoder
647         {
648                 private bool allowOptionals;
649                 private int leftOver = 0;
650                 private bool isInShifted = false;
651
652                 // Constructor.
653                 public UTF7Encoder (bool allowOptionals)
654                 {
655                         this.allowOptionals = allowOptionals;
656                 }
657
658                 // Override inherited methods.
659                 public override int GetByteCount (char[] chars, int index,
660                                                                                  int count, bool flush)
661                 {
662                         return InternalGetByteCount
663                                 (chars, index, count, flush, leftOver, isInShifted, allowOptionals);
664                 }
665                 public override int GetBytes (char[] chars, int charIndex,
666                                                                          int charCount, byte[] bytes,
667                                                                          int byteIndex, bool flush)
668                 {
669                         return InternalGetBytes (chars, charIndex, charCount,
670                                                                         bytes, byteIndex, flush,
671                                                                         ref leftOver, ref isInShifted, allowOptionals);
672                 }
673
674         } // class UTF7Encoder
675
676 #if NET_2_0
677         // a bunch of practically missing implementations (but should just work)
678
679         [CLSCompliantAttribute (false)]
680         [ComVisible(true)]
681         public override unsafe int GetByteCount (char *chars, int count)
682         {
683                 return base.GetByteCount (chars, count);
684         }
685
686         [ComVisible(true)]
687         public override int GetByteCount (string s)
688         {
689                 return base.GetByteCount (s);
690         }
691
692         [ComVisible(true)]
693         [CLSCompliantAttribute (false)]
694         public override unsafe int GetBytes (char *chars, int charCount, byte* bytes, int byteCount)
695         {
696                 return base.GetBytes (chars, charCount, bytes, byteCount);
697         }
698
699         [ComVisible(true)]
700         public override int GetBytes (string s, int charIndex, int charCount, byte [] bytes, int byteIndex)
701         {
702                 return base.GetBytes (s, charIndex, charCount, bytes, byteIndex);
703         }
704
705         [ComVisible(true)]
706         [CLSCompliantAttribute (false)]
707         public override unsafe int GetCharCount (byte *bytes, int count)
708         {
709                 return base.GetCharCount (bytes, count);
710         }
711
712         [ComVisible(true)]
713         [CLSCompliantAttribute (false)]
714         public override unsafe int GetChars (byte* bytes, int byteCount, char* chars, int charCount)
715         {
716                 return base.GetChars (bytes, byteCount, chars, charCount);
717         }
718
719         [ComVisible(true)]
720         public override string GetString (byte [] bytes, int index, int count)
721         {
722                 return base.GetString (bytes, index, count);
723         }
724
725 #endif
726
727 }; // class UTF7Encoding
728
729 }; // namespace System.Text