Encoding code clean up (#12864)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Text / UTF7Encoding.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 //
6 // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
7 //
8
9 using System;
10 using System.Diagnostics;
11 using System.Diagnostics.Contracts;
12
13 namespace System.Text
14 {
15     public class UTF7Encoding : Encoding
16     {
17         private const String base64Chars =
18             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
19         //   0123456789111111111122222222223333333333444444444455555555556666
20         //             012345678901234567890123456789012345678901234567890123
21
22         // These are the characters that can be directly encoded in UTF7.
23         private const String directChars =
24             "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
25
26         // These are the characters that can be optionally directly encoded in UTF7.
27         private const String optionalChars =
28             "!\"#$%&*;<=>@[]^_`{|}";
29
30         // Used by Encoding.UTF7 for lazy initialization
31         // The initialization code will not be run until a static member of the class is referenced
32         internal static readonly UTF7Encoding s_default = new UTF7Encoding();
33
34         // The set of base 64 characters.
35         private byte[] _base64Bytes;
36         // The decoded bits for every base64 values. This array has a size of 128 elements.
37         // The index is the code point value of the base 64 characters.  The value is -1 if
38         // the code point is not a valid base 64 character.  Otherwise, the value is a value
39         // from 0 ~ 63.
40         private sbyte[] _base64Values;
41         // The array to decide if a Unicode code point below 0x80 can be directly encoded in UTF7.
42         // This array has a size of 128.
43         private bool[] _directEncode;
44
45         private bool _allowOptionals;
46
47         private const int UTF7_CODEPAGE = 65000;
48
49
50         public UTF7Encoding()
51             : this(false)
52         {
53         }
54
55         public UTF7Encoding(bool allowOptionals)
56             : base(UTF7_CODEPAGE) //Set the data item.
57         {
58             // Allowing optionals?
59             _allowOptionals = allowOptionals;
60
61             // Make our tables
62             MakeTables();
63         }
64
65         private void MakeTables()
66         {
67             // Build our tables
68             _base64Bytes = new byte[64];
69             for (int i = 0; i < 64; i++) _base64Bytes[i] = (byte)base64Chars[i];
70             _base64Values = new sbyte[128];
71             for (int i = 0; i < 128; i++) _base64Values[i] = -1;
72             for (int i = 0; i < 64; i++) _base64Values[_base64Bytes[i]] = (sbyte)i;
73             _directEncode = new bool[128];
74             int count = directChars.Length;
75             for (int i = 0; i < count; i++)
76             {
77                 _directEncode[directChars[i]] = true;
78             }
79
80             if (_allowOptionals)
81             {
82                 count = optionalChars.Length;
83                 for (int i = 0; i < count; i++)
84                 {
85                     _directEncode[optionalChars[i]] = true;
86                 }
87             }
88         }
89
90         // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7.
91         internal override void SetDefaultFallbacks()
92         {
93             // UTF7 had an odd decoderFallback behavior, and the Encoder fallback
94             // is irrelevant because we encode surrogates individually and never check for unmatched ones
95             // (so nothing can fallback during encoding)
96             this.encoderFallback = new EncoderReplacementFallback(String.Empty);
97             this.decoderFallback = new DecoderUTF7Fallback();
98         }
99
100         public override bool Equals(Object value)
101         {
102             UTF7Encoding that = value as UTF7Encoding;
103             if (that != null)
104             {
105                 return (_allowOptionals == that._allowOptionals) &&
106                        (EncoderFallback.Equals(that.EncoderFallback)) &&
107                        (DecoderFallback.Equals(that.DecoderFallback));
108             }
109             return (false);
110         }
111
112         // Compared to all the other encodings, variations of UTF7 are unlikely
113
114         public override int GetHashCode()
115         {
116             return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode();
117         }
118
119         // The following methods are copied from EncodingNLS.cs.
120         // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
121         // These should be kept in sync for the following classes:
122         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
123
124         // Returns the number of bytes required to encode a range of characters in
125         // a character array.
126         //
127         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
128         // So if you fix this, fix the others.  Currently those include:
129         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
130         // parent method is safe
131
132         public override unsafe int GetByteCount(char[] chars, int index, int count)
133         {
134             // Validate input parameters
135             if (chars == null)
136                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
137
138             if (index < 0 || count < 0)
139                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
140
141             if (chars.Length - index < count)
142                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
143             Contract.EndContractBlock();
144
145             // If no input, return 0, avoid fixed empty array problem
146             if (count == 0)
147                 return 0;
148
149             // Just call the pointer version
150             fixed (char* pChars = chars)
151                 return GetByteCount(pChars + index, count, null);
152         }
153
154         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
155         // So if you fix this, fix the others.  Currently those include:
156         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
157         // parent method is safe
158
159         public override unsafe int GetByteCount(string s)
160         {
161             // Validate input
162             if (s==null)
163                 throw new ArgumentNullException("s");
164             Contract.EndContractBlock();
165
166             fixed (char* pChars = s)
167                 return GetByteCount(pChars, s.Length, null);
168         }
169
170         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
171         // So if you fix this, fix the others.  Currently those include:
172         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
173
174         [CLSCompliant(false)]
175         public override unsafe int GetByteCount(char* chars, int count)
176         {
177             // Validate Parameters
178             if (chars == null)
179                 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
180
181             if (count < 0)
182                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
183             Contract.EndContractBlock();
184
185             // Call it with empty encoder
186             return GetByteCount(chars, count, null);
187         }
188
189         // Parent method is safe.
190         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
191         // So if you fix this, fix the others.  Currently those include:
192         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
193
194         public override unsafe int GetBytes(string s, int charIndex, int charCount,
195                                               byte[] bytes, int byteIndex)
196         {
197             if (s == null || bytes == null)
198                 throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
199
200             if (charIndex < 0 || charCount < 0)
201                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
202
203             if (s.Length - charIndex < charCount)
204                 throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
205
206             if (byteIndex < 0 || byteIndex > bytes.Length)
207                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
208             Contract.EndContractBlock();
209
210             int byteCount = bytes.Length - byteIndex;
211
212             // Fixed doesn't like empty arrays
213             if (bytes.Length == 0)
214                 bytes = new byte[1];
215
216             fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
217                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
218         }
219
220         // Encodes a range of characters in a character array into a range of bytes
221         // in a byte array. An exception occurs if the byte array is not large
222         // enough to hold the complete encoding of the characters. The
223         // GetByteCount method can be used to determine the exact number of
224         // bytes that will be produced for a given range of characters.
225         // Alternatively, the GetMaxByteCount method can be used to
226         // determine the maximum number of bytes that will be produced for a given
227         // number of characters, regardless of the actual character values.
228         //
229         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
230         // So if you fix this, fix the others.  Currently those include:
231         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
232         // parent method is safe
233
234         public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
235                                                byte[] bytes, int byteIndex)
236         {
237             // Validate parameters
238             if (chars == null || bytes == null)
239                 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
240
241             if (charIndex < 0 || charCount < 0)
242                 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
243
244             if (chars.Length - charIndex < charCount)
245                 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
246
247             if (byteIndex < 0 || byteIndex > bytes.Length)
248                 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
249             Contract.EndContractBlock();
250
251             // If nothing to encode return 0, avoid fixed problem
252             if (charCount == 0)
253                 return 0;
254
255             // Just call pointer version
256             int byteCount = bytes.Length - byteIndex;
257
258             // Fixed doesn't like empty arrays
259             if (bytes.Length == 0)
260                 bytes = new byte[1];
261
262             fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
263                 // Remember that byteCount is # to decode, not size of array.
264                 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
265         }
266
267         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
268         // So if you fix this, fix the others.  Currently those include:
269         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
270
271         [CLSCompliant(false)]
272         public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
273         {
274             // Validate Parameters
275             if (bytes == null || chars == null)
276                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
277
278             if (charCount < 0 || byteCount < 0)
279                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
280             Contract.EndContractBlock();
281
282             return GetBytes(chars, charCount, bytes, byteCount, null);
283         }
284
285         // Returns the number of characters produced by decoding a range of bytes
286         // in a byte array.
287         //
288         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
289         // So if you fix this, fix the others.  Currently those include:
290         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
291         // parent method is safe
292
293         public override unsafe int GetCharCount(byte[] bytes, int index, int count)
294         {
295             // Validate Parameters
296             if (bytes == null)
297                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
298
299             if (index < 0 || count < 0)
300                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
301
302             if (bytes.Length - index < count)
303                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
304             Contract.EndContractBlock();
305
306             // If no input just return 0, fixed doesn't like 0 length arrays.
307             if (count == 0)
308                 return 0;
309
310             // Just call pointer version
311             fixed (byte* pBytes = bytes)
312                 return GetCharCount(pBytes + index, count, null);
313         }
314
315         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
316         // So if you fix this, fix the others.  Currently those include:
317         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
318
319         [CLSCompliant(false)]
320         public override unsafe int GetCharCount(byte* bytes, int count)
321         {
322             // Validate Parameters
323             if (bytes == null)
324                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
325
326             if (count < 0)
327                 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
328             Contract.EndContractBlock();
329
330             return GetCharCount(bytes, count, null);
331         }
332
333         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
334         // So if you fix this, fix the others.  Currently those include:
335         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
336         // parent method is safe
337
338         public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
339                                               char[] chars, int charIndex)
340         {
341             // Validate Parameters
342             if (bytes == null || chars == null)
343                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
344
345             if (byteIndex < 0 || byteCount < 0)
346                 throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
347
348             if ( bytes.Length - byteIndex < byteCount)
349                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
350
351             if (charIndex < 0 || charIndex > chars.Length)
352                 throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
353             Contract.EndContractBlock();
354
355             // If no input, return 0 & avoid fixed problem
356             if (byteCount == 0)
357                 return 0;
358
359             // Just call pointer version
360             int charCount = chars.Length - charIndex;
361
362             // Fixed doesn't like empty arrays
363             if (chars.Length == 0)
364                 chars = new char[1];
365
366             fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
367                 // Remember that charCount is # to decode, not size of array
368                 return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
369         }
370
371         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
372         // So if you fix this, fix the others.  Currently those include:
373         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
374
375         [CLSCompliant(false)]
376         public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
377         {
378             // Validate Parameters
379             if (bytes == null || chars == null)
380                 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
381
382             if (charCount < 0 || byteCount < 0)
383                 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
384             Contract.EndContractBlock();
385
386             return GetChars(bytes, byteCount, chars, charCount, null);
387         }
388
389         // Returns a string containing the decoded representation of a range of
390         // bytes in a byte array.
391         //
392         // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
393         // So if you fix this, fix the others.  Currently those include:
394         // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
395         // parent method is safe
396
397         public override unsafe String GetString(byte[] bytes, int index, int count)
398         {
399             // Validate Parameters
400             if (bytes == null)
401                 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
402
403             if (index < 0 || count < 0)
404                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
405
406             if (bytes.Length - index < count)
407                 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
408             Contract.EndContractBlock();
409
410             // Avoid problems with empty input buffer
411             if (count == 0) return String.Empty;
412
413             fixed (byte* pBytes = bytes)
414                 return String.CreateStringFromEncoding(
415                     pBytes + index, count, this);
416         }
417
418         //
419         // End of standard methods copied from EncodingNLS.cs
420         //
421
422         internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
423         {
424             Debug.Assert(chars != null, "[UTF7Encoding.GetByteCount]chars!=null");
425             Debug.Assert(count >= 0, "[UTF7Encoding.GetByteCount]count >=0");
426
427             // Just call GetBytes with bytes == null
428             return GetBytes(chars, count, null, 0, baseEncoder);
429         }
430
431         internal override unsafe int GetBytes(char* chars, int charCount,
432                                                 byte* bytes, int byteCount, EncoderNLS baseEncoder)
433         {
434             Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetBytes]byteCount >=0");
435             Debug.Assert(chars != null, "[UTF7Encoding.GetBytes]chars!=null");
436             Debug.Assert(charCount >= 0, "[UTF7Encoding.GetBytes]charCount >=0");
437
438             // Get encoder info
439             UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder;
440
441             // Default bits & count
442             int bits = 0;
443             int bitCount = -1;
444
445             // prepare our helpers
446             Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(
447                 this, encoder, bytes, byteCount, chars, charCount);
448
449             if (encoder != null)
450             {
451                 bits = encoder.bits;
452                 bitCount = encoder.bitCount;
453
454                 // May have had too many left over
455                 while (bitCount >= 6)
456                 {
457                     bitCount -= 6;
458                     // If we fail we'll never really have enough room
459                     if (!buffer.AddByte(_base64Bytes[(bits >> bitCount) & 0x3F]))
460                         ThrowBytesOverflow(encoder, buffer.Count == 0);
461                 }
462             }
463
464             while (buffer.MoreData)
465             {
466                 char currentChar = buffer.GetNextChar();
467
468                 if (currentChar < 0x80 && _directEncode[currentChar])
469                 {
470                     if (bitCount >= 0)
471                     {
472                         if (bitCount > 0)
473                         {
474                             // Try to add the next byte
475                             if (!buffer.AddByte(_base64Bytes[bits << 6 - bitCount & 0x3F]))
476                                 break;                                          // Stop here, didn't throw
477
478                             bitCount = 0;
479                         }
480
481                         // Need to get emit '-' and our char, 2 bytes total
482                         if (!buffer.AddByte((byte)'-'))
483                             break;                                          // Stop here, didn't throw
484
485                         bitCount = -1;
486                     }
487
488                     // Need to emit our char
489                     if (!buffer.AddByte((byte)currentChar))
490                         break;                                          // Stop here, didn't throw
491                 }
492                 else if (bitCount < 0 && currentChar == '+')
493                 {
494                     if (!buffer.AddByte((byte)'+', (byte)'-'))
495                         break;                                          // Stop here, didn't throw
496                 }
497                 else
498                 {
499                     if (bitCount < 0)
500                     {
501                         // Need to emit a + and 12 bits (3 bytes)
502                         // Only 12 of the 16 bits will be emitted this time, the other 4 wait 'til next time
503                         if (!buffer.AddByte((byte)'+'))
504                             break;                                          // Stop here, didn't throw
505
506                         // We're now in bit mode, but haven't stored data yet
507                         bitCount = 0;
508                     }
509
510                     // Add our bits
511                     bits = bits << 16 | currentChar;
512                     bitCount += 16;
513
514                     while (bitCount >= 6)
515                     {
516                         bitCount -= 6;
517                         if (!buffer.AddByte(_base64Bytes[(bits >> bitCount) & 0x3F]))
518                         {
519                             bitCount += 6;                              // We didn't use these bits
520                             currentChar = buffer.GetNextChar();              // We're processing this char still, but AddByte
521                                                                              // --'d it when we ran out of space
522                             break;                                      // Stop here, not enough room for bytes
523                         }
524                     }
525
526                     if (bitCount >= 6)
527                         break;                  // Didn't have room to encode enough bits
528                 }
529             }
530
531             // Now if we have bits left over we have to encode them.
532             // MustFlush may have been cleared by encoding.ThrowBytesOverflow earlier if converting
533             if (bitCount >= 0 && (encoder == null || encoder.MustFlush))
534             {
535                 // Do we have bits we have to stick in?
536                 if (bitCount > 0)
537                 {
538                     if (buffer.AddByte(_base64Bytes[(bits << (6 - bitCount)) & 0x3F]))
539                     {
540                         // Emitted spare bits, 0 bits left
541                         bitCount = 0;
542                     }
543                 }
544
545                 // If converting and failed bitCount above, then we'll fail this too
546                 if (buffer.AddByte((byte)'-'))
547                 {
548                     // turned off bit mode';
549                     bits = 0;
550                     bitCount = -1;
551                 }
552                 else
553                     // If not successful, convert will maintain state for next time, also
554                     // AddByte will have decremented our char count, however we need it to remain the same
555                     buffer.GetNextChar();
556             }
557
558             // Do we have an encoder we're allowed to use?
559             // bytes == null if counting, so don't use encoder then
560             if (bytes != null && encoder != null)
561             {
562                 // We already cleared bits & bitcount for mustflush case
563                 encoder.bits = bits;
564                 encoder.bitCount = bitCount;
565                 encoder._charsUsed = buffer.CharsUsed;
566             }
567
568             return buffer.Count;
569         }
570
571         internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
572         {
573             Debug.Assert(count >= 0, "[UTF7Encoding.GetCharCount]count >=0");
574             Debug.Assert(bytes != null, "[UTF7Encoding.GetCharCount]bytes!=null");
575
576             // Just call GetChars with null char* to do counting
577             return GetChars(bytes, count, null, 0, baseDecoder);
578         }
579
580         internal override unsafe int GetChars(byte* bytes, int byteCount,
581                                                 char* chars, int charCount, DecoderNLS baseDecoder)
582         {
583             Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetChars]byteCount >=0");
584             Debug.Assert(bytes != null, "[UTF7Encoding.GetChars]bytes!=null");
585             Debug.Assert(charCount >= 0, "[UTF7Encoding.GetChars]charCount >=0");
586
587             // Might use a decoder
588             UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder)baseDecoder;
589
590             // Get our output buffer info.
591             Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(
592                 this, decoder, chars, charCount, bytes, byteCount);
593
594             // Get decoder info
595             int bits = 0;
596             int bitCount = -1;
597             bool firstByte = false;
598             if (decoder != null)
599             {
600                 bits = decoder.bits;
601                 bitCount = decoder.bitCount;
602                 firstByte = decoder.firstByte;
603
604                 Debug.Assert(firstByte == false || decoder.bitCount <= 0,
605                     "[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set");
606             }
607
608             // We may have had bits in the decoder that we couldn't output last time, so do so now
609             if (bitCount >= 16)
610             {
611                 // Check our decoder buffer
612                 if (!buffer.AddChar((char)((bits >> (bitCount - 16)) & 0xFFFF)))
613                     ThrowCharsOverflow(decoder, true);  // Always throw, they need at least 1 char even in Convert
614
615                 // Used this one, clean up extra bits
616                 bitCount -= 16;
617             }
618
619             // Loop through the input
620             while (buffer.MoreData)
621             {
622                 byte currentByte = buffer.GetNextByte();
623                 int c;
624
625                 if (bitCount >= 0)
626                 {
627                     //
628                     // Modified base 64 encoding.
629                     //
630                     sbyte v;
631                     if (currentByte < 0x80 && ((v = _base64Values[currentByte]) >= 0))
632                     {
633                         firstByte = false;
634                         bits = (bits << 6) | ((byte)v);
635                         bitCount += 6;
636                         if (bitCount >= 16)
637                         {
638                             c = (bits >> (bitCount - 16)) & 0xFFFF;
639                             bitCount -= 16;
640                         }
641                         // If not enough bits just continue
642                         else continue;
643                     }
644                     else
645                     {
646                         // If it wasn't a base 64 byte, everything's going to turn off base 64 mode
647                         bitCount = -1;
648
649                         if (currentByte != '-')
650                         {
651                             // >= 0x80 (because of 1st if statemtn)
652                             // We need this check since the _base64Values[b] check below need b <= 0x7f.
653                             // This is not a valid base 64 byte.  Terminate the shifted-sequence and
654                             // emit this byte.
655
656                             // not in base 64 table
657                             // According to the RFC 1642 and the example code of UTF-7
658                             // in Unicode 2.0, we should just zero-extend the invalid UTF7 byte
659
660                             // Chars won't be updated unless this works, try to fallback
661                             if (!buffer.Fallback(currentByte))
662                                 break;                                          // Stop here, didn't throw
663
664                             // Used that byte, we're done with it
665                             continue;
666                         }
667
668                         //
669                         // The encoding for '+' is "+-".
670                         //
671                         if (firstByte) c = '+';
672                         // We just turn it off if not emitting a +, so we're done.
673                         else continue;
674                     }
675                     //
676                     // End of modified base 64 encoding block.
677                     //
678                 }
679                 else if (currentByte == '+')
680                 {
681                     //
682                     // Found the start of a modified base 64 encoding block or a plus sign.
683                     //
684                     bitCount = 0;
685                     firstByte = true;
686                     continue;
687                 }
688                 else
689                 {
690                     // Normal character
691                     if (currentByte >= 0x80)
692                     {
693                         // Try to fallback
694                         if (!buffer.Fallback(currentByte))
695                             break;                                          // Stop here, didn't throw
696
697                         // Done falling back
698                         continue;
699                     }
700
701                     // Use the normal character
702                     c = currentByte;
703                 }
704
705                 if (c >= 0)
706                 {
707                     // Check our buffer
708                     if (!buffer.AddChar((char)c))
709                     {
710                         // No room.  If it was a plain char we'll try again later.
711                         // Note, we'll consume this byte and stick it in decoder, even if we can't output it
712                         if (bitCount >= 0)                                  // Can we rememmber this byte (char)
713                         {
714                             buffer.AdjustBytes(+1);                         // Need to readd the byte that AddChar subtracted when it failed
715                             bitCount += 16;                                 // We'll still need that char we have in our bits
716                         }
717                         break;                                              // didn't throw, stop
718                     }
719                 }
720             }
721
722             // Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder)
723             if (chars != null && decoder != null)
724             {
725                 // MustFlush?  (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer)
726                 if (decoder.MustFlush)
727                 {
728                     // RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them
729                     decoder.bits = 0;
730                     decoder.bitCount = -1;
731                     decoder.firstByte = false;
732                 }
733                 else
734                 {
735                     decoder.bits = bits;
736                     decoder.bitCount = bitCount;
737                     decoder.firstByte = firstByte;
738                 }
739                 decoder._bytesUsed = buffer.BytesUsed;
740             }
741             // else ignore any hanging bits.
742
743             // Return our count
744             return buffer.Count;
745         }
746
747
748         public override System.Text.Decoder GetDecoder()
749         {
750             return new UTF7Encoding.Decoder(this);
751         }
752
753
754         public override System.Text.Encoder GetEncoder()
755         {
756             return new UTF7Encoding.Encoder(this);
757         }
758
759
760         public override int GetMaxByteCount(int charCount)
761         {
762             if (charCount < 0)
763                 throw new ArgumentOutOfRangeException(nameof(charCount),
764                      SR.ArgumentOutOfRange_NeedNonNegNum);
765             Contract.EndContractBlock();
766
767             // Suppose that every char can not be direct-encoded, we know that
768             // a byte can encode 6 bits of the Unicode character.  And we will
769             // also need two extra bytes for the shift-in ('+') and shift-out ('-') mark.
770             // Therefore, the max byte should be:
771             // byteCount = 2 + Math.Ceiling((double)charCount * 16 / 6);
772             // That is always <= 2 + 3 * charCount;
773             // Longest case is alternating encoded, direct, encoded data for 5 + 1 + 5... bytes per char.
774             // UTF7 doesn't have left over surrogates, but if no input we may need an output - to turn off
775             // encoding if MustFlush is true.
776
777             // Its easiest to think of this as 2 bytes to turn on/off the base64 mode, then 3 bytes per char.
778             // 3 bytes is 18 bits of encoding, which is more than we need, but if its direct encoded then 3
779             // bytes allows us to turn off and then back on base64 mode if necessary.
780
781             // Note that UTF7 encoded surrogates individually and isn't worried about mismatches, so all
782             // code points are encodable int UTF7.
783             long byteCount = (long)charCount * 3 + 2;
784
785             // check for overflow
786             if (byteCount > 0x7fffffff)
787                 throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
788
789             return (int)byteCount;
790         }
791
792
793         public override int GetMaxCharCount(int byteCount)
794         {
795             if (byteCount < 0)
796                 throw new ArgumentOutOfRangeException(nameof(byteCount),
797                      SR.ArgumentOutOfRange_NeedNonNegNum);
798             Contract.EndContractBlock();
799
800             // Worst case is 1 char per byte.  Minimum 1 for left over bits in case decoder is being flushed
801             // Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction.
802             int charCount = byteCount;
803             if (charCount == 0) charCount = 1;
804
805             return charCount;
806         }
807
808         // Of all the amazing things... This MUST be Decoder so that our com name
809         // for System.Text.Decoder doesn't change
810         private sealed class Decoder : DecoderNLS
811         {
812             /*private*/
813             internal int bits;
814             /*private*/
815             internal int bitCount;
816             /*private*/
817             internal bool firstByte;
818
819             public Decoder(UTF7Encoding encoding) : base(encoding)
820             {
821                 // base calls reset
822             }
823
824             public override void Reset()
825             {
826                 this.bits = 0;
827                 this.bitCount = -1;
828                 this.firstByte = false;
829                 if (_fallbackBuffer != null)
830                     _fallbackBuffer.Reset();
831             }
832
833             // Anything left in our encoder?
834             internal override bool HasState
835             {
836                 get
837                 {
838                     // NOTE: This forces the last -, which some encoder might not encode.  If we
839                     // don't see it we don't think we're done reading.
840                     return (this.bitCount != -1);
841                 }
842             }
843         }
844
845         // Of all the amazing things... This MUST be Encoder so that our com name
846         // for System.Text.Encoder doesn't change
847         private sealed class Encoder : EncoderNLS
848         {
849             /*private*/
850             internal int bits;
851             /*private*/
852             internal int bitCount;
853
854             public Encoder(UTF7Encoding encoding) : base(encoding)
855             {
856                 // base calls reset
857             }
858
859             public override void Reset()
860             {
861                 this.bitCount = -1;
862                 this.bits = 0;
863                 if (_fallbackBuffer != null)
864                     _fallbackBuffer.Reset();
865             }
866
867             // Anything left in our encoder?
868             internal override bool HasState
869             {
870                 get
871                 {
872                     return (this.bits != 0 || this.bitCount != -1);
873                 }
874             }
875         }
876
877         // Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char
878         // and turn off base64 mode if it was in that mode.  We still exit the mode, but now we fallback.
879         private sealed class DecoderUTF7Fallback : DecoderFallback
880         {
881             // Construction.  Default replacement fallback uses no best fit and ? replacement string
882             public DecoderUTF7Fallback()
883             {
884             }
885
886             public override DecoderFallbackBuffer CreateFallbackBuffer()
887             {
888                 return new DecoderUTF7FallbackBuffer(this);
889             }
890
891             // Maximum number of characters that this instance of this fallback could return
892             public override int MaxCharCount
893             {
894                 get
895                 {
896                     // returns 1 char per bad byte
897                     return 1;
898                 }
899             }
900
901             public override bool Equals(Object value)
902             {
903                 DecoderUTF7Fallback that = value as DecoderUTF7Fallback;
904                 if (that != null)
905                 {
906                     return true;
907                 }
908                 return (false);
909             }
910
911             public override int GetHashCode()
912             {
913                 return 984;
914             }
915         }
916
917         private sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer
918         {
919             // Store our default string
920             private char cFallback = (char)0;
921             private int iCount = -1;
922             private int iSize;
923
924             // Construction
925             public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback)
926             {
927             }
928
929             // Fallback Methods
930             public override bool Fallback(byte[] bytesUnknown, int index)
931             {
932                 // We expect no previous fallback in our buffer
933                 Debug.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.Fallback] Can't have recursive fallbacks");
934                 Debug.Assert(bytesUnknown.Length == 1, "[DecoderUTF7FallbackBuffer.Fallback] Only possible fallback case should be 1 unknown byte");
935
936                 // Go ahead and get our fallback
937                 cFallback = (char)bytesUnknown[0];
938
939                 // Any of the fallback characters can be handled except for 0
940                 if (cFallback == 0)
941                 {
942                     return false;
943                 }
944
945                 iCount = iSize = 1;
946
947                 return true;
948             }
949
950             public override char GetNextChar()
951             {
952                 if (iCount-- > 0)
953                     return cFallback;
954
955                 // Note: this means that 0 in UTF7 stream will never be emitted.
956                 return (char)0;
957             }
958
959             public override bool MovePrevious()
960             {
961                 if (iCount >= 0)
962                 {
963                     iCount++;
964                 }
965
966                 // return true if we were allowed to do this
967                 return (iCount >= 0 && iCount <= iSize);
968             }
969
970             // Return # of chars left in this fallback
971             public override int Remaining
972             {
973                 get
974                 {
975                     return (iCount > 0) ? iCount : 0;
976                 }
977             }
978
979             // Clear the buffer
980             public override unsafe void Reset()
981             {
982                 iCount = -1;
983                 byteStart = null;
984             }
985
986             // This version just counts the fallback and doesn't actually copy anything.
987             internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
988             // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
989             // array, and we might need the index, hence the byte*
990             {
991                 // We expect no previous fallback in our buffer
992                 Debug.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.InternalFallback] Can't have recursive fallbacks");
993                 if (bytes.Length != 1)
994                 {
995                     throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
996                 }
997
998                 // Can't fallback a byte 0, so return for that case, 1 otherwise.
999                 return bytes[0] == 0 ? 0 : 1;
1000             }
1001         }
1002     }
1003 }