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.
6 // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
10 using System.Diagnostics;
11 using System.Diagnostics.Contracts;
15 public class UTF7Encoding : Encoding
17 private const String base64Chars =
18 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
19 // 0123456789111111111122222222223333333333444444444455555555556666
20 // 012345678901234567890123456789012345678901234567890123
22 // These are the characters that can be directly encoded in UTF7.
23 private const String directChars =
24 "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
26 // These are the characters that can be optionally directly encoded in UTF7.
27 private const String optionalChars =
28 "!\"#$%&*;<=>@[]^_`{|}";
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();
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
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;
45 private bool _allowOptionals;
47 private const int UTF7_CODEPAGE = 65000;
55 public UTF7Encoding(bool allowOptionals)
56 : base(UTF7_CODEPAGE) //Set the data item.
58 // Allowing optionals?
59 _allowOptionals = allowOptionals;
65 private void MakeTables()
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++)
77 _directEncode[directChars[i]] = true;
82 count = optionalChars.Length;
83 for (int i = 0; i < count; i++)
85 _directEncode[optionalChars[i]] = true;
90 // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7.
91 internal override void SetDefaultFallbacks()
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();
100 public override bool Equals(Object value)
102 UTF7Encoding that = value as UTF7Encoding;
105 return (_allowOptionals == that._allowOptionals) &&
106 (EncoderFallback.Equals(that.EncoderFallback)) &&
107 (DecoderFallback.Equals(that.DecoderFallback));
112 // Compared to all the other encodings, variations of UTF7 are unlikely
114 public override int GetHashCode()
116 return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode();
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
124 // Returns the number of bytes required to encode a range of characters in
125 // a character array.
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
132 public override unsafe int GetByteCount(char[] chars, int index, int count)
134 // Validate input parameters
136 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
138 if (index < 0 || count < 0)
139 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
141 if (chars.Length - index < count)
142 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
143 Contract.EndContractBlock();
145 // If no input, return 0, avoid fixed empty array problem
149 // Just call the pointer version
150 fixed (char* pChars = chars)
151 return GetByteCount(pChars + index, count, null);
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
159 public override unsafe int GetByteCount(string s)
163 throw new ArgumentNullException("s");
164 Contract.EndContractBlock();
166 fixed (char* pChars = s)
167 return GetByteCount(pChars, s.Length, null);
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
174 [CLSCompliant(false)]
175 public override unsafe int GetByteCount(char* chars, int count)
177 // Validate Parameters
179 throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
182 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
183 Contract.EndContractBlock();
185 // Call it with empty encoder
186 return GetByteCount(chars, count, null);
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
194 public override unsafe int GetBytes(string s, int charIndex, int charCount,
195 byte[] bytes, int byteIndex)
197 if (s == null || bytes == null)
198 throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
200 if (charIndex < 0 || charCount < 0)
201 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
203 if (s.Length - charIndex < charCount)
204 throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
206 if (byteIndex < 0 || byteIndex > bytes.Length)
207 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
208 Contract.EndContractBlock();
210 int byteCount = bytes.Length - byteIndex;
212 // Fixed doesn't like empty arrays
213 if (bytes.Length == 0)
216 fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
217 return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
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.
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
234 public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
235 byte[] bytes, int byteIndex)
237 // Validate parameters
238 if (chars == null || bytes == null)
239 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
241 if (charIndex < 0 || charCount < 0)
242 throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
244 if (chars.Length - charIndex < charCount)
245 throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
247 if (byteIndex < 0 || byteIndex > bytes.Length)
248 throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
249 Contract.EndContractBlock();
251 // If nothing to encode return 0, avoid fixed problem
255 // Just call pointer version
256 int byteCount = bytes.Length - byteIndex;
258 // Fixed doesn't like empty arrays
259 if (bytes.Length == 0)
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);
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
271 [CLSCompliant(false)]
272 public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
274 // Validate Parameters
275 if (bytes == null || chars == null)
276 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
278 if (charCount < 0 || byteCount < 0)
279 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
280 Contract.EndContractBlock();
282 return GetBytes(chars, charCount, bytes, byteCount, null);
285 // Returns the number of characters produced by decoding a range of bytes
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
293 public override unsafe int GetCharCount(byte[] bytes, int index, int count)
295 // Validate Parameters
297 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
299 if (index < 0 || count < 0)
300 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
302 if (bytes.Length - index < count)
303 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
304 Contract.EndContractBlock();
306 // If no input just return 0, fixed doesn't like 0 length arrays.
310 // Just call pointer version
311 fixed (byte* pBytes = bytes)
312 return GetCharCount(pBytes + index, count, null);
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
319 [CLSCompliant(false)]
320 public override unsafe int GetCharCount(byte* bytes, int count)
322 // Validate Parameters
324 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
327 throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
328 Contract.EndContractBlock();
330 return GetCharCount(bytes, count, null);
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
338 public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
339 char[] chars, int charIndex)
341 // Validate Parameters
342 if (bytes == null || chars == null)
343 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
345 if (byteIndex < 0 || byteCount < 0)
346 throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
348 if ( bytes.Length - byteIndex < byteCount)
349 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
351 if (charIndex < 0 || charIndex > chars.Length)
352 throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
353 Contract.EndContractBlock();
355 // If no input, return 0 & avoid fixed problem
359 // Just call pointer version
360 int charCount = chars.Length - charIndex;
362 // Fixed doesn't like empty arrays
363 if (chars.Length == 0)
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);
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
375 [CLSCompliant(false)]
376 public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
378 // Validate Parameters
379 if (bytes == null || chars == null)
380 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
382 if (charCount < 0 || byteCount < 0)
383 throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
384 Contract.EndContractBlock();
386 return GetChars(bytes, byteCount, chars, charCount, null);
389 // Returns a string containing the decoded representation of a range of
390 // bytes in a byte array.
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
397 public override unsafe String GetString(byte[] bytes, int index, int count)
399 // Validate Parameters
401 throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
403 if (index < 0 || count < 0)
404 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
406 if (bytes.Length - index < count)
407 throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
408 Contract.EndContractBlock();
410 // Avoid problems with empty input buffer
411 if (count == 0) return String.Empty;
413 fixed (byte* pBytes = bytes)
414 return String.CreateStringFromEncoding(
415 pBytes + index, count, this);
419 // End of standard methods copied from EncodingNLS.cs
422 internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
424 Debug.Assert(chars != null, "[UTF7Encoding.GetByteCount]chars!=null");
425 Debug.Assert(count >= 0, "[UTF7Encoding.GetByteCount]count >=0");
427 // Just call GetBytes with bytes == null
428 return GetBytes(chars, count, null, 0, baseEncoder);
431 internal override unsafe int GetBytes(char* chars, int charCount,
432 byte* bytes, int byteCount, EncoderNLS baseEncoder)
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");
439 UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder;
441 // Default bits & count
445 // prepare our helpers
446 Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(
447 this, encoder, bytes, byteCount, chars, charCount);
452 bitCount = encoder.bitCount;
454 // May have had too many left over
455 while (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);
464 while (buffer.MoreData)
466 char currentChar = buffer.GetNextChar();
468 if (currentChar < 0x80 && _directEncode[currentChar])
474 // Try to add the next byte
475 if (!buffer.AddByte(_base64Bytes[bits << 6 - bitCount & 0x3F]))
476 break; // Stop here, didn't throw
481 // Need to get emit '-' and our char, 2 bytes total
482 if (!buffer.AddByte((byte)'-'))
483 break; // Stop here, didn't throw
488 // Need to emit our char
489 if (!buffer.AddByte((byte)currentChar))
490 break; // Stop here, didn't throw
492 else if (bitCount < 0 && currentChar == '+')
494 if (!buffer.AddByte((byte)'+', (byte)'-'))
495 break; // Stop here, didn't throw
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
506 // We're now in bit mode, but haven't stored data yet
511 bits = bits << 16 | currentChar;
514 while (bitCount >= 6)
517 if (!buffer.AddByte(_base64Bytes[(bits >> bitCount) & 0x3F]))
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
527 break; // Didn't have room to encode enough bits
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))
535 // Do we have bits we have to stick in?
538 if (buffer.AddByte(_base64Bytes[(bits << (6 - bitCount)) & 0x3F]))
540 // Emitted spare bits, 0 bits left
545 // If converting and failed bitCount above, then we'll fail this too
546 if (buffer.AddByte((byte)'-'))
548 // turned off bit mode';
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();
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)
562 // We already cleared bits & bitcount for mustflush case
564 encoder.bitCount = bitCount;
565 encoder._charsUsed = buffer.CharsUsed;
571 internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
573 Debug.Assert(count >= 0, "[UTF7Encoding.GetCharCount]count >=0");
574 Debug.Assert(bytes != null, "[UTF7Encoding.GetCharCount]bytes!=null");
576 // Just call GetChars with null char* to do counting
577 return GetChars(bytes, count, null, 0, baseDecoder);
580 internal override unsafe int GetChars(byte* bytes, int byteCount,
581 char* chars, int charCount, DecoderNLS baseDecoder)
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");
587 // Might use a decoder
588 UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder)baseDecoder;
590 // Get our output buffer info.
591 Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(
592 this, decoder, chars, charCount, bytes, byteCount);
597 bool firstByte = false;
601 bitCount = decoder.bitCount;
602 firstByte = decoder.firstByte;
604 Debug.Assert(firstByte == false || decoder.bitCount <= 0,
605 "[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set");
608 // We may have had bits in the decoder that we couldn't output last time, so do so now
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
615 // Used this one, clean up extra bits
619 // Loop through the input
620 while (buffer.MoreData)
622 byte currentByte = buffer.GetNextByte();
628 // Modified base 64 encoding.
631 if (currentByte < 0x80 && ((v = _base64Values[currentByte]) >= 0))
634 bits = (bits << 6) | ((byte)v);
638 c = (bits >> (bitCount - 16)) & 0xFFFF;
641 // If not enough bits just continue
646 // If it wasn't a base 64 byte, everything's going to turn off base 64 mode
649 if (currentByte != '-')
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
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
660 // Chars won't be updated unless this works, try to fallback
661 if (!buffer.Fallback(currentByte))
662 break; // Stop here, didn't throw
664 // Used that byte, we're done with it
669 // The encoding for '+' is "+-".
671 if (firstByte) c = '+';
672 // We just turn it off if not emitting a +, so we're done.
676 // End of modified base 64 encoding block.
679 else if (currentByte == '+')
682 // Found the start of a modified base 64 encoding block or a plus sign.
691 if (currentByte >= 0x80)
694 if (!buffer.Fallback(currentByte))
695 break; // Stop here, didn't throw
701 // Use the normal character
708 if (!buffer.AddChar((char)c))
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)
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
717 break; // didn't throw, stop
722 // Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder)
723 if (chars != null && decoder != null)
725 // MustFlush? (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer)
726 if (decoder.MustFlush)
728 // RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them
730 decoder.bitCount = -1;
731 decoder.firstByte = false;
736 decoder.bitCount = bitCount;
737 decoder.firstByte = firstByte;
739 decoder._bytesUsed = buffer.BytesUsed;
741 // else ignore any hanging bits.
748 public override System.Text.Decoder GetDecoder()
750 return new UTF7Encoding.Decoder(this);
754 public override System.Text.Encoder GetEncoder()
756 return new UTF7Encoding.Encoder(this);
760 public override int GetMaxByteCount(int charCount)
763 throw new ArgumentOutOfRangeException(nameof(charCount),
764 SR.ArgumentOutOfRange_NeedNonNegNum);
765 Contract.EndContractBlock();
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.
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.
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;
785 // check for overflow
786 if (byteCount > 0x7fffffff)
787 throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
789 return (int)byteCount;
793 public override int GetMaxCharCount(int byteCount)
796 throw new ArgumentOutOfRangeException(nameof(byteCount),
797 SR.ArgumentOutOfRange_NeedNonNegNum);
798 Contract.EndContractBlock();
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;
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
815 internal int bitCount;
817 internal bool firstByte;
819 public Decoder(UTF7Encoding encoding) : base(encoding)
824 public override void Reset()
828 this.firstByte = false;
829 if (_fallbackBuffer != null)
830 _fallbackBuffer.Reset();
833 // Anything left in our encoder?
834 internal override bool HasState
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);
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
852 internal int bitCount;
854 public Encoder(UTF7Encoding encoding) : base(encoding)
859 public override void Reset()
863 if (_fallbackBuffer != null)
864 _fallbackBuffer.Reset();
867 // Anything left in our encoder?
868 internal override bool HasState
872 return (this.bits != 0 || this.bitCount != -1);
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
881 // Construction. Default replacement fallback uses no best fit and ? replacement string
882 public DecoderUTF7Fallback()
886 public override DecoderFallbackBuffer CreateFallbackBuffer()
888 return new DecoderUTF7FallbackBuffer(this);
891 // Maximum number of characters that this instance of this fallback could return
892 public override int MaxCharCount
896 // returns 1 char per bad byte
901 public override bool Equals(Object value)
903 DecoderUTF7Fallback that = value as DecoderUTF7Fallback;
911 public override int GetHashCode()
917 private sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer
919 // Store our default string
920 private char cFallback = (char)0;
921 private int iCount = -1;
925 public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback)
930 public override bool Fallback(byte[] bytesUnknown, int index)
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");
936 // Go ahead and get our fallback
937 cFallback = (char)bytesUnknown[0];
939 // Any of the fallback characters can be handled except for 0
950 public override char GetNextChar()
955 // Note: this means that 0 in UTF7 stream will never be emitted.
959 public override bool MovePrevious()
966 // return true if we were allowed to do this
967 return (iCount >= 0 && iCount <= iSize);
970 // Return # of chars left in this fallback
971 public override int Remaining
975 return (iCount > 0) ? iCount : 0;
980 public override unsafe void Reset()
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*
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)
995 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
998 // Can't fallback a byte 0, so return for that case, 1 otherwise.
999 return bytes[0] == 0 ? 0 : 1;