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.
12 using System.Runtime.Serialization;
13 using System.Security.Permissions;
14 using System.Diagnostics.Contracts;
18 [System.Runtime.InteropServices.ComVisible(true)]
19 public class UTF7Encoding : Encoding
21 private const String base64Chars =
22 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
23 // 0123456789111111111122222222223333333333444444444455555555556666
24 // 012345678901234567890123456789012345678901234567890123
26 // These are the characters that can be directly encoded in UTF7.
27 private const String directChars =
28 "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
30 // These are the characters that can be optionally directly encoded in UTF7.
31 private const String optionalChars =
32 "!\"#$%&*;<=>@[]^_`{|}";
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 [OptionalField(VersionAdded = 2)]
46 private bool m_allowOptionals;
48 private const int UTF7_CODEPAGE=65000;
56 public UTF7Encoding(bool allowOptionals)
57 : base(UTF7_CODEPAGE) //Set the data item.
59 // Allowing optionals?
60 this.m_allowOptionals = allowOptionals;
66 private void MakeTables()
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++)
78 directEncode[directChars[i]] = true;
81 if (this.m_allowOptionals)
83 count = optionalChars.Length;
84 for (int i = 0; i < count; i++)
86 directEncode[optionalChars[i]] = true;
91 // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7.
92 internal override void SetDefaultFallbacks()
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();
102 #region Serialization
104 private void OnDeserializing(StreamingContext ctx)
106 // make sure the optional fields initialized correctly.
107 base.OnDeserializing();
111 private void OnDeserialized(StreamingContext ctx)
113 base.OnDeserialized();
115 if (m_deserializedFromEverett)
117 // If 1st optional char is encoded we're allowing optionals
118 m_allowOptionals = directEncode[optionalChars[0]];
123 #endregion Serialization
127 [System.Runtime.InteropServices.ComVisible(false)]
128 public override bool Equals(Object value)
130 UTF7Encoding that = value as UTF7Encoding;
133 return (m_allowOptionals == that.m_allowOptionals) &&
134 (EncoderFallback.Equals(that.EncoderFallback)) &&
135 (DecoderFallback.Equals(that.DecoderFallback));
140 // Compared to all the other encodings, variations of UTF7 are unlikely
142 [System.Runtime.InteropServices.ComVisible(false)]
143 public override int GetHashCode()
145 return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode();
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
155 // Returns the number of bytes required to encode a range of characters in
156 // a character array.
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
163 [System.Security.SecuritySafeCritical] // auto-generated
164 public override unsafe int GetByteCount(char[] chars, int index, int count)
166 // Validate input parameters
168 throw new ArgumentNullException("chars",
169 Environment.GetResourceString("ArgumentNull_Array"));
171 if (index < 0 || count < 0)
172 throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"),
173 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
175 if (chars.Length - index < count)
176 throw new ArgumentOutOfRangeException("chars",
177 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
178 Contract.EndContractBlock();
180 // If no input, return 0, avoid fixed empty array problem
181 if (chars.Length == 0)
184 // Just call the pointer version
185 fixed (char* pChars = chars)
186 return GetByteCount(pChars + index, count, null);
189 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
190 // So if you fix this, fix the others. Currently those include:
191 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
192 // parent method is safe
194 [System.Security.SecuritySafeCritical] // auto-generated
195 [System.Runtime.InteropServices.ComVisible(false)]
196 public override unsafe int GetByteCount(String s)
200 throw new ArgumentNullException("s");
201 Contract.EndContractBlock();
203 fixed (char* pChars = s)
204 return GetByteCount(pChars, s.Length, null);
207 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
208 // So if you fix this, fix the others. Currently those include:
209 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
211 [System.Security.SecurityCritical] // auto-generated
212 [CLSCompliant(false)]
213 [System.Runtime.InteropServices.ComVisible(false)]
214 public override unsafe int GetByteCount(char* chars, int count)
216 // Validate Parameters
218 throw new ArgumentNullException("chars",
219 Environment.GetResourceString("ArgumentNull_Array"));
222 throw new ArgumentOutOfRangeException("count",
223 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
224 Contract.EndContractBlock();
226 // Call it with empty encoder
227 return GetByteCount(chars, count, null);
230 // Parent method is safe.
231 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
232 // So if you fix this, fix the others. Currently those include:
233 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
235 [System.Security.SecuritySafeCritical] // auto-generated
236 [System.Runtime.InteropServices.ComVisible(false)]
237 public override unsafe int GetBytes(String s, int charIndex, int charCount,
238 byte[] bytes, int byteIndex)
240 if (s == null || bytes == null)
241 throw new ArgumentNullException((s == null ? "s" : "bytes"),
242 Environment.GetResourceString("ArgumentNull_Array"));
244 if (charIndex < 0 || charCount < 0)
245 throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"),
246 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
248 if (s.Length - charIndex < charCount)
249 throw new ArgumentOutOfRangeException("s",
250 Environment.GetResourceString("ArgumentOutOfRange_IndexCount"));
252 if (byteIndex < 0 || byteIndex > bytes.Length)
253 throw new ArgumentOutOfRangeException("byteIndex",
254 Environment.GetResourceString("ArgumentOutOfRange_Index"));
255 Contract.EndContractBlock();
257 int byteCount = bytes.Length - byteIndex;
259 // Fixed doesn't like empty arrays
260 if (bytes.Length == 0)
263 fixed (char* pChars = s)
264 fixed ( byte* pBytes = bytes)
265 return GetBytes(pChars + charIndex, charCount,
266 pBytes + byteIndex, byteCount, null);
269 // Encodes a range of characters in a character array into a range of bytes
270 // in a byte array. An exception occurs if the byte array is not large
271 // enough to hold the complete encoding of the characters. The
272 // GetByteCount method can be used to determine the exact number of
273 // bytes that will be produced for a given range of characters.
274 // Alternatively, the GetMaxByteCount method can be used to
275 // determine the maximum number of bytes that will be produced for a given
276 // number of characters, regardless of the actual character values.
278 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
279 // So if you fix this, fix the others. Currently those include:
280 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
281 // parent method is safe
283 [System.Security.SecuritySafeCritical] // auto-generated
284 public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
285 byte[] bytes, int byteIndex)
287 // Validate parameters
288 if (chars == null || bytes == null)
289 throw new ArgumentNullException((chars == null ? "chars" : "bytes"),
290 Environment.GetResourceString("ArgumentNull_Array"));
292 if (charIndex < 0 || charCount < 0)
293 throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"),
294 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
296 if (chars.Length - charIndex < charCount)
297 throw new ArgumentOutOfRangeException("chars",
298 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
300 if (byteIndex < 0 || byteIndex > bytes.Length)
301 throw new ArgumentOutOfRangeException("byteIndex",
302 Environment.GetResourceString("ArgumentOutOfRange_Index"));
303 Contract.EndContractBlock();
305 // If nothing to encode return 0, avoid fixed problem
306 if (chars.Length == 0)
309 // Just call pointer version
310 int byteCount = bytes.Length - byteIndex;
312 // Fixed doesn't like empty arrays
313 if (bytes.Length == 0)
316 fixed (char* pChars = chars)
317 fixed (byte* pBytes = bytes)
318 // Remember that byteCount is # to decode, not size of array.
319 return GetBytes(pChars + charIndex, charCount,
320 pBytes + byteIndex, byteCount, null);
323 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
324 // So if you fix this, fix the others. Currently those include:
325 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
327 [System.Security.SecurityCritical] // auto-generated
328 [CLSCompliant(false)]
329 [System.Runtime.InteropServices.ComVisible(false)]
330 public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
332 // Validate Parameters
333 if (bytes == null || chars == null)
334 throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
335 Environment.GetResourceString("ArgumentNull_Array"));
337 if (charCount < 0 || byteCount < 0)
338 throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"),
339 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
340 Contract.EndContractBlock();
342 return GetBytes(chars, charCount, bytes, byteCount, null);
345 // Returns the number of characters produced by decoding a range of bytes
348 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
349 // So if you fix this, fix the others. Currently those include:
350 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
351 // parent method is safe
353 [System.Security.SecuritySafeCritical] // auto-generated
354 public override unsafe int GetCharCount(byte[] bytes, int index, int count)
356 // Validate Parameters
358 throw new ArgumentNullException("bytes",
359 Environment.GetResourceString("ArgumentNull_Array"));
361 if (index < 0 || count < 0)
362 throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"),
363 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
365 if (bytes.Length - index < count)
366 throw new ArgumentOutOfRangeException("bytes",
367 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
368 Contract.EndContractBlock();
370 // If no input just return 0, fixed doesn't like 0 length arrays.
371 if (bytes.Length == 0)
374 // Just call pointer version
375 fixed (byte* pBytes = bytes)
376 return GetCharCount(pBytes + index, count, null);
379 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
380 // So if you fix this, fix the others. Currently those include:
381 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
383 [System.Security.SecurityCritical] // auto-generated
384 [CLSCompliant(false)]
385 [System.Runtime.InteropServices.ComVisible(false)]
386 public override unsafe int GetCharCount(byte* bytes, int count)
388 // Validate Parameters
390 throw new ArgumentNullException("bytes",
391 Environment.GetResourceString("ArgumentNull_Array"));
394 throw new ArgumentOutOfRangeException("count",
395 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
396 Contract.EndContractBlock();
398 return GetCharCount(bytes, count, null);
401 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
402 // So if you fix this, fix the others. Currently those include:
403 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
404 // parent method is safe
406 [System.Security.SecuritySafeCritical] // auto-generated
407 public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
408 char[] chars, int charIndex)
410 // Validate Parameters
411 if (bytes == null || chars == null)
412 throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
413 Environment.GetResourceString("ArgumentNull_Array"));
415 if (byteIndex < 0 || byteCount < 0)
416 throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"),
417 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
419 if ( bytes.Length - byteIndex < byteCount)
420 throw new ArgumentOutOfRangeException("bytes",
421 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
423 if (charIndex < 0 || charIndex > chars.Length)
424 throw new ArgumentOutOfRangeException("charIndex",
425 Environment.GetResourceString("ArgumentOutOfRange_Index"));
426 Contract.EndContractBlock();
428 // If no input, return 0 & avoid fixed problem
429 if (bytes.Length == 0)
432 // Just call pointer version
433 int charCount = chars.Length - charIndex;
435 // Fixed doesn't like empty arrays
436 if (chars.Length == 0)
439 fixed (byte* pBytes = bytes)
440 fixed (char* pChars = chars)
441 // Remember that charCount is # to decode, not size of array
442 return GetChars(pBytes + byteIndex, byteCount,
443 pChars + charIndex, charCount, null);
446 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
447 // So if you fix this, fix the others. Currently those include:
448 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
450 [System.Security.SecurityCritical] // auto-generated
451 [CLSCompliant(false)]
452 [System.Runtime.InteropServices.ComVisible(false)]
453 public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
455 // Validate Parameters
456 if (bytes == null || chars == null)
457 throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
458 Environment.GetResourceString("ArgumentNull_Array"));
460 if (charCount < 0 || byteCount < 0)
461 throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"),
462 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
463 Contract.EndContractBlock();
465 return GetChars(bytes, byteCount, chars, charCount, null);
468 // Returns a string containing the decoded representation of a range of
469 // bytes in a byte array.
471 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
472 // So if you fix this, fix the others. Currently those include:
473 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
474 // parent method is safe
476 [System.Security.SecuritySafeCritical] // auto-generated
477 [System.Runtime.InteropServices.ComVisible(false)]
478 public override unsafe String GetString(byte[] bytes, int index, int count)
480 // Validate Parameters
482 throw new ArgumentNullException("bytes",
483 Environment.GetResourceString("ArgumentNull_Array"));
485 if (index < 0 || count < 0)
486 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"),
487 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
489 if (bytes.Length - index < count)
490 throw new ArgumentOutOfRangeException("bytes",
491 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
492 Contract.EndContractBlock();
494 // Avoid problems with empty input buffer
495 if (bytes.Length == 0) return String.Empty;
497 fixed (byte* pBytes = bytes)
498 return String.CreateStringFromEncoding(
499 pBytes + index, count, this);
503 // End of standard methods copied from EncodingNLS.cs
506 [System.Security.SecurityCritical] // auto-generated
507 internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
509 Contract.Assert(chars!=null, "[UTF7Encoding.GetByteCount]chars!=null");
510 Contract.Assert(count >=0, "[UTF7Encoding.GetByteCount]count >=0");
512 // Just call GetBytes with bytes == null
513 return GetBytes(chars, count, null, 0, baseEncoder);
516 [System.Security.SecurityCritical] // auto-generated
517 internal override unsafe int GetBytes(char* chars, int charCount,
518 byte* bytes, int byteCount, EncoderNLS baseEncoder)
520 Contract.Assert(byteCount >=0, "[UTF7Encoding.GetBytes]byteCount >=0");
521 Contract.Assert(chars!=null, "[UTF7Encoding.GetBytes]chars!=null");
522 Contract.Assert(charCount >=0, "[UTF7Encoding.GetBytes]charCount >=0");
525 UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder;
527 // Default bits & count
531 // prepare our helpers
532 Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(
533 this, encoder, bytes, byteCount, chars, charCount);
538 bitCount = encoder.bitCount;
540 // May have had too many left over
541 while (bitCount >= 6)
544 // If we fail we'll never really have enough room
545 if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F]))
546 ThrowBytesOverflow(encoder, buffer.Count == 0);
550 while (buffer.MoreData)
552 char currentChar = buffer.GetNextChar();
554 if (currentChar < 0x80 && directEncode[currentChar])
560 // Try to add the next byte
561 if (!buffer.AddByte(base64Bytes[bits << 6 - bitCount & 0x3F]))
562 break; // Stop here, didn't throw
567 // Need to get emit '-' and our char, 2 bytes total
568 if (!buffer.AddByte((byte)'-'))
569 break; // Stop here, didn't throw
574 // Need to emit our char
575 if (!buffer.AddByte((byte)currentChar))
576 break; // Stop here, didn't throw
578 else if (bitCount < 0 && currentChar == '+')
580 if (!buffer.AddByte((byte)'+', (byte)'-'))
581 break; // Stop here, didn't throw
587 // Need to emit a + and 12 bits (3 bytes)
588 // Only 12 of the 16 bits will be emitted this time, the other 4 wait 'til next time
589 if (!buffer.AddByte((byte)'+'))
590 break; // Stop here, didn't throw
592 // We're now in bit mode, but haven't stored data yet
597 bits = bits << 16 | currentChar;
600 while (bitCount >= 6)
603 if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F]))
605 bitCount += 6; // We didn't use these bits
606 currentChar = buffer.GetNextChar(); // We're processing this char still, but AddByte
607 // --'d it when we ran out of space
608 break; // Stop here, not enough room for bytes
613 break; // Didn't have room to encode enough bits
617 // Now if we have bits left over we have to encode them.
618 // MustFlush may have been cleared by encoding.ThrowBytesOverflow earlier if converting
619 if (bitCount >= 0 && (encoder == null || encoder.MustFlush))
621 // Do we have bits we have to stick in?
624 if (buffer.AddByte(base64Bytes[(bits << (6 - bitCount)) & 0x3F]))
626 // Emitted spare bits, 0 bits left
631 // If converting and failed bitCount above, then we'll fail this too
632 if (buffer.AddByte((byte)'-'))
634 // turned off bit mode';
639 // If not successful, convert will maintain state for next time, also
640 // AddByte will have decremented our char count, however we need it to remain the same
641 buffer.GetNextChar();
644 // Do we have an encoder we're allowed to use?
645 // bytes == null if counting, so don't use encoder then
646 if (bytes != null && encoder != null)
648 // We already cleared bits & bitcount for mustflush case
650 encoder.bitCount = bitCount;
651 encoder.m_charsUsed = buffer.CharsUsed;
657 [System.Security.SecurityCritical] // auto-generated
658 internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
660 Contract.Assert(count >=0, "[UTF7Encoding.GetCharCount]count >=0");
661 Contract.Assert(bytes!=null, "[UTF7Encoding.GetCharCount]bytes!=null");
663 // Just call GetChars with null char* to do counting
664 return GetChars(bytes, count, null, 0, baseDecoder);
667 [System.Security.SecurityCritical] // auto-generated
668 internal override unsafe int GetChars(byte* bytes, int byteCount,
669 char* chars, int charCount, DecoderNLS baseDecoder)
671 Contract.Assert(byteCount >=0, "[UTF7Encoding.GetChars]byteCount >=0");
672 Contract.Assert(bytes!=null, "[UTF7Encoding.GetChars]bytes!=null");
673 Contract.Assert(charCount >=0, "[UTF7Encoding.GetChars]charCount >=0");
675 // Might use a decoder
676 UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder) baseDecoder;
678 // Get our output buffer info.
679 Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(
680 this, decoder, chars, charCount, bytes, byteCount);
685 bool firstByte = false;
689 bitCount = decoder.bitCount;
690 firstByte = decoder.firstByte;
692 Contract.Assert(firstByte == false || decoder.bitCount <= 0,
693 "[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set");
696 // We may have had bits in the decoder that we couldn't output last time, so do so now
699 // Check our decoder buffer
700 if (!buffer.AddChar((char)((bits >> (bitCount - 16)) & 0xFFFF)))
701 ThrowCharsOverflow(decoder, true); // Always throw, they need at least 1 char even in Convert
703 // Used this one, clean up extra bits
707 // Loop through the input
708 while (buffer.MoreData)
710 byte currentByte = buffer.GetNextByte();
716 // Modified base 64 encoding.
719 if (currentByte < 0x80 && ((v = base64Values[currentByte]) >=0))
722 bits = (bits << 6) | ((byte)v);
726 c = (bits >> (bitCount - 16)) & 0xFFFF;
729 // If not enough bits just continue
734 // If it wasn't a base 64 byte, everything's going to turn off base 64 mode
737 if (currentByte != '-')
739 // >= 0x80 (because of 1st if statemtn)
740 // We need this check since the base64Values[b] check below need b <= 0x7f.
741 // This is not a valid base 64 byte. Terminate the shifted-sequence and
744 // not in base 64 table
745 // According to the RFC 1642 and the example code of UTF-7
746 // in Unicode 2.0, we should just zero-extend the invalid UTF7 byte
748 // Chars won't be updated unless this works, try to fallback
749 if (!buffer.Fallback(currentByte))
750 break; // Stop here, didn't throw
752 // Used that byte, we're done with it
757 // The encoding for '+' is "+-".
759 if (firstByte) c = '+';
760 // We just turn it off if not emitting a +, so we're done.
764 // End of modified base 64 encoding block.
767 else if (currentByte == '+')
770 // Found the start of a modified base 64 encoding block or a plus sign.
779 if (currentByte >= 0x80)
782 if (!buffer.Fallback(currentByte))
783 break; // Stop here, didn't throw
789 // Use the normal character
796 if (!buffer.AddChar((char)c))
798 // No room. If it was a plain char we'll try again later.
799 // Note, we'll consume this byte and stick it in decoder, even if we can't output it
800 if (bitCount >= 0) // Can we rememmber this byte (char)
802 buffer.AdjustBytes(+1); // Need to readd the byte that AddChar subtracted when it failed
803 bitCount += 16; // We'll still need that char we have in our bits
805 break; // didn't throw, stop
810 // Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder)
811 if (chars != null && decoder != null)
813 // MustFlush? (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer)
814 if (decoder.MustFlush)
816 // RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them
818 decoder.bitCount = -1;
819 decoder.firstByte = false;
824 decoder.bitCount = bitCount;
825 decoder.firstByte = firstByte;
827 decoder.m_bytesUsed = buffer.BytesUsed;
829 // else ignore any hanging bits.
836 public override System.Text.Decoder GetDecoder()
838 return new UTF7Encoding.Decoder(this);
842 public override System.Text.Encoder GetEncoder()
844 return new UTF7Encoding.Encoder(this);
848 public override int GetMaxByteCount(int charCount)
851 throw new ArgumentOutOfRangeException("charCount",
852 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
853 Contract.EndContractBlock();
855 // Suppose that every char can not be direct-encoded, we know that
856 // a byte can encode 6 bits of the Unicode character. And we will
857 // also need two extra bytes for the shift-in ('+') and shift-out ('-') mark.
858 // Therefore, the max byte should be:
859 // byteCount = 2 + Math.Ceiling((double)charCount * 16 / 6);
860 // That is always <= 2 + 3 * charCount;
861 // Longest case is alternating encoded, direct, encoded data for 5 + 1 + 5... bytes per char.
862 // UTF7 doesn't have left over surrogates, but if no input we may need an output - to turn off
863 // encoding if MustFlush is true.
865 // Its easiest to think of this as 2 bytes to turn on/off the base64 mode, then 3 bytes per char.
866 // 3 bytes is 18 bits of encoding, which is more than we need, but if its direct encoded then 3
867 // bytes allows us to turn off and then back on base64 mode if necessary.
869 // Note that UTF7 encoded surrogates individually and isn't worried about mismatches, so all
870 // code points are encodable int UTF7.
871 long byteCount = (long)charCount * 3 + 2;
873 // check for overflow
874 if (byteCount > 0x7fffffff)
875 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow"));
877 return (int)byteCount;
881 public override int GetMaxCharCount(int byteCount)
884 throw new ArgumentOutOfRangeException("byteCount",
885 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
886 Contract.EndContractBlock();
888 // Worst case is 1 char per byte. Minimum 1 for left over bits in case decoder is being flushed
889 // Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction.
890 int charCount = byteCount;
891 if (charCount == 0) charCount = 1;
897 // Of all the amazing things... This MUST be Decoder so that our com name
898 // for System.Text.Decoder doesn't change
899 private class Decoder : DecoderNLS, ISerializable
901 /*private*/ internal int bits;
902 /*private*/ internal int bitCount;
903 /*private*/ internal bool firstByte;
905 public Decoder(UTF7Encoding encoding) : base (encoding)
910 // Constructor called by serialization, have to handle deserializing from Everett
911 internal Decoder(SerializationInfo info, StreamingContext context)
914 if (info==null) throw new ArgumentNullException("info");
915 Contract.EndContractBlock();
918 this.bits = (int)info.GetValue("bits", typeof(int));
919 this.bitCount = (int)info.GetValue("bitCount", typeof(int));
920 this.firstByte = (bool)info.GetValue("firstByte", typeof(bool));
921 this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
924 #if FEATURE_SERIALIZATION
925 // ISerializable implementation, get data for this object
926 [System.Security.SecurityCritical] // auto-generated_required
927 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
930 if (info==null) throw new ArgumentNullException("info");
931 Contract.EndContractBlock();
934 info.AddValue("encoding", this.m_encoding);
935 info.AddValue("bits", this.bits);
936 info.AddValue("bitCount", this.bitCount);
937 info.AddValue("firstByte", this.firstByte);
941 public override void Reset()
945 this.firstByte = false;
946 if (m_fallbackBuffer != null)
947 m_fallbackBuffer.Reset();
950 // Anything left in our encoder?
951 internal override bool HasState
955 // NOTE: This forces the last -, which some encoder might not encode. If we
956 // don't see it we don't think we're done reading.
957 return (this.bitCount != -1);
963 // Of all the amazing things... This MUST be Encoder so that our com name
964 // for System.Text.Encoder doesn't change
965 private class Encoder : EncoderNLS, ISerializable
967 /*private*/ internal int bits;
968 /*private*/ internal int bitCount;
970 public Encoder(UTF7Encoding encoding) : base(encoding)
975 // Constructor called by serialization, have to handle deserializing from Everett
976 internal Encoder(SerializationInfo info, StreamingContext context)
979 if (info==null) throw new ArgumentNullException("info");
980 Contract.EndContractBlock();
983 this.bits = (int)info.GetValue("bits", typeof(int));
984 this.bitCount = (int)info.GetValue("bitCount", typeof(int));
985 this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
988 #if FEATURE_SERIALIZATION
989 // ISerializable implementation, get data for this object
990 [System.Security.SecurityCritical] // auto-generated_required
991 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
994 if (info==null) throw new ArgumentNullException("info");
995 Contract.EndContractBlock();
998 info.AddValue("encoding", this.m_encoding);
999 info.AddValue("bits", this.bits);
1000 info.AddValue("bitCount", this.bitCount);
1004 public override void Reset()
1008 if (m_fallbackBuffer != null)
1009 m_fallbackBuffer.Reset();
1012 // Anything left in our encoder?
1013 internal override bool HasState
1017 return (this.bits != 0 || this.bitCount != -1);
1022 // Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char
1023 // and turn off base64 mode if it was in that mode. We still exit the mode, but now we fallback.
1025 internal sealed class DecoderUTF7Fallback : DecoderFallback
1027 // Construction. Default replacement fallback uses no best fit and ? replacement string
1028 public DecoderUTF7Fallback()
1032 public override DecoderFallbackBuffer CreateFallbackBuffer()
1034 return new DecoderUTF7FallbackBuffer(this);
1037 // Maximum number of characters that this instance of this fallback could return
1038 public override int MaxCharCount
1042 // returns 1 char per bad byte
1047 public override bool Equals(Object value)
1049 DecoderUTF7Fallback that = value as DecoderUTF7Fallback;
1057 public override int GetHashCode()
1063 internal sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer
1065 // Store our default string
1066 char cFallback = (char)0;
1071 public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback)
1076 public override bool Fallback(byte[] bytesUnknown, int index)
1078 // We expect no previous fallback in our buffer
1079 Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.Fallback] Can't have recursive fallbacks");
1080 Contract.Assert(bytesUnknown.Length == 1, "[DecoderUTF7FallbackBuffer.Fallback] Only possible fallback case should be 1 unknown byte");
1082 // Go ahead and get our fallback
1083 cFallback = (char)bytesUnknown[0];
1085 // Any of the fallback characters can be handled except for 0
1096 public override char GetNextChar()
1101 // Note: this means that 0 in UTF7 stream will never be emitted.
1105 public override bool MovePrevious()
1112 // return true if we were allowed to do this
1113 return (iCount >= 0 && iCount <= iSize);
1116 // Return # of chars left in this fallback
1117 public override int Remaining
1121 return (iCount > 0) ? iCount : 0;
1126 [System.Security.SecuritySafeCritical] // overrides public transparent member
1127 public override unsafe void Reset()
1133 // This version just counts the fallback and doesn't actually copy anything.
1134 [System.Security.SecurityCritical] // auto-generated
1135 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
1136 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
1137 // array, and we might need the index, hence the byte*
1139 // We expect no previous fallback in our buffer
1140 Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.InternalFallback] Can't have recursive fallbacks");
1141 if (bytes.Length != 1)
1143 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
1146 // Can't fallback a byte 0, so return for that case, 1 otherwise.
1147 return bytes[0] == 0 ? 0 : 1;