The Labs \ Source Viewer \ SSCLI \ System.Text \ DecoderUTF7FallbackBuffer

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

Developer Fusion