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