1154 lines
47 KiB
C#
1154 lines
47 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
//
|
|
// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
|
|
//
|
|
|
|
namespace System.Text
|
|
{
|
|
using System;
|
|
using System.Runtime.Serialization;
|
|
using System.Security.Permissions;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
|
|
[Serializable]
|
|
[System.Runtime.InteropServices.ComVisible(true)]
|
|
public class UTF7Encoding : Encoding
|
|
{
|
|
private const String base64Chars =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
// 0123456789111111111122222222223333333333444444444455555555556666
|
|
// 012345678901234567890123456789012345678901234567890123
|
|
|
|
// These are the characters that can be directly encoded in UTF7.
|
|
private const String directChars =
|
|
"\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
|
|
// These are the characters that can be optionally directly encoded in UTF7.
|
|
private const String optionalChars =
|
|
"!\"#$%&*;<=>@[]^_`{|}";
|
|
|
|
// The set of base 64 characters.
|
|
private byte[] base64Bytes;
|
|
// The decoded bits for every base64 values. This array has a size of 128 elements.
|
|
// The index is the code point value of the base 64 characters. The value is -1 if
|
|
// the code point is not a valid base 64 character. Otherwise, the value is a value
|
|
// from 0 ~ 63.
|
|
private sbyte[] base64Values;
|
|
// The array to decide if a Unicode code point below 0x80 can be directly encoded in UTF7.
|
|
// This array has a size of 128.
|
|
private bool[] directEncode;
|
|
|
|
[OptionalField(VersionAdded = 2)]
|
|
private bool m_allowOptionals;
|
|
|
|
private const int UTF7_CODEPAGE=65000;
|
|
|
|
|
|
public UTF7Encoding()
|
|
: this(false)
|
|
{
|
|
}
|
|
|
|
public UTF7Encoding(bool allowOptionals)
|
|
: base(UTF7_CODEPAGE) //Set the data item.
|
|
{
|
|
// Allowing optionals?
|
|
this.m_allowOptionals = allowOptionals;
|
|
|
|
// Make our tables
|
|
MakeTables();
|
|
}
|
|
|
|
private void MakeTables()
|
|
{
|
|
// Build our tables
|
|
base64Bytes = new byte[64];
|
|
for (int i = 0; i < 64; i++) base64Bytes[i] = (byte)base64Chars[i];
|
|
base64Values = new sbyte[128];
|
|
for (int i = 0; i < 128; i++) base64Values[i] = -1;
|
|
for (int i = 0; i < 64; i++) base64Values[base64Bytes[i]] = (sbyte)i;
|
|
directEncode = new bool[128];
|
|
int count = directChars.Length;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
directEncode[directChars[i]] = true;
|
|
}
|
|
|
|
if (this.m_allowOptionals)
|
|
{
|
|
count = optionalChars.Length;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
directEncode[optionalChars[i]] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7.
|
|
internal override void SetDefaultFallbacks()
|
|
{
|
|
// UTF7 had an odd decoderFallback behavior, and the Encoder fallback
|
|
// is irrelevent because we encode surrogates individually and never check for unmatched ones
|
|
// (so nothing can fallback during encoding)
|
|
this.encoderFallback = new EncoderReplacementFallback(String.Empty);
|
|
this.decoderFallback = new DecoderUTF7Fallback();
|
|
}
|
|
|
|
|
|
#region Serialization
|
|
[OnDeserializing]
|
|
private void OnDeserializing(StreamingContext ctx)
|
|
{
|
|
// make sure the optional fields initialized correctly.
|
|
base.OnDeserializing();
|
|
}
|
|
|
|
[OnDeserialized]
|
|
private void OnDeserialized(StreamingContext ctx)
|
|
{
|
|
base.OnDeserialized();
|
|
|
|
if (m_deserializedFromEverett)
|
|
{
|
|
// If 1st optional char is encoded we're allowing optionals
|
|
m_allowOptionals = directEncode[optionalChars[0]];
|
|
}
|
|
|
|
MakeTables();
|
|
}
|
|
#endregion Serialization
|
|
|
|
|
|
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override bool Equals(Object value)
|
|
{
|
|
UTF7Encoding that = value as UTF7Encoding;
|
|
if (that != null)
|
|
{
|
|
return (m_allowOptionals == that.m_allowOptionals) &&
|
|
(EncoderFallback.Equals(that.EncoderFallback)) &&
|
|
(DecoderFallback.Equals(that.DecoderFallback));
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
// Compared to all the other encodings, variations of UTF7 are unlikely
|
|
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override int GetHashCode()
|
|
{
|
|
return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode();
|
|
}
|
|
|
|
//
|
|
// The following methods are copied from EncodingNLS.cs.
|
|
// Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here.
|
|
// These should be kept in [....] for the following classes:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
//
|
|
|
|
// Returns the number of bytes required to encode a range of characters in
|
|
// a character array.
|
|
//
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
public override unsafe int GetByteCount(char[] chars, int index, int count)
|
|
{
|
|
// Validate input parameters
|
|
if (chars == null)
|
|
throw new ArgumentNullException("chars",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (index < 0 || count < 0)
|
|
throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if (chars.Length - index < count)
|
|
throw new ArgumentOutOfRangeException("chars",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
|
|
Contract.EndContractBlock();
|
|
|
|
// If no input, return 0, avoid fixed empty array problem
|
|
if (chars.Length == 0)
|
|
return 0;
|
|
|
|
// Just call the pointer version
|
|
fixed (char* pChars = chars)
|
|
return GetByteCount(pChars + index, count, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe int GetByteCount(String s)
|
|
{
|
|
// Validate input
|
|
if (s==null)
|
|
throw new ArgumentNullException("s");
|
|
Contract.EndContractBlock();
|
|
|
|
fixed (char* pChars = s)
|
|
return GetByteCount(pChars, s.Length, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[CLSCompliant(false)]
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe int GetByteCount(char* chars, int count)
|
|
{
|
|
// Validate Parameters
|
|
if (chars == null)
|
|
throw new ArgumentNullException("chars",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException("count",
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
// Call it with empty encoder
|
|
return GetByteCount(chars, count, null);
|
|
}
|
|
|
|
// Parent method is safe.
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe int GetBytes(String s, int charIndex, int charCount,
|
|
byte[] bytes, int byteIndex)
|
|
{
|
|
if (s == null || bytes == null)
|
|
throw new ArgumentNullException((s == null ? "s" : "bytes"),
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (charIndex < 0 || charCount < 0)
|
|
throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if (s.Length - charIndex < charCount)
|
|
throw new ArgumentOutOfRangeException("s",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCount"));
|
|
|
|
if (byteIndex < 0 || byteIndex > bytes.Length)
|
|
throw new ArgumentOutOfRangeException("byteIndex",
|
|
Environment.GetResourceString("ArgumentOutOfRange_Index"));
|
|
Contract.EndContractBlock();
|
|
|
|
int byteCount = bytes.Length - byteIndex;
|
|
|
|
// Fixed doesn't like empty arrays
|
|
if (bytes.Length == 0)
|
|
bytes = new byte[1];
|
|
|
|
fixed (char* pChars = s)
|
|
fixed ( byte* pBytes = bytes)
|
|
return GetBytes(pChars + charIndex, charCount,
|
|
pBytes + byteIndex, byteCount, null);
|
|
}
|
|
|
|
// Encodes a range of characters in a character array into a range of bytes
|
|
// in a byte array. An exception occurs if the byte array is not large
|
|
// enough to hold the complete encoding of the characters. The
|
|
// GetByteCount method can be used to determine the exact number of
|
|
// bytes that will be produced for a given range of characters.
|
|
// Alternatively, the GetMaxByteCount method can be used to
|
|
// determine the maximum number of bytes that will be produced for a given
|
|
// number of characters, regardless of the actual character values.
|
|
//
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
|
|
byte[] bytes, int byteIndex)
|
|
{
|
|
// Validate parameters
|
|
if (chars == null || bytes == null)
|
|
throw new ArgumentNullException((chars == null ? "chars" : "bytes"),
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (charIndex < 0 || charCount < 0)
|
|
throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if (chars.Length - charIndex < charCount)
|
|
throw new ArgumentOutOfRangeException("chars",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
|
|
|
|
if (byteIndex < 0 || byteIndex > bytes.Length)
|
|
throw new ArgumentOutOfRangeException("byteIndex",
|
|
Environment.GetResourceString("ArgumentOutOfRange_Index"));
|
|
Contract.EndContractBlock();
|
|
|
|
// If nothing to encode return 0, avoid fixed problem
|
|
if (chars.Length == 0)
|
|
return 0;
|
|
|
|
// Just call pointer version
|
|
int byteCount = bytes.Length - byteIndex;
|
|
|
|
// Fixed doesn't like empty arrays
|
|
if (bytes.Length == 0)
|
|
bytes = new byte[1];
|
|
|
|
fixed (char* pChars = chars)
|
|
fixed (byte* pBytes = bytes)
|
|
// Remember that byteCount is # to decode, not size of array.
|
|
return GetBytes(pChars + charIndex, charCount,
|
|
pBytes + byteIndex, byteCount, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[CLSCompliant(false)]
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null || chars == null)
|
|
throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (charCount < 0 || byteCount < 0)
|
|
throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
return GetBytes(chars, charCount, bytes, byteCount, null);
|
|
}
|
|
|
|
// Returns the number of characters produced by decoding a range of bytes
|
|
// in a byte array.
|
|
//
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
public override unsafe int GetCharCount(byte[] bytes, int index, int count)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null)
|
|
throw new ArgumentNullException("bytes",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (index < 0 || count < 0)
|
|
throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if (bytes.Length - index < count)
|
|
throw new ArgumentOutOfRangeException("bytes",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
|
|
Contract.EndContractBlock();
|
|
|
|
// If no input just return 0, fixed doesn't like 0 length arrays.
|
|
if (bytes.Length == 0)
|
|
return 0;
|
|
|
|
// Just call pointer version
|
|
fixed (byte* pBytes = bytes)
|
|
return GetCharCount(pBytes + index, count, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[CLSCompliant(false)]
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe int GetCharCount(byte* bytes, int count)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null)
|
|
throw new ArgumentNullException("bytes",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException("count",
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
return GetCharCount(bytes, count, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
|
|
char[] chars, int charIndex)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null || chars == null)
|
|
throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (byteIndex < 0 || byteCount < 0)
|
|
throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if ( bytes.Length - byteIndex < byteCount)
|
|
throw new ArgumentOutOfRangeException("bytes",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
|
|
|
|
if (charIndex < 0 || charIndex > chars.Length)
|
|
throw new ArgumentOutOfRangeException("charIndex",
|
|
Environment.GetResourceString("ArgumentOutOfRange_Index"));
|
|
Contract.EndContractBlock();
|
|
|
|
// If no input, return 0 & avoid fixed problem
|
|
if (bytes.Length == 0)
|
|
return 0;
|
|
|
|
// Just call pointer version
|
|
int charCount = chars.Length - charIndex;
|
|
|
|
// Fixed doesn't like empty arrays
|
|
if (chars.Length == 0)
|
|
chars = new char[1];
|
|
|
|
fixed (byte* pBytes = bytes)
|
|
fixed (char* pChars = chars)
|
|
// Remember that charCount is # to decode, not size of array
|
|
return GetChars(pBytes + byteIndex, byteCount,
|
|
pChars + charIndex, charCount, null);
|
|
}
|
|
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[CLSCompliant(false)]
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null || chars == null)
|
|
throw new ArgumentNullException(bytes == null ? "bytes" : "chars",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (charCount < 0 || byteCount < 0)
|
|
throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
return GetChars(bytes, byteCount, chars, charCount, null);
|
|
}
|
|
|
|
// Returns a string containing the decoded representation of a range of
|
|
// bytes in a byte array.
|
|
//
|
|
// All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
|
|
// So if you fix this, fix the others. Currently those include:
|
|
// EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
|
|
// parent method is safe
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
public override unsafe String GetString(byte[] bytes, int index, int count)
|
|
{
|
|
// Validate Parameters
|
|
if (bytes == null)
|
|
throw new ArgumentNullException("bytes",
|
|
Environment.GetResourceString("ArgumentNull_Array"));
|
|
|
|
if (index < 0 || count < 0)
|
|
throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"),
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
|
|
if (bytes.Length - index < count)
|
|
throw new ArgumentOutOfRangeException("bytes",
|
|
Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
|
|
Contract.EndContractBlock();
|
|
|
|
// Avoid problems with empty input buffer
|
|
if (bytes.Length == 0) return String.Empty;
|
|
|
|
fixed (byte* pBytes = bytes)
|
|
return String.CreateStringFromEncoding(
|
|
pBytes + index, count, this);
|
|
}
|
|
|
|
//
|
|
// End of standard methods copied from EncodingNLS.cs
|
|
//
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
|
|
{
|
|
Contract.Assert(chars!=null, "[UTF7Encoding.GetByteCount]chars!=null");
|
|
Contract.Assert(count >=0, "[UTF7Encoding.GetByteCount]count >=0");
|
|
|
|
// Just call GetBytes with bytes == null
|
|
return GetBytes(chars, count, null, 0, baseEncoder);
|
|
}
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
internal override unsafe int GetBytes(char* chars, int charCount,
|
|
byte* bytes, int byteCount, EncoderNLS baseEncoder)
|
|
{
|
|
Contract.Assert(byteCount >=0, "[UTF7Encoding.GetBytes]byteCount >=0");
|
|
Contract.Assert(chars!=null, "[UTF7Encoding.GetBytes]chars!=null");
|
|
Contract.Assert(charCount >=0, "[UTF7Encoding.GetBytes]charCount >=0");
|
|
|
|
// Get encoder info
|
|
UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder;
|
|
|
|
// Default bits & count
|
|
int bits = 0;
|
|
int bitCount = -1;
|
|
|
|
// prepare our helpers
|
|
Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(
|
|
this, encoder, bytes, byteCount, chars, charCount);
|
|
|
|
if (encoder != null)
|
|
{
|
|
bits = encoder.bits;
|
|
bitCount = encoder.bitCount;
|
|
|
|
// May have had too many left over
|
|
while (bitCount >= 6)
|
|
{
|
|
bitCount -= 6;
|
|
// If we fail we'll never really have enough room
|
|
if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F]))
|
|
ThrowBytesOverflow(encoder, buffer.Count == 0);
|
|
}
|
|
}
|
|
|
|
while (buffer.MoreData)
|
|
{
|
|
char currentChar = buffer.GetNextChar();
|
|
|
|
if (currentChar < 0x80 && directEncode[currentChar])
|
|
{
|
|
if (bitCount >= 0)
|
|
{
|
|
if (bitCount > 0)
|
|
{
|
|
// Try to add the next byte
|
|
if (!buffer.AddByte(base64Bytes[bits << 6 - bitCount & 0x3F]))
|
|
break; // Stop here, didn't throw
|
|
|
|
bitCount = 0;
|
|
}
|
|
|
|
// Need to get emit '-' and our char, 2 bytes total
|
|
if (!buffer.AddByte((byte)'-'))
|
|
break; // Stop here, didn't throw
|
|
|
|
bitCount = -1;
|
|
}
|
|
|
|
// Need to emit our char
|
|
if (!buffer.AddByte((byte)currentChar))
|
|
break; // Stop here, didn't throw
|
|
}
|
|
else if (bitCount < 0 && currentChar == '+')
|
|
{
|
|
if (!buffer.AddByte((byte)'+', (byte)'-'))
|
|
break; // Stop here, didn't throw
|
|
}
|
|
else
|
|
{
|
|
if (bitCount < 0)
|
|
{
|
|
// Need to emit a + and 12 bits (3 bytes)
|
|
// Only 12 of the 16 bits will be emitted this time, the other 4 wait 'til next time
|
|
if (!buffer.AddByte((byte)'+'))
|
|
break; // Stop here, didn't throw
|
|
|
|
// We're now in bit mode, but haven't stored data yet
|
|
bitCount = 0;
|
|
}
|
|
|
|
// Add our bits
|
|
bits = bits << 16 | currentChar;
|
|
bitCount += 16;
|
|
|
|
while (bitCount >= 6)
|
|
{
|
|
bitCount -= 6;
|
|
if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F]))
|
|
{
|
|
bitCount += 6; // We didn't use these bits
|
|
currentChar = buffer.GetNextChar(); // We're processing this char still, but AddByte
|
|
// --'d it when we ran out of space
|
|
break; // Stop here, not enough room for bytes
|
|
}
|
|
}
|
|
|
|
if (bitCount >= 6)
|
|
break; // Didn't have room to encode enough bits
|
|
}
|
|
}
|
|
|
|
// Now if we have bits left over we have to encode them.
|
|
// MustFlush may have been cleared by encoding.ThrowBytesOverflow earlier if converting
|
|
if (bitCount >= 0 && (encoder == null || encoder.MustFlush))
|
|
{
|
|
// Do we have bits we have to stick in?
|
|
if (bitCount > 0)
|
|
{
|
|
if (buffer.AddByte(base64Bytes[(bits << (6 - bitCount)) & 0x3F]))
|
|
{
|
|
// Emitted spare bits, 0 bits left
|
|
bitCount = 0;
|
|
}
|
|
}
|
|
|
|
// If converting and failed bitCount above, then we'll fail this too
|
|
if (buffer.AddByte((byte)'-'))
|
|
{
|
|
// turned off bit mode';
|
|
bits = 0;
|
|
bitCount = -1;
|
|
}
|
|
else
|
|
// If not successful, convert will maintain state for next time, also
|
|
// AddByte will have decremented our char count, however we need it to remain the same
|
|
buffer.GetNextChar();
|
|
}
|
|
|
|
// Do we have an encoder we're allowed to use?
|
|
// bytes == null if counting, so don't use encoder then
|
|
if (bytes != null && encoder != null)
|
|
{
|
|
// We already cleared bits & bitcount for mustflush case
|
|
encoder.bits = bits;
|
|
encoder.bitCount = bitCount;
|
|
encoder.m_charsUsed = buffer.CharsUsed;
|
|
}
|
|
|
|
return buffer.Count;
|
|
}
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
|
|
{
|
|
Contract.Assert(count >=0, "[UTF7Encoding.GetCharCount]count >=0");
|
|
Contract.Assert(bytes!=null, "[UTF7Encoding.GetCharCount]bytes!=null");
|
|
|
|
// Just call GetChars with null char* to do counting
|
|
return GetChars(bytes, count, null, 0, baseDecoder);
|
|
}
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
internal override unsafe int GetChars(byte* bytes, int byteCount,
|
|
char* chars, int charCount, DecoderNLS baseDecoder)
|
|
{
|
|
Contract.Assert(byteCount >=0, "[UTF7Encoding.GetChars]byteCount >=0");
|
|
Contract.Assert(bytes!=null, "[UTF7Encoding.GetChars]bytes!=null");
|
|
Contract.Assert(charCount >=0, "[UTF7Encoding.GetChars]charCount >=0");
|
|
|
|
// Might use a decoder
|
|
UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder) baseDecoder;
|
|
|
|
// Get our output buffer info.
|
|
Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(
|
|
this, decoder, chars, charCount, bytes, byteCount);
|
|
|
|
// Get decoder info
|
|
int bits = 0;
|
|
int bitCount = -1;
|
|
bool firstByte = false;
|
|
if (decoder != null)
|
|
{
|
|
bits = decoder.bits;
|
|
bitCount = decoder.bitCount;
|
|
firstByte = decoder.firstByte;
|
|
|
|
Contract.Assert(firstByte == false || decoder.bitCount <= 0,
|
|
"[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set");
|
|
}
|
|
|
|
// We may have had bits in the decoder that we couldn't output last time, so do so now
|
|
if (bitCount >= 16)
|
|
{
|
|
// Check our decoder buffer
|
|
if (!buffer.AddChar((char)((bits >> (bitCount - 16)) & 0xFFFF)))
|
|
ThrowCharsOverflow(decoder, true); // Always throw, they need at least 1 char even in Convert
|
|
|
|
// Used this one, clean up extra bits
|
|
bitCount -= 16;
|
|
}
|
|
|
|
// Loop through the input
|
|
while (buffer.MoreData)
|
|
{
|
|
byte currentByte = buffer.GetNextByte();
|
|
int c;
|
|
|
|
if (bitCount >= 0)
|
|
{
|
|
//
|
|
// Modified base 64 encoding.
|
|
//
|
|
sbyte v;
|
|
if (currentByte < 0x80 && ((v = base64Values[currentByte]) >=0))
|
|
{
|
|
firstByte = false;
|
|
bits = (bits << 6) | ((byte)v);
|
|
bitCount += 6;
|
|
if (bitCount >= 16)
|
|
{
|
|
c = (bits >> (bitCount - 16)) & 0xFFFF;
|
|
bitCount -= 16;
|
|
}
|
|
// If not enough bits just continue
|
|
else continue;
|
|
}
|
|
else
|
|
{
|
|
// If it wasn't a base 64 byte, everything's going to turn off base 64 mode
|
|
bitCount = -1;
|
|
|
|
if (currentByte != '-')
|
|
{
|
|
// >= 0x80 (because of 1st if statemtn)
|
|
// We need this check since the base64Values[b] check below need b <= 0x7f.
|
|
// This is not a valid base 64 byte. Terminate the shifted-sequence and
|
|
// emit this byte.
|
|
|
|
// not in base 64 table
|
|
// According to the RFC 1642 and the example code of UTF-7
|
|
// in Unicode 2.0, we should just zero-extend the invalid UTF7 byte
|
|
|
|
// Chars won't be updated unless this works, try to fallback
|
|
if (!buffer.Fallback(currentByte))
|
|
break; // Stop here, didn't throw
|
|
|
|
// Used that byte, we're done with it
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The encoding for '+' is "+-".
|
|
//
|
|
if (firstByte) c = '+';
|
|
// We just turn it off if not emitting a +, so we're done.
|
|
else continue;
|
|
}
|
|
//
|
|
// End of modified base 64 encoding block.
|
|
//
|
|
}
|
|
else if (currentByte == '+')
|
|
{
|
|
//
|
|
// Found the start of a modified base 64 encoding block or a plus sign.
|
|
//
|
|
bitCount = 0;
|
|
firstByte = true;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Normal character
|
|
if (currentByte >= 0x80)
|
|
{
|
|
// Try to fallback
|
|
if (!buffer.Fallback(currentByte))
|
|
break; // Stop here, didn't throw
|
|
|
|
// Done falling back
|
|
continue;
|
|
}
|
|
|
|
// Use the normal character
|
|
c = currentByte;
|
|
}
|
|
|
|
if (c >= 0)
|
|
{
|
|
// Check our buffer
|
|
if (!buffer.AddChar((char)c))
|
|
{
|
|
// No room. If it was a plain char we'll try again later.
|
|
// Note, we'll consume this byte and stick it in decoder, even if we can't output it
|
|
if (bitCount >= 0) // Can we rememmber this byte (char)
|
|
{
|
|
buffer.AdjustBytes(+1); // Need to readd the byte that AddChar subtracted when it failed
|
|
bitCount += 16; // We'll still need that char we have in our bits
|
|
}
|
|
break; // didn't throw, stop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder)
|
|
if (chars != null && decoder != null)
|
|
{
|
|
// MustFlush? (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer)
|
|
if (decoder.MustFlush)
|
|
{
|
|
// RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them
|
|
decoder.bits = 0;
|
|
decoder.bitCount = -1;
|
|
decoder.firstByte = false;
|
|
}
|
|
else
|
|
{
|
|
decoder.bits = bits;
|
|
decoder.bitCount = bitCount;
|
|
decoder.firstByte = firstByte;
|
|
}
|
|
decoder.m_bytesUsed = buffer.BytesUsed;
|
|
}
|
|
// else ignore any hanging bits.
|
|
|
|
// Return our count
|
|
return buffer.Count;
|
|
}
|
|
|
|
|
|
public override System.Text.Decoder GetDecoder()
|
|
{
|
|
return new UTF7Encoding.Decoder(this);
|
|
}
|
|
|
|
|
|
public override System.Text.Encoder GetEncoder()
|
|
{
|
|
return new UTF7Encoding.Encoder(this);
|
|
}
|
|
|
|
|
|
public override int GetMaxByteCount(int charCount)
|
|
{
|
|
if (charCount < 0)
|
|
throw new ArgumentOutOfRangeException("charCount",
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
// Suppose that every char can not be direct-encoded, we know that
|
|
// a byte can encode 6 bits of the Unicode character. And we will
|
|
// also need two extra bytes for the shift-in ('+') and shift-out ('-') mark.
|
|
// Therefore, the max byte should be:
|
|
// byteCount = 2 + Math.Ceiling((double)charCount * 16 / 6);
|
|
// That is always <= 2 + 3 * charCount;
|
|
// Longest case is alternating encoded, direct, encoded data for 5 + 1 + 5... bytes per char.
|
|
// UTF7 doesn't have left over surrogates, but if no input we may need an output - to turn off
|
|
// encoding if MustFlush is true.
|
|
|
|
// Its easiest to think of this as 2 bytes to turn on/off the base64 mode, then 3 bytes per char.
|
|
// 3 bytes is 18 bits of encoding, which is more than we need, but if its direct encoded then 3
|
|
// bytes allows us to turn off and then back on base64 mode if necessary.
|
|
|
|
// Note that UTF7 encoded surrogates individually and isn't worried about mismatches, so all
|
|
// code points are encodable int UTF7.
|
|
long byteCount = (long)charCount * 3 + 2;
|
|
|
|
// check for overflow
|
|
if (byteCount > 0x7fffffff)
|
|
throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow"));
|
|
|
|
return (int)byteCount;
|
|
}
|
|
|
|
|
|
public override int GetMaxCharCount(int byteCount)
|
|
{
|
|
if (byteCount < 0)
|
|
throw new ArgumentOutOfRangeException("byteCount",
|
|
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
|
|
Contract.EndContractBlock();
|
|
|
|
// Worst case is 1 char per byte. Minimum 1 for left over bits in case decoder is being flushed
|
|
// Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction.
|
|
int charCount = byteCount;
|
|
if (charCount == 0) charCount = 1;
|
|
|
|
return charCount;
|
|
}
|
|
|
|
[Serializable]
|
|
// Of all the amazing things... This MUST be Decoder so that our com name
|
|
// for System.Text.Decoder doesn't change
|
|
private class Decoder : DecoderNLS, ISerializable
|
|
{
|
|
/*private*/ internal int bits;
|
|
/*private*/ internal int bitCount;
|
|
/*private*/ internal bool firstByte;
|
|
|
|
public Decoder(UTF7Encoding encoding) : base (encoding)
|
|
{
|
|
// base calls reset
|
|
}
|
|
|
|
// Constructor called by serialization, have to handle deserializing from Everett
|
|
internal Decoder(SerializationInfo info, StreamingContext context)
|
|
{
|
|
// Any info?
|
|
if (info==null) throw new ArgumentNullException("info");
|
|
Contract.EndContractBlock();
|
|
|
|
// Get common info
|
|
this.bits = (int)info.GetValue("bits", typeof(int));
|
|
this.bitCount = (int)info.GetValue("bitCount", typeof(int));
|
|
this.firstByte = (bool)info.GetValue("firstByte", typeof(bool));
|
|
this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
|
|
}
|
|
|
|
#if FEATURE_SERIALIZATION
|
|
// ISerializable implementation, get data for this object
|
|
[System.Security.SecurityCritical] // auto-generated_required
|
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
// Any info?
|
|
if (info==null) throw new ArgumentNullException("info");
|
|
Contract.EndContractBlock();
|
|
|
|
// Save Whidbey data
|
|
info.AddValue("encoding", this.m_encoding);
|
|
info.AddValue("bits", this.bits);
|
|
info.AddValue("bitCount", this.bitCount);
|
|
info.AddValue("firstByte", this.firstByte);
|
|
}
|
|
#endif
|
|
|
|
public override void Reset()
|
|
{
|
|
this.bits = 0;
|
|
this.bitCount = -1;
|
|
this.firstByte = false;
|
|
if (m_fallbackBuffer != null)
|
|
m_fallbackBuffer.Reset();
|
|
}
|
|
|
|
// Anything left in our encoder?
|
|
internal override bool HasState
|
|
{
|
|
get
|
|
{
|
|
// NOTE: This forces the last -, which some encoder might not encode. If we
|
|
// don't see it we don't think we're done reading.
|
|
return (this.bitCount != -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
// Of all the amazing things... This MUST be Encoder so that our com name
|
|
// for System.Text.Encoder doesn't change
|
|
private class Encoder : EncoderNLS, ISerializable
|
|
{
|
|
/*private*/ internal int bits;
|
|
/*private*/ internal int bitCount;
|
|
|
|
public Encoder(UTF7Encoding encoding) : base(encoding)
|
|
{
|
|
// base calls reset
|
|
}
|
|
|
|
// Constructor called by serialization, have to handle deserializing from Everett
|
|
internal Encoder(SerializationInfo info, StreamingContext context)
|
|
{
|
|
// Any info?
|
|
if (info==null) throw new ArgumentNullException("info");
|
|
Contract.EndContractBlock();
|
|
|
|
// Get common info
|
|
this.bits = (int)info.GetValue("bits", typeof(int));
|
|
this.bitCount = (int)info.GetValue("bitCount", typeof(int));
|
|
this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
|
|
}
|
|
|
|
#if FEATURE_SERIALIZATION
|
|
// ISerializable implementation, get data for this object
|
|
[System.Security.SecurityCritical] // auto-generated_required
|
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
// Any info?
|
|
if (info==null) throw new ArgumentNullException("info");
|
|
Contract.EndContractBlock();
|
|
|
|
// Save Whidbey data
|
|
info.AddValue("encoding", this.m_encoding);
|
|
info.AddValue("bits", this.bits);
|
|
info.AddValue("bitCount", this.bitCount);
|
|
}
|
|
#endif
|
|
|
|
public override void Reset()
|
|
{
|
|
this.bitCount = -1;
|
|
this.bits = 0;
|
|
if (m_fallbackBuffer != null)
|
|
m_fallbackBuffer.Reset();
|
|
}
|
|
|
|
// Anything left in our encoder?
|
|
internal override bool HasState
|
|
{
|
|
get
|
|
{
|
|
return (this.bits != 0 || this.bitCount != -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char
|
|
// and turn off base64 mode if it was in that mode. We still exit the mode, but now we fallback.
|
|
[Serializable]
|
|
internal sealed class DecoderUTF7Fallback : DecoderFallback
|
|
{
|
|
// Construction. Default replacement fallback uses no best fit and ? replacement string
|
|
public DecoderUTF7Fallback()
|
|
{
|
|
}
|
|
|
|
public override DecoderFallbackBuffer CreateFallbackBuffer()
|
|
{
|
|
return new DecoderUTF7FallbackBuffer(this);
|
|
}
|
|
|
|
// Maximum number of characters that this instance of this fallback could return
|
|
public override int MaxCharCount
|
|
{
|
|
get
|
|
{
|
|
// returns 1 char per bad byte
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
public override bool Equals(Object value)
|
|
{
|
|
DecoderUTF7Fallback that = value as DecoderUTF7Fallback;
|
|
if (that != null)
|
|
{
|
|
return true;
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return 984;
|
|
}
|
|
}
|
|
|
|
internal sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer
|
|
{
|
|
// Store our default string
|
|
char cFallback = (char)0;
|
|
int iCount = -1;
|
|
int iSize;
|
|
|
|
// Construction
|
|
public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback)
|
|
{
|
|
}
|
|
|
|
// Fallback Methods
|
|
public override bool Fallback(byte[] bytesUnknown, int index)
|
|
{
|
|
// We expect no previous fallback in our buffer
|
|
Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.Fallback] Can't have recursive fallbacks");
|
|
Contract.Assert(bytesUnknown.Length == 1, "[DecoderUTF7FallbackBuffer.Fallback] Only possible fallback case should be 1 unknown byte");
|
|
|
|
// Go ahead and get our fallback
|
|
cFallback = (char)bytesUnknown[0];
|
|
|
|
// Any of the fallback characters can be handled except for 0
|
|
if (cFallback == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
iCount = iSize = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
public override char GetNextChar()
|
|
{
|
|
if (iCount-- > 0)
|
|
return cFallback;
|
|
|
|
// Note: this means that 0 in UTF7 stream will never be emitted.
|
|
return (char)0;
|
|
}
|
|
|
|
public override bool MovePrevious()
|
|
{
|
|
if (iCount >= 0)
|
|
{
|
|
iCount++;
|
|
}
|
|
|
|
// return true if we were allowed to do this
|
|
return (iCount >= 0 && iCount <= iSize);
|
|
}
|
|
|
|
// Return # of chars left in this fallback
|
|
public override int Remaining
|
|
{
|
|
get
|
|
{
|
|
return (iCount > 0) ? iCount : 0;
|
|
}
|
|
}
|
|
|
|
// Clear the buffer
|
|
[System.Security.SecuritySafeCritical] // overrides public transparent member
|
|
public override unsafe void Reset()
|
|
{
|
|
iCount = -1;
|
|
byteStart = null;
|
|
}
|
|
|
|
// This version just counts the fallback and doesn't actually copy anything.
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
|
|
// Right now this has both bytes and bytes[], since we might have extra bytes, hence the
|
|
// array, and we might need the index, hence the byte*
|
|
{
|
|
// We expect no previous fallback in our buffer
|
|
Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.InternalFallback] Can't have recursive fallbacks");
|
|
if (bytes.Length != 1)
|
|
{
|
|
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
|
|
}
|
|
|
|
// Can't fallback a byte 0, so return for that case, 1 otherwise.
|
|
return bytes[0] == 0 ? 0 : 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|