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