e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
978 lines
35 KiB
C#
978 lines
35 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.Xml
|
|
{
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
|
|
enum ValueHandleConstStringType
|
|
{
|
|
String = 0,
|
|
Number = 1,
|
|
Array = 2,
|
|
Object = 3,
|
|
Boolean = 4,
|
|
Null = 5,
|
|
}
|
|
|
|
static class ValueHandleLength
|
|
{
|
|
public const int Int8 = 1;
|
|
public const int Int16 = 2;
|
|
public const int Int32 = 4;
|
|
public const int Int64 = 8;
|
|
public const int UInt64 = 8;
|
|
public const int Single = 4;
|
|
public const int Double = 8;
|
|
public const int Decimal = 16;
|
|
public const int DateTime = 8;
|
|
public const int TimeSpan = 8;
|
|
public const int Guid = 16;
|
|
public const int UniqueId = 16;
|
|
}
|
|
|
|
enum ValueHandleType
|
|
{
|
|
Empty,
|
|
True,
|
|
False,
|
|
Zero,
|
|
One,
|
|
Int8,
|
|
Int16,
|
|
Int32,
|
|
Int64,
|
|
UInt64,
|
|
Single,
|
|
Double,
|
|
Decimal,
|
|
DateTime,
|
|
TimeSpan,
|
|
Guid,
|
|
UniqueId,
|
|
UTF8,
|
|
EscapedUTF8,
|
|
Base64,
|
|
Dictionary,
|
|
List,
|
|
Char,
|
|
Unicode,
|
|
QName,
|
|
ConstString
|
|
}
|
|
|
|
class ValueHandle
|
|
{
|
|
XmlBufferReader bufferReader;
|
|
ValueHandleType type;
|
|
int offset;
|
|
int length;
|
|
static Base64Encoding base64Encoding;
|
|
|
|
|
|
static string[] constStrings = {
|
|
"string",
|
|
"number",
|
|
"array",
|
|
"object",
|
|
"boolean",
|
|
"null",
|
|
};
|
|
|
|
public ValueHandle(XmlBufferReader bufferReader)
|
|
{
|
|
this.bufferReader = bufferReader;
|
|
this.type = ValueHandleType.Empty;
|
|
}
|
|
|
|
static Base64Encoding Base64Encoding
|
|
{
|
|
get
|
|
{
|
|
if (base64Encoding == null)
|
|
base64Encoding = new Base64Encoding();
|
|
return base64Encoding;
|
|
}
|
|
}
|
|
|
|
public void SetConstantValue(ValueHandleConstStringType constStringType)
|
|
{
|
|
type = ValueHandleType.ConstString;
|
|
offset = (int)constStringType;
|
|
}
|
|
|
|
public void SetValue(ValueHandleType type)
|
|
{
|
|
this.type = type;
|
|
}
|
|
|
|
public void SetDictionaryValue(int key)
|
|
{
|
|
SetValue(ValueHandleType.Dictionary, key, 0);
|
|
}
|
|
|
|
public void SetCharValue(int ch)
|
|
{
|
|
SetValue(ValueHandleType.Char, ch, 0);
|
|
}
|
|
|
|
public void SetQNameValue(int prefix, int key)
|
|
{
|
|
SetValue(ValueHandleType.QName, key, prefix);
|
|
}
|
|
|
|
public void SetValue(ValueHandleType type, int offset, int length)
|
|
{
|
|
this.type = type;
|
|
this.offset = offset;
|
|
this.length = length;
|
|
}
|
|
|
|
public bool IsWhitespace()
|
|
{
|
|
switch (this.type)
|
|
{
|
|
case ValueHandleType.UTF8:
|
|
return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
|
|
|
|
case ValueHandleType.Dictionary:
|
|
return bufferReader.IsWhitespaceKey(this.offset);
|
|
|
|
case ValueHandleType.Char:
|
|
int ch = GetChar();
|
|
if (ch > char.MaxValue)
|
|
return false;
|
|
return XmlConverter.IsWhitespace((char)ch);
|
|
|
|
case ValueHandleType.EscapedUTF8:
|
|
return bufferReader.IsWhitespaceUTF8(this.offset, this.length);
|
|
|
|
case ValueHandleType.Unicode:
|
|
return bufferReader.IsWhitespaceUnicode(this.offset, this.length);
|
|
|
|
case ValueHandleType.True:
|
|
case ValueHandleType.False:
|
|
case ValueHandleType.Zero:
|
|
case ValueHandleType.One:
|
|
return false;
|
|
|
|
case ValueHandleType.ConstString:
|
|
return constStrings[offset].Length == 0;
|
|
|
|
default:
|
|
return this.length == 0;
|
|
}
|
|
}
|
|
|
|
public Type ToType()
|
|
{
|
|
switch (type)
|
|
{
|
|
case ValueHandleType.False:
|
|
case ValueHandleType.True:
|
|
return typeof(bool);
|
|
case ValueHandleType.Zero:
|
|
case ValueHandleType.One:
|
|
case ValueHandleType.Int8:
|
|
case ValueHandleType.Int16:
|
|
case ValueHandleType.Int32:
|
|
return typeof(int);
|
|
case ValueHandleType.Int64:
|
|
return typeof(long);
|
|
case ValueHandleType.UInt64:
|
|
return typeof(ulong);
|
|
case ValueHandleType.Single:
|
|
return typeof(float);
|
|
case ValueHandleType.Double:
|
|
return typeof(double);
|
|
case ValueHandleType.Decimal:
|
|
return typeof(decimal);
|
|
case ValueHandleType.DateTime:
|
|
return typeof(DateTime);
|
|
case ValueHandleType.Empty:
|
|
case ValueHandleType.UTF8:
|
|
case ValueHandleType.Unicode:
|
|
case ValueHandleType.EscapedUTF8:
|
|
case ValueHandleType.Dictionary:
|
|
case ValueHandleType.Char:
|
|
case ValueHandleType.QName:
|
|
case ValueHandleType.ConstString:
|
|
return typeof(string);
|
|
case ValueHandleType.Base64:
|
|
return typeof(byte[]);
|
|
case ValueHandleType.List:
|
|
return typeof(object[]);
|
|
case ValueHandleType.UniqueId:
|
|
return typeof(UniqueId);
|
|
case ValueHandleType.Guid:
|
|
return typeof(Guid);
|
|
case ValueHandleType.TimeSpan:
|
|
return typeof(TimeSpan);
|
|
default:
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
}
|
|
}
|
|
|
|
public Boolean ToBoolean()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.False)
|
|
return false;
|
|
if (type == ValueHandleType.True)
|
|
return true;
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToBoolean(bufferReader.Buffer, offset, length);
|
|
if (type == ValueHandleType.Int8)
|
|
{
|
|
int value = GetInt8();
|
|
if (value == 0)
|
|
return false;
|
|
if (value == 1)
|
|
return true;
|
|
}
|
|
return XmlConverter.ToBoolean(GetString());
|
|
}
|
|
|
|
public int ToInt()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type == ValueHandleType.Int8)
|
|
return GetInt8();
|
|
if (type == ValueHandleType.Int16)
|
|
return GetInt16();
|
|
if (type == ValueHandleType.Int32)
|
|
return GetInt32();
|
|
if (type == ValueHandleType.Int64)
|
|
{
|
|
long value = GetInt64();
|
|
if (value >= int.MinValue && value <= int.MaxValue)
|
|
{
|
|
return (int)value;
|
|
}
|
|
}
|
|
if (type == ValueHandleType.UInt64)
|
|
{
|
|
ulong value = GetUInt64();
|
|
if (value <= int.MaxValue)
|
|
{
|
|
return (int)value;
|
|
}
|
|
}
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToInt32(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToInt32(GetString());
|
|
}
|
|
|
|
public long ToLong()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type == ValueHandleType.Int8)
|
|
return GetInt8();
|
|
if (type == ValueHandleType.Int16)
|
|
return GetInt16();
|
|
if (type == ValueHandleType.Int32)
|
|
return GetInt32();
|
|
if (type == ValueHandleType.Int64)
|
|
return GetInt64();
|
|
if (type == ValueHandleType.UInt64)
|
|
{
|
|
ulong value = GetUInt64();
|
|
if (value <= long.MaxValue)
|
|
{
|
|
return (long)value;
|
|
}
|
|
}
|
|
if (type == ValueHandleType.UTF8)
|
|
{
|
|
return XmlConverter.ToInt64(bufferReader.Buffer, offset, length);
|
|
}
|
|
return XmlConverter.ToInt64(GetString());
|
|
}
|
|
|
|
public ulong ToULong()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
|
|
{
|
|
long value = ToLong();
|
|
if (value >= 0)
|
|
return (ulong)value;
|
|
}
|
|
if (type == ValueHandleType.UInt64)
|
|
return GetUInt64();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToUInt64(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToUInt64(GetString());
|
|
}
|
|
|
|
public Single ToSingle()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Single)
|
|
return GetSingle();
|
|
if (type == ValueHandleType.Double)
|
|
{
|
|
double value = GetDouble();
|
|
if ((value >= Single.MinValue && value <= Single.MaxValue) || double.IsInfinity(value) || double.IsNaN(value))
|
|
return (Single)value;
|
|
}
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type == ValueHandleType.Int8)
|
|
return GetInt8();
|
|
if (type == ValueHandleType.Int16)
|
|
return GetInt16();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToSingle(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToSingle(GetString());
|
|
}
|
|
|
|
public Double ToDouble()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Double)
|
|
return GetDouble();
|
|
if (type == ValueHandleType.Single)
|
|
return GetSingle();
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type == ValueHandleType.Int8)
|
|
return GetInt8();
|
|
if (type == ValueHandleType.Int16)
|
|
return GetInt16();
|
|
if (type == ValueHandleType.Int32)
|
|
return GetInt32();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToDouble(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToDouble(GetString());
|
|
}
|
|
|
|
public Decimal ToDecimal()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.Decimal)
|
|
return GetDecimal();
|
|
if (type == ValueHandleType.Zero)
|
|
return 0;
|
|
if (type == ValueHandleType.One)
|
|
return 1;
|
|
if (type >= ValueHandleType.Int8 && type <= ValueHandleType.Int64)
|
|
return ToLong();
|
|
if (type == ValueHandleType.UInt64)
|
|
return GetUInt64();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToDecimal(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToDecimal(GetString());
|
|
}
|
|
|
|
public DateTime ToDateTime()
|
|
{
|
|
if (type == ValueHandleType.DateTime)
|
|
{
|
|
return XmlConverter.ToDateTime(GetInt64());
|
|
}
|
|
if (type == ValueHandleType.UTF8)
|
|
{
|
|
return XmlConverter.ToDateTime(bufferReader.Buffer, offset, length);
|
|
}
|
|
return XmlConverter.ToDateTime(GetString());
|
|
}
|
|
|
|
public UniqueId ToUniqueId()
|
|
{
|
|
if (type == ValueHandleType.UniqueId)
|
|
return GetUniqueId();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToUniqueId(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToUniqueId(GetString());
|
|
}
|
|
|
|
public TimeSpan ToTimeSpan()
|
|
{
|
|
if (type == ValueHandleType.TimeSpan)
|
|
return new TimeSpan(GetInt64());
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToTimeSpan(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToTimeSpan(GetString());
|
|
}
|
|
|
|
public Guid ToGuid()
|
|
{
|
|
if (type == ValueHandleType.Guid)
|
|
return GetGuid();
|
|
if (type == ValueHandleType.UTF8)
|
|
return XmlConverter.ToGuid(bufferReader.Buffer, offset, length);
|
|
return XmlConverter.ToGuid(GetString());
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return GetString();
|
|
}
|
|
|
|
public byte[] ToByteArray()
|
|
{
|
|
if (type == ValueHandleType.Base64)
|
|
{
|
|
byte[] buffer = new byte[length];
|
|
GetBase64(buffer, 0, length);
|
|
return buffer;
|
|
}
|
|
if (type == ValueHandleType.UTF8 && (length % 4) == 0)
|
|
{
|
|
try
|
|
{
|
|
int expectedLength = length / 4 * 3;
|
|
if (length > 0)
|
|
{
|
|
if (bufferReader.Buffer[offset + length - 1] == '=')
|
|
{
|
|
expectedLength--;
|
|
if (bufferReader.Buffer[offset + length - 2] == '=')
|
|
expectedLength--;
|
|
}
|
|
}
|
|
byte[] buffer = new byte[expectedLength];
|
|
int actualLength = Base64Encoding.GetBytes(bufferReader.Buffer, this.offset, this.length, buffer, 0);
|
|
if (actualLength != buffer.Length)
|
|
{
|
|
byte[] newBuffer = new byte[actualLength];
|
|
Buffer.BlockCopy(buffer, 0, newBuffer, 0, actualLength);
|
|
buffer = newBuffer;
|
|
}
|
|
return buffer;
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
// Something unhappy with the characters, fall back to the hard way
|
|
}
|
|
}
|
|
try
|
|
{
|
|
return Base64Encoding.GetBytes(XmlConverter.StripWhitespace(GetString()));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(exception.Message, exception.InnerException));
|
|
}
|
|
}
|
|
|
|
public string GetString()
|
|
{
|
|
ValueHandleType type = this.type;
|
|
if (type == ValueHandleType.UTF8)
|
|
return GetCharsText();
|
|
|
|
switch (type)
|
|
{
|
|
case ValueHandleType.False:
|
|
return "false";
|
|
case ValueHandleType.True:
|
|
return "true";
|
|
case ValueHandleType.Zero:
|
|
return "0";
|
|
case ValueHandleType.One:
|
|
return "1";
|
|
case ValueHandleType.Int8:
|
|
case ValueHandleType.Int16:
|
|
case ValueHandleType.Int32:
|
|
return XmlConverter.ToString(ToInt());
|
|
case ValueHandleType.Int64:
|
|
return XmlConverter.ToString(GetInt64());
|
|
case ValueHandleType.UInt64:
|
|
return XmlConverter.ToString(GetUInt64());
|
|
case ValueHandleType.Single:
|
|
return XmlConverter.ToString(GetSingle());
|
|
case ValueHandleType.Double:
|
|
return XmlConverter.ToString(GetDouble());
|
|
case ValueHandleType.Decimal:
|
|
return XmlConverter.ToString(GetDecimal());
|
|
case ValueHandleType.DateTime:
|
|
return XmlConverter.ToString(ToDateTime());
|
|
case ValueHandleType.Empty:
|
|
return string.Empty;
|
|
case ValueHandleType.UTF8:
|
|
return GetCharsText();
|
|
case ValueHandleType.Unicode:
|
|
return GetUnicodeCharsText();
|
|
case ValueHandleType.EscapedUTF8:
|
|
return GetEscapedCharsText();
|
|
case ValueHandleType.Char:
|
|
return GetCharText();
|
|
case ValueHandleType.Dictionary:
|
|
return GetDictionaryString().Value;
|
|
case ValueHandleType.Base64:
|
|
return Base64Encoding.GetString(ToByteArray());
|
|
case ValueHandleType.List:
|
|
return XmlConverter.ToString(ToList());
|
|
case ValueHandleType.UniqueId:
|
|
return XmlConverter.ToString(ToUniqueId());
|
|
case ValueHandleType.Guid:
|
|
return XmlConverter.ToString(ToGuid());
|
|
case ValueHandleType.TimeSpan:
|
|
return XmlConverter.ToString(ToTimeSpan());
|
|
case ValueHandleType.QName:
|
|
return GetQNameDictionaryText();
|
|
case ValueHandleType.ConstString:
|
|
return constStrings[offset];
|
|
default:
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
}
|
|
}
|
|
|
|
// ASSUMPTION ([....]): all chars in str will be ASCII
|
|
public bool Equals2(string str, bool checkLower)
|
|
{
|
|
if (this.type != ValueHandleType.UTF8)
|
|
return GetString() == str;
|
|
|
|
if (this.length != str.Length)
|
|
return false;
|
|
|
|
byte[] buffer = bufferReader.Buffer;
|
|
for (int i = 0; i < this.length; ++i)
|
|
{
|
|
Fx.Assert(str[i] < 128, "");
|
|
byte ch = buffer[i + this.offset];
|
|
if (ch == str[i])
|
|
continue;
|
|
|
|
if (checkLower && char.ToLowerInvariant((char)ch) == str[i])
|
|
continue;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Sign(XmlSigningNodeWriter writer)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ValueHandleType.Int8:
|
|
case ValueHandleType.Int16:
|
|
case ValueHandleType.Int32:
|
|
writer.WriteInt32Text(ToInt());
|
|
break;
|
|
case ValueHandleType.Int64:
|
|
writer.WriteInt64Text(GetInt64());
|
|
break;
|
|
case ValueHandleType.UInt64:
|
|
writer.WriteUInt64Text(GetUInt64());
|
|
break;
|
|
case ValueHandleType.Single:
|
|
writer.WriteFloatText(GetSingle());
|
|
break;
|
|
case ValueHandleType.Double:
|
|
writer.WriteDoubleText(GetDouble());
|
|
break;
|
|
case ValueHandleType.Decimal:
|
|
writer.WriteDecimalText(GetDecimal());
|
|
break;
|
|
case ValueHandleType.DateTime:
|
|
writer.WriteDateTimeText(ToDateTime());
|
|
break;
|
|
case ValueHandleType.Empty:
|
|
break;
|
|
case ValueHandleType.UTF8:
|
|
writer.WriteEscapedText(bufferReader.Buffer, offset, length);
|
|
break;
|
|
case ValueHandleType.Base64:
|
|
writer.WriteBase64Text(bufferReader.Buffer, 0, bufferReader.Buffer, offset, length);
|
|
break;
|
|
case ValueHandleType.UniqueId:
|
|
writer.WriteUniqueIdText(ToUniqueId());
|
|
break;
|
|
case ValueHandleType.Guid:
|
|
writer.WriteGuidText(ToGuid());
|
|
break;
|
|
case ValueHandleType.TimeSpan:
|
|
writer.WriteTimeSpanText(ToTimeSpan());
|
|
break;
|
|
default:
|
|
writer.WriteEscapedText(GetString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
public object[] ToList()
|
|
{
|
|
return bufferReader.GetList(offset, length);
|
|
}
|
|
|
|
public object ToObject()
|
|
{
|
|
switch (type)
|
|
{
|
|
case ValueHandleType.False:
|
|
case ValueHandleType.True:
|
|
return ToBoolean();
|
|
case ValueHandleType.Zero:
|
|
case ValueHandleType.One:
|
|
case ValueHandleType.Int8:
|
|
case ValueHandleType.Int16:
|
|
case ValueHandleType.Int32:
|
|
return ToInt();
|
|
case ValueHandleType.Int64:
|
|
return ToLong();
|
|
case ValueHandleType.UInt64:
|
|
return GetUInt64();
|
|
case ValueHandleType.Single:
|
|
return ToSingle();
|
|
case ValueHandleType.Double:
|
|
return ToDouble();
|
|
case ValueHandleType.Decimal:
|
|
return ToDecimal();
|
|
case ValueHandleType.DateTime:
|
|
return ToDateTime();
|
|
case ValueHandleType.Empty:
|
|
case ValueHandleType.UTF8:
|
|
case ValueHandleType.Unicode:
|
|
case ValueHandleType.EscapedUTF8:
|
|
case ValueHandleType.Dictionary:
|
|
case ValueHandleType.Char:
|
|
case ValueHandleType.ConstString:
|
|
return ToString();
|
|
case ValueHandleType.Base64:
|
|
return ToByteArray();
|
|
case ValueHandleType.List:
|
|
return ToList();
|
|
case ValueHandleType.UniqueId:
|
|
return ToUniqueId();
|
|
case ValueHandleType.Guid:
|
|
return ToGuid();
|
|
case ValueHandleType.TimeSpan:
|
|
return ToTimeSpan();
|
|
default:
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
}
|
|
}
|
|
|
|
public bool TryReadBase64(byte[] buffer, int offset, int count, out int actual)
|
|
{
|
|
if (type == ValueHandleType.Base64)
|
|
{
|
|
actual = Math.Min(this.length, count);
|
|
GetBase64(buffer, offset, actual);
|
|
this.offset += actual;
|
|
this.length -= actual;
|
|
return true;
|
|
}
|
|
if (type == ValueHandleType.UTF8 && count >= 3 && (this.length % 4) == 0)
|
|
{
|
|
try
|
|
{
|
|
int charCount = Math.Min(count / 3 * 4, this.length);
|
|
actual = Base64Encoding.GetBytes(bufferReader.Buffer, this.offset, charCount, buffer, offset);
|
|
this.offset += charCount;
|
|
this.length -= charCount;
|
|
return true;
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
// Something unhappy with the characters, fall back to the hard way
|
|
}
|
|
}
|
|
actual = 0;
|
|
return false;
|
|
}
|
|
|
|
public bool TryReadChars(char[] chars, int offset, int count, out int actual)
|
|
{
|
|
Fx.Assert(offset + count <= chars.Length, string.Format("offset '{0}' + count '{1}' MUST BE <= chars.Length '{2}'", offset, count, chars.Length));
|
|
|
|
if (type == ValueHandleType.Unicode)
|
|
return TryReadUnicodeChars(chars, offset, count, out actual);
|
|
|
|
if (type != ValueHandleType.UTF8)
|
|
{
|
|
actual = 0;
|
|
return false;
|
|
}
|
|
|
|
int charOffset = offset;
|
|
int charCount = count;
|
|
byte[] bytes = bufferReader.Buffer;
|
|
int byteOffset = this.offset;
|
|
int byteCount = this.length;
|
|
bool insufficientSpaceInCharsArray = false;
|
|
|
|
while (true)
|
|
{
|
|
while (charCount > 0 && byteCount > 0)
|
|
{
|
|
// fast path for codepoints U+0000 - U+007F
|
|
byte b = bytes[byteOffset];
|
|
if (b >= 0x80)
|
|
break;
|
|
chars[charOffset] = (char)b;
|
|
byteOffset++;
|
|
byteCount--;
|
|
charOffset++;
|
|
charCount--;
|
|
}
|
|
|
|
if (charCount == 0 || byteCount == 0 || insufficientSpaceInCharsArray)
|
|
break;
|
|
|
|
int actualByteCount;
|
|
int actualCharCount;
|
|
|
|
UTF8Encoding encoding = new UTF8Encoding(false, true);
|
|
try
|
|
{
|
|
// If we're asking for more than are possibly available, or more than are truly available then we can return the entire thing
|
|
if (charCount >= encoding.GetMaxCharCount(byteCount) || charCount >= encoding.GetCharCount(bytes, byteOffset, byteCount))
|
|
{
|
|
actualCharCount = encoding.GetChars(bytes, byteOffset, byteCount, chars, charOffset);
|
|
actualByteCount = byteCount;
|
|
}
|
|
else
|
|
{
|
|
Decoder decoder = encoding.GetDecoder();
|
|
|
|
// Since x bytes can never generate more than x characters this is a safe estimate as to what will fit
|
|
actualByteCount = Math.Min(charCount, byteCount);
|
|
|
|
// We use a decoder so we don't error if we fall across a character boundary
|
|
actualCharCount = decoder.GetChars(bytes, byteOffset, actualByteCount, chars, charOffset);
|
|
|
|
// We might've gotten zero characters though if < 4 bytes were requested because
|
|
// codepoints from U+0000 - U+FFFF can be up to 3 bytes in UTF-8, and represented as ONE char
|
|
// codepoints from U+10000 - U+10FFFF (last Unicode codepoint representable in UTF-8) are represented by up to 4 bytes in UTF-8
|
|
// and represented as TWO chars (high+low surrogate)
|
|
// (e.g. 1 char requested, 1 char in the buffer represented in 3 bytes)
|
|
while (actualCharCount == 0)
|
|
{
|
|
// Note the by the time we arrive here, if actualByteCount == 3, the next decoder.GetChars() call will read the 4th byte
|
|
// if we don't bail out since the while loop will advance actualByteCount only after reading the byte.
|
|
if (actualByteCount >= 3 && charCount < 2)
|
|
{
|
|
// If we reach here, it means that we're:
|
|
// - trying to decode more than 3 bytes and,
|
|
// - there is only one char left of charCount where we're stuffing decoded characters.
|
|
// In this case, we need to back off since decoding > 3 bytes in UTF-8 means that we will get 2 16-bit chars
|
|
// (a high surrogate and a low surrogate) - the Decoder will attempt to provide both at once
|
|
// and an ArgumentException will be thrown complaining that there's not enough space in the output char array.
|
|
|
|
// actualByteCount = 0 when the while loop is broken out of; decoder goes out of scope so its state no longer matters
|
|
|
|
insufficientSpaceInCharsArray = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(byteOffset + actualByteCount < bytes.Length,
|
|
string.Format("byteOffset {0} + actualByteCount {1} MUST BE < bytes.Length {2}", byteOffset, actualByteCount, bytes.Length));
|
|
|
|
// Request a few more bytes to get at least one character
|
|
actualCharCount = decoder.GetChars(bytes, byteOffset + actualByteCount, 1, chars, charOffset);
|
|
actualByteCount++;
|
|
}
|
|
}
|
|
|
|
// Now that we actually retrieved some characters, figure out how many bytes it actually was
|
|
actualByteCount = encoding.GetByteCount(chars, charOffset, actualCharCount);
|
|
}
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(bytes, byteOffset, byteCount, exception));
|
|
}
|
|
|
|
// Advance
|
|
byteOffset += actualByteCount;
|
|
byteCount -= actualByteCount;
|
|
|
|
charOffset += actualCharCount;
|
|
charCount -= actualCharCount;
|
|
}
|
|
|
|
this.offset = byteOffset;
|
|
this.length = byteCount;
|
|
|
|
actual = (count - charCount);
|
|
return true;
|
|
}
|
|
|
|
bool TryReadUnicodeChars(char[] chars, int offset, int count, out int actual)
|
|
{
|
|
int charCount = Math.Min(count, this.length / sizeof(char));
|
|
for (int i = 0; i < charCount; i++)
|
|
{
|
|
chars[offset + i] = (char)bufferReader.GetInt16(this.offset + i * sizeof(char));
|
|
}
|
|
this.offset += charCount * sizeof(char);
|
|
this.length -= charCount * sizeof(char);
|
|
actual = charCount;
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetDictionaryString(out XmlDictionaryString value)
|
|
{
|
|
if (type == ValueHandleType.Dictionary)
|
|
{
|
|
value = GetDictionaryString();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
value = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryGetByteArrayLength(out int length)
|
|
{
|
|
if (type == ValueHandleType.Base64)
|
|
{
|
|
length = this.length;
|
|
return true;
|
|
}
|
|
length = 0;
|
|
return false;
|
|
}
|
|
|
|
string GetCharsText()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.UTF8, "");
|
|
if (length == 1 && bufferReader.GetByte(offset) == '1')
|
|
return "1";
|
|
return bufferReader.GetString(offset, length);
|
|
}
|
|
|
|
string GetUnicodeCharsText()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Unicode, "");
|
|
return bufferReader.GetUnicodeString(offset, length);
|
|
}
|
|
|
|
string GetEscapedCharsText()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.EscapedUTF8, "");
|
|
return bufferReader.GetEscapedString(offset, length);
|
|
}
|
|
|
|
string GetCharText()
|
|
{
|
|
int ch = GetChar();
|
|
if (ch > char.MaxValue)
|
|
{
|
|
SurrogateChar surrogate = new SurrogateChar(ch);
|
|
char[] chars = new char[2];
|
|
chars[0] = surrogate.HighChar;
|
|
chars[1] = surrogate.LowChar;
|
|
return new string(chars, 0, 2);
|
|
}
|
|
else
|
|
{
|
|
return ((char)ch).ToString();
|
|
}
|
|
}
|
|
|
|
int GetChar()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Char, "");
|
|
return offset;
|
|
}
|
|
|
|
int GetInt8()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Int8, "");
|
|
return bufferReader.GetInt8(offset);
|
|
}
|
|
|
|
int GetInt16()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Int16, "");
|
|
return bufferReader.GetInt16(offset);
|
|
}
|
|
|
|
int GetInt32()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Int32, "");
|
|
return bufferReader.GetInt32(offset);
|
|
}
|
|
|
|
long GetInt64()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Int64 || type == ValueHandleType.TimeSpan || type == ValueHandleType.DateTime, "");
|
|
return bufferReader.GetInt64(offset);
|
|
}
|
|
|
|
ulong GetUInt64()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.UInt64, "");
|
|
return bufferReader.GetUInt64(offset);
|
|
}
|
|
|
|
float GetSingle()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Single, "");
|
|
return bufferReader.GetSingle(offset);
|
|
}
|
|
|
|
double GetDouble()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Double, "");
|
|
return bufferReader.GetDouble(offset);
|
|
}
|
|
|
|
decimal GetDecimal()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Decimal, "");
|
|
return bufferReader.GetDecimal(offset);
|
|
}
|
|
|
|
UniqueId GetUniqueId()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.UniqueId, "");
|
|
return bufferReader.GetUniqueId(offset);
|
|
}
|
|
|
|
Guid GetGuid()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Guid, "");
|
|
return bufferReader.GetGuid(offset);
|
|
}
|
|
|
|
void GetBase64(byte[] buffer, int offset, int count)
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Base64, "");
|
|
bufferReader.GetBase64(this.offset, buffer, offset, count);
|
|
}
|
|
|
|
XmlDictionaryString GetDictionaryString()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.Dictionary, "");
|
|
return bufferReader.GetDictionaryString(offset);
|
|
}
|
|
|
|
string GetQNameDictionaryText()
|
|
{
|
|
Fx.Assert(type == ValueHandleType.QName, "");
|
|
return string.Concat(PrefixHandle.GetString(PrefixHandle.GetAlphaPrefix(length)), ":", bufferReader.GetDictionaryString(offset));
|
|
}
|
|
}
|
|
}
|