536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1181 lines
42 KiB
C#
1181 lines
42 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
// PERF, Microsoft, Microsoft: Make LookupNamespace do something smarter when lots of names
|
|
// PERF, Microsoft, Microsoft: Make Attribute lookup smarter when lots of attributes
|
|
// PERF: Microsoft, Microsoft: Compare safe/unsafe versions
|
|
|
|
namespace System.Xml
|
|
{
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Security;
|
|
using System.Text;
|
|
|
|
static class XmlConverter
|
|
{
|
|
public const int MaxDateTimeChars = 64;
|
|
public const int MaxInt32Chars = 16;
|
|
public const int MaxInt64Chars = 32;
|
|
public const int MaxBoolChars = 5;
|
|
public const int MaxFloatChars = 16;
|
|
public const int MaxDoubleChars = 32;
|
|
public const int MaxDecimalChars = 40;
|
|
public const int MaxUInt64Chars = 32;
|
|
public const int MaxPrimitiveChars = MaxDateTimeChars;
|
|
|
|
static char[] whiteSpaceChars = new char[] { ' ', '\t', '\n', '\r' };
|
|
static UTF8Encoding utf8Encoding;
|
|
static UnicodeEncoding unicodeEncoding;
|
|
static Base64Encoding base64Encoding;
|
|
|
|
static public Base64Encoding Base64Encoding
|
|
{
|
|
get
|
|
{
|
|
if (base64Encoding == null)
|
|
base64Encoding = new Base64Encoding();
|
|
return base64Encoding;
|
|
}
|
|
}
|
|
|
|
static UTF8Encoding UTF8Encoding
|
|
{
|
|
get
|
|
{
|
|
if (utf8Encoding == null)
|
|
utf8Encoding = new UTF8Encoding(false, true);
|
|
return utf8Encoding;
|
|
}
|
|
}
|
|
|
|
static UnicodeEncoding UnicodeEncoding
|
|
{
|
|
get
|
|
{
|
|
if (unicodeEncoding == null)
|
|
unicodeEncoding = new UnicodeEncoding(false, false, true);
|
|
return unicodeEncoding;
|
|
}
|
|
}
|
|
|
|
static public bool ToBoolean(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToBoolean(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Boolean", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Boolean", exception));
|
|
}
|
|
}
|
|
|
|
static public bool ToBoolean(byte[] buffer, int offset, int count)
|
|
{
|
|
if (count == 1)
|
|
{
|
|
byte ch = buffer[offset];
|
|
if (ch == (byte)'1')
|
|
return true;
|
|
else if (ch == (byte)'0')
|
|
return false;
|
|
}
|
|
return ToBoolean(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public int ToInt32(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToInt32(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int32", exception));
|
|
}
|
|
}
|
|
|
|
static public int ToInt32(byte[] buffer, int offset, int count)
|
|
{
|
|
int value;
|
|
if (TryParseInt32(buffer, offset, count, out value))
|
|
return value;
|
|
return ToInt32(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public Int64 ToInt64(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToInt64(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Int64", exception));
|
|
}
|
|
}
|
|
|
|
static public Int64 ToInt64(byte[] buffer, int offset, int count)
|
|
{
|
|
long value;
|
|
if (TryParseInt64(buffer, offset, count, out value))
|
|
return value;
|
|
return ToInt64(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public float ToSingle(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToSingle(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "float", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "float", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "float", exception));
|
|
}
|
|
}
|
|
|
|
static public float ToSingle(byte[] buffer, int offset, int count)
|
|
{
|
|
float value;
|
|
if (TryParseSingle(buffer, offset, count, out value))
|
|
return value;
|
|
return ToSingle(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public double ToDouble(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToDouble(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "double", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "double", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "double", exception));
|
|
}
|
|
}
|
|
|
|
static public double ToDouble(byte[] buffer, int offset, int count)
|
|
{
|
|
double value;
|
|
if (TryParseDouble(buffer, offset, count, out value))
|
|
return value;
|
|
return ToDouble(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public decimal ToDecimal(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToDecimal(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "decimal", exception));
|
|
}
|
|
}
|
|
|
|
static public decimal ToDecimal(byte[] buffer, int offset, int count)
|
|
{
|
|
return ToDecimal(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public DateTime ToDateTime(Int64 value)
|
|
{
|
|
try
|
|
{
|
|
return DateTime.FromBinary(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(ToString(value), "DateTime", exception));
|
|
}
|
|
}
|
|
|
|
static public DateTime ToDateTime(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.RoundtripKind);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "DateTime", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "DateTime", exception));
|
|
}
|
|
}
|
|
|
|
static public DateTime ToDateTime(byte[] buffer, int offset, int count)
|
|
{
|
|
DateTime value;
|
|
if (TryParseDateTime(buffer, offset, count, out value))
|
|
return value;
|
|
return ToDateTime(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public UniqueId ToUniqueId(string value)
|
|
{
|
|
try
|
|
{
|
|
return new UniqueId(Trim(value));
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "UniqueId", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "UniqueId", exception));
|
|
}
|
|
}
|
|
|
|
static public UniqueId ToUniqueId(byte[] buffer, int offset, int count)
|
|
{
|
|
return ToUniqueId(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public TimeSpan ToTimeSpan(string value)
|
|
{
|
|
try
|
|
{
|
|
return XmlConvert.ToTimeSpan(value);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "TimeSpan", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "TimeSpan", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "TimeSpan", exception));
|
|
}
|
|
}
|
|
|
|
static public TimeSpan ToTimeSpan(byte[] buffer, int offset, int count)
|
|
{
|
|
return ToTimeSpan(ToString(buffer, offset, count));
|
|
}
|
|
|
|
[SuppressMessage("Reliability", "Reliability113", Justification = "Catching expected exceptions inline instead of calling Fx.CreateGuid to minimize code change")]
|
|
static public Guid ToGuid(string value)
|
|
{
|
|
try
|
|
{
|
|
return Guid.Parse(Trim(value));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Guid", exception));
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Guid", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "Guid", exception));
|
|
}
|
|
}
|
|
|
|
static public Guid ToGuid(byte[] buffer, int offset, int count)
|
|
{
|
|
return ToGuid(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public UInt64 ToUInt64(string value)
|
|
{
|
|
try
|
|
{
|
|
return ulong.Parse(value, NumberFormatInfo.InvariantInfo);
|
|
}
|
|
catch (ArgumentException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "UInt64", exception));
|
|
}
|
|
catch (FormatException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "UInt64", exception));
|
|
}
|
|
catch (OverflowException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateConversionException(value, "UInt64", exception));
|
|
}
|
|
}
|
|
|
|
static public UInt64 ToUInt64(byte[] buffer, int offset, int count)
|
|
{
|
|
return ToUInt64(ToString(buffer, offset, count));
|
|
}
|
|
|
|
static public string ToString(byte[] buffer, int offset, int count)
|
|
{
|
|
try
|
|
{
|
|
return UTF8Encoding.GetString(buffer, offset, count);
|
|
}
|
|
catch (DecoderFallbackException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(buffer, offset, count, exception));
|
|
}
|
|
}
|
|
|
|
static public string ToStringUnicode(byte[] buffer, int offset, int count)
|
|
{
|
|
try
|
|
{
|
|
return UnicodeEncoding.GetString(buffer, offset, count);
|
|
}
|
|
catch (DecoderFallbackException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(buffer, offset, count, exception));
|
|
}
|
|
}
|
|
|
|
|
|
static public byte[] ToBytes(string value)
|
|
{
|
|
try
|
|
{
|
|
return UTF8Encoding.GetBytes(value);
|
|
}
|
|
catch (DecoderFallbackException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(value, exception));
|
|
}
|
|
}
|
|
|
|
static public int ToChars(byte[] buffer, int offset, int count, char[] chars, int charOffset)
|
|
{
|
|
try
|
|
{
|
|
return UTF8Encoding.GetChars(buffer, offset, count, chars, charOffset);
|
|
}
|
|
catch (DecoderFallbackException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlExceptionHelper.CreateEncodingException(buffer, offset, count, exception));
|
|
}
|
|
}
|
|
|
|
static public string ToString(bool value) { return value ? "true" : "false"; }
|
|
static public string ToString(int value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(Int64 value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(float value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(double value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(decimal value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(TimeSpan value) { return XmlConvert.ToString(value); }
|
|
static public string ToString(UniqueId value) { return value.ToString(); }
|
|
static public string ToString(Guid value) { return value.ToString(); }
|
|
static public string ToString(UInt64 value) { return value.ToString(NumberFormatInfo.InvariantInfo); }
|
|
|
|
static public string ToString(DateTime value)
|
|
{
|
|
byte[] dateChars = new byte[MaxDateTimeChars];
|
|
int count = ToChars(value, dateChars, 0);
|
|
return ToString(dateChars, 0, count);
|
|
}
|
|
|
|
static string ToString(object value)
|
|
{
|
|
if (value is int)
|
|
return ToString((int)value);
|
|
else if (value is Int64)
|
|
return ToString((Int64)value);
|
|
else if (value is float)
|
|
return ToString((float)value);
|
|
else if (value is double)
|
|
return ToString((double)value);
|
|
else if (value is decimal)
|
|
return ToString((decimal)value);
|
|
else if (value is TimeSpan)
|
|
return ToString((TimeSpan)value);
|
|
else if (value is UniqueId)
|
|
return ToString((UniqueId)value);
|
|
else if (value is Guid)
|
|
return ToString((Guid)value);
|
|
else if (value is UInt64)
|
|
return ToString((UInt64)value);
|
|
else if (value is DateTime)
|
|
return ToString((DateTime)value);
|
|
else if (value is bool)
|
|
return ToString((bool)value);
|
|
else
|
|
return value.ToString();
|
|
}
|
|
|
|
static public string ToString(object[] objects)
|
|
{
|
|
if (objects.Length == 0)
|
|
return string.Empty;
|
|
string value = ToString(objects[0]);
|
|
if (objects.Length > 1)
|
|
{
|
|
StringBuilder sb = new StringBuilder(value);
|
|
for (int i = 1; i < objects.Length; i++)
|
|
{
|
|
sb.Append(' ');
|
|
sb.Append(ToString(objects[i]));
|
|
}
|
|
value = sb.ToString();
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static public void ToQualifiedName(string qname, out string prefix, out string localName)
|
|
{
|
|
int index = qname.IndexOf(':');
|
|
if (index < 0)
|
|
{
|
|
prefix = string.Empty;
|
|
localName = Trim(qname);
|
|
}
|
|
else
|
|
{
|
|
if (index == qname.Length - 1)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.XmlInvalidQualifiedName, qname)));
|
|
prefix = Trim(qname.Substring(0, index));
|
|
localName = Trim(qname.Substring(index + 1));
|
|
}
|
|
}
|
|
|
|
static bool TryParseInt32(byte[] chars, int offset, int count, out int result)
|
|
{
|
|
result = 0;
|
|
if (count == 0)
|
|
return false;
|
|
int value = 0;
|
|
int offsetMax = offset + count;
|
|
if (chars[offset] == '-')
|
|
{
|
|
if (count == 1)
|
|
return false;
|
|
for (int i = offset + 1; i < offsetMax; i++)
|
|
{
|
|
int digit = (chars[i] - '0');
|
|
if ((uint)digit > 9)
|
|
return false;
|
|
if (value < int.MinValue / 10)
|
|
return false;
|
|
value *= 10;
|
|
if (value < int.MinValue + digit)
|
|
return false;
|
|
value -= digit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = offset; i < offsetMax; i++)
|
|
{
|
|
int digit = (chars[i] - '0');
|
|
if ((uint)digit > 9)
|
|
return false;
|
|
if (value > int.MaxValue / 10)
|
|
return false;
|
|
value *= 10;
|
|
if (value > int.MaxValue - digit)
|
|
return false;
|
|
value += digit;
|
|
}
|
|
}
|
|
result = value;
|
|
return true;
|
|
}
|
|
|
|
static bool TryParseInt64(byte[] chars, int offset, int count, out long result)
|
|
{
|
|
result = 0;
|
|
if (count < 11)
|
|
{
|
|
int value;
|
|
if (!TryParseInt32(chars, offset, count, out value))
|
|
return false;
|
|
result = value;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
long value = 0;
|
|
int offsetMax = offset + count;
|
|
if (chars[offset] == '-')
|
|
{
|
|
if (count == 1)
|
|
return false;
|
|
for (int i = offset + 1; i < offsetMax; i++)
|
|
{
|
|
int digit = (chars[i] - '0');
|
|
if ((uint)digit > 9)
|
|
return false;
|
|
if (value < long.MinValue / 10)
|
|
return false;
|
|
value *= 10;
|
|
if (value < long.MinValue + digit)
|
|
return false;
|
|
value -= digit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = offset; i < offsetMax; i++)
|
|
{
|
|
int digit = (chars[i] - '0');
|
|
if ((uint)digit > 9)
|
|
return false;
|
|
if (value > long.MaxValue / 10)
|
|
return false;
|
|
value *= 10;
|
|
if (value > long.MaxValue - digit)
|
|
return false;
|
|
value += digit;
|
|
}
|
|
}
|
|
result = value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool TryParseSingle(byte[] chars, int offset, int count, out float result)
|
|
{
|
|
result = 0;
|
|
int offsetMax = offset + count;
|
|
bool negative = false;
|
|
if (offset < offsetMax && chars[offset] == '-')
|
|
{
|
|
negative = true;
|
|
offset++;
|
|
count--;
|
|
}
|
|
if (count < 1 || count > 10)
|
|
return false;
|
|
int value = 0;
|
|
int ch;
|
|
while (offset < offsetMax)
|
|
{
|
|
ch = (chars[offset] - '0');
|
|
if (ch == ('.' - '0'))
|
|
{
|
|
offset++;
|
|
int pow10 = 1;
|
|
while (offset < offsetMax)
|
|
{
|
|
ch = chars[offset] - '0';
|
|
if (((uint)ch) >= 10)
|
|
return false;
|
|
pow10 *= 10;
|
|
value = value * 10 + ch;
|
|
offset++;
|
|
}
|
|
// More than 8 characters (7 sig figs and a decimal) and int -> float conversion is lossy, so use double
|
|
if (count > 8)
|
|
{
|
|
result = (float)((double)value / (double)pow10);
|
|
}
|
|
else
|
|
{
|
|
result = (float)value / (float)pow10;
|
|
}
|
|
if (negative)
|
|
result = -result;
|
|
return true;
|
|
}
|
|
else if (((uint)ch) >= 10)
|
|
return false;
|
|
value = value * 10 + ch;
|
|
offset++;
|
|
}
|
|
// Ten digits w/out a decimal point might've overflowed the int
|
|
if (count == 10)
|
|
return false;
|
|
if (negative)
|
|
result = -value;
|
|
else
|
|
result = value;
|
|
return true;
|
|
}
|
|
|
|
static bool TryParseDouble(byte[] chars, int offset, int count, out double result)
|
|
{
|
|
result = 0;
|
|
int offsetMax = offset + count;
|
|
bool negative = false;
|
|
if (offset < offsetMax && chars[offset] == '-')
|
|
{
|
|
negative = true;
|
|
offset++;
|
|
count--;
|
|
}
|
|
if (count < 1 || count > 10)
|
|
return false;
|
|
int value = 0;
|
|
int ch;
|
|
while (offset < offsetMax)
|
|
{
|
|
ch = (chars[offset] - '0');
|
|
if (ch == ('.' - '0'))
|
|
{
|
|
offset++;
|
|
int pow10 = 1;
|
|
while (offset < offsetMax)
|
|
{
|
|
ch = chars[offset] - '0';
|
|
if (((uint)ch) >= 10)
|
|
return false;
|
|
pow10 *= 10;
|
|
value = value * 10 + ch;
|
|
offset++;
|
|
}
|
|
if (negative)
|
|
result = -(double)value / pow10;
|
|
else
|
|
result = (double)value / pow10;
|
|
return true;
|
|
}
|
|
else if (((uint)ch) >= 10)
|
|
return false;
|
|
value = value * 10 + ch;
|
|
offset++;
|
|
}
|
|
// Ten digits w/out a decimal point might've overflowed the int
|
|
if (count == 10)
|
|
return false;
|
|
if (negative)
|
|
result = -value;
|
|
else
|
|
result = value;
|
|
return true;
|
|
}
|
|
|
|
static int ToInt32D2(byte[] chars, int offset)
|
|
{
|
|
byte ch1 = (byte)(chars[offset + 0] - '0');
|
|
byte ch2 = (byte)(chars[offset + 1] - '0');
|
|
if (ch1 > 9 || ch2 > 9)
|
|
return -1;
|
|
return 10 * ch1 + ch2;
|
|
}
|
|
|
|
static int ToInt32D4(byte[] chars, int offset, int count)
|
|
{
|
|
return ToInt32D7(chars, offset, count);
|
|
}
|
|
|
|
static int ToInt32D7(byte[] chars, int offset, int count)
|
|
{
|
|
int value = 0;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
byte ch = (byte)(chars[offset + i] - '0');
|
|
if (ch > 9)
|
|
return -1;
|
|
value = value * 10 + ch;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static bool TryParseDateTime(byte[] chars, int offset, int count, out DateTime result)
|
|
{
|
|
int offsetMax = offset + count;
|
|
result = DateTime.MaxValue;
|
|
|
|
if (count < 19)
|
|
return false;
|
|
|
|
// 1 2 3
|
|
// 012345678901234567890123456789012
|
|
// "yyyy-MM-ddTHH:mm:ss"
|
|
// "yyyy-MM-ddTHH:mm:ss.fffffff"
|
|
// "yyyy-MM-ddTHH:mm:ss.fffffffZ"
|
|
// "yyyy-MM-ddTHH:mm:ss.fffffff+xx:yy"
|
|
// "yyyy-MM-ddTHH:mm:ss.fffffff-xx:yy"
|
|
if (chars[offset + 4] != '-' || chars[offset + 7] != '-' || chars[offset + 10] != 'T' ||
|
|
chars[offset + 13] != ':' || chars[offset + 16] != ':')
|
|
return false;
|
|
|
|
int year = ToInt32D4(chars, offset + 0, 4);
|
|
int month = ToInt32D2(chars, offset + 5);
|
|
int day = ToInt32D2(chars, offset + 8);
|
|
int hour = ToInt32D2(chars, offset + 11);
|
|
int minute = ToInt32D2(chars, offset + 14);
|
|
int second = ToInt32D2(chars, offset + 17);
|
|
|
|
if ((year | month | day | hour | minute | second) < 0)
|
|
return false;
|
|
|
|
DateTimeKind kind = DateTimeKind.Unspecified;
|
|
offset += 19;
|
|
|
|
int ticks = 0;
|
|
if (offset < offsetMax && chars[offset] == '.')
|
|
{
|
|
offset++;
|
|
int digitOffset = offset;
|
|
while (offset < offsetMax)
|
|
{
|
|
byte ch = chars[offset];
|
|
if (ch < '0' || ch > '9')
|
|
break;
|
|
offset++;
|
|
}
|
|
int digitCount = offset - digitOffset;
|
|
if (digitCount < 1 || digitCount > 7)
|
|
return false;
|
|
ticks = ToInt32D7(chars, digitOffset, digitCount);
|
|
if (ticks < 0)
|
|
return false;
|
|
for (int i = digitCount; i < 7; ++i)
|
|
ticks *= 10;
|
|
}
|
|
|
|
bool isLocal = false;
|
|
int hourDelta = 0;
|
|
int minuteDelta = 0;
|
|
if (offset < offsetMax)
|
|
{
|
|
byte ch = chars[offset];
|
|
if (ch == 'Z')
|
|
{
|
|
offset++;
|
|
kind = DateTimeKind.Utc;
|
|
}
|
|
else if (ch == '+' || ch == '-')
|
|
{
|
|
offset++;
|
|
if (offset + 5 > offsetMax || chars[offset + 2] != ':')
|
|
return false;
|
|
kind = DateTimeKind.Utc;
|
|
isLocal = true;
|
|
hourDelta = ToInt32D2(chars, offset);
|
|
minuteDelta = ToInt32D2(chars, offset + 3);
|
|
if ((hourDelta | minuteDelta) < 0)
|
|
return false;
|
|
if (ch == '+')
|
|
{
|
|
hourDelta = -hourDelta;
|
|
minuteDelta = -minuteDelta;
|
|
}
|
|
offset += 5;
|
|
}
|
|
}
|
|
if (offset < offsetMax)
|
|
return false;
|
|
|
|
DateTime value;
|
|
try
|
|
{
|
|
value = new DateTime(year, month, day, hour, minute, second, kind);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ticks > 0)
|
|
{
|
|
value = value.AddTicks(ticks);
|
|
}
|
|
if (isLocal)
|
|
{
|
|
try
|
|
{
|
|
TimeSpan ts = new TimeSpan(hourDelta, minuteDelta, 0);
|
|
if (hourDelta >= 0 && (value < DateTime.MaxValue - ts) ||
|
|
hourDelta < 0 && (value > DateTime.MinValue - ts))
|
|
{
|
|
value = value.Add(ts).ToLocalTime();
|
|
}
|
|
else
|
|
{
|
|
value = value.ToLocalTime().Add(ts);
|
|
}
|
|
}
|
|
catch (ArgumentOutOfRangeException) // Overflow
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
result = value;
|
|
return true;
|
|
}
|
|
|
|
static public int ToChars(bool value, byte[] buffer, int offset)
|
|
{
|
|
if (value)
|
|
{
|
|
buffer[offset + 0] = (byte)'t';
|
|
buffer[offset + 1] = (byte)'r';
|
|
buffer[offset + 2] = (byte)'u';
|
|
buffer[offset + 3] = (byte)'e';
|
|
return 4;
|
|
}
|
|
else
|
|
{
|
|
buffer[offset + 0] = (byte)'f';
|
|
buffer[offset + 1] = (byte)'a';
|
|
buffer[offset + 2] = (byte)'l';
|
|
buffer[offset + 3] = (byte)'s';
|
|
buffer[offset + 4] = (byte)'e';
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
// Works left from offset
|
|
static public int ToCharsR(int value, byte[] chars, int offset)
|
|
{
|
|
int count = 0;
|
|
if (value >= 0)
|
|
{
|
|
while (value >= 10)
|
|
{
|
|
int valueDiv10 = value / 10;
|
|
count++;
|
|
chars[--offset] = (byte)('0' + (value - valueDiv10 * 10));
|
|
value = valueDiv10;
|
|
}
|
|
chars[--offset] = (byte)('0' + value);
|
|
count++;
|
|
}
|
|
else
|
|
{
|
|
while (value <= -10)
|
|
{
|
|
int valueDiv10 = value / 10;
|
|
count++;
|
|
chars[--offset] = (byte)('0' - (value - valueDiv10 * 10));
|
|
value = valueDiv10;
|
|
}
|
|
chars[--offset] = (byte)('0' - value);
|
|
chars[--offset] = (byte)'-';
|
|
count += 2;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static public int ToChars(int value, byte[] chars, int offset)
|
|
{
|
|
int count = ToCharsR(value, chars, offset + MaxInt32Chars);
|
|
Buffer.BlockCopy(chars, offset + MaxInt32Chars - count, chars, offset, count);
|
|
return count;
|
|
}
|
|
|
|
static public int ToCharsR(long value, byte[] chars, int offset)
|
|
{
|
|
int count = 0;
|
|
if (value >= 0)
|
|
{
|
|
while (value > int.MaxValue)
|
|
{
|
|
long valueDiv10 = value / 10;
|
|
count++;
|
|
chars[--offset] = (byte)('0' + (int)(value - valueDiv10 * 10));
|
|
value = valueDiv10;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (value < int.MinValue)
|
|
{
|
|
long valueDiv10 = value / 10;
|
|
count++;
|
|
chars[--offset] = (byte)('0' - (int)(value - valueDiv10 * 10));
|
|
value = valueDiv10;
|
|
}
|
|
}
|
|
Fx.Assert(value >= int.MinValue && value <= int.MaxValue, "");
|
|
return count + ToCharsR((int)value, chars, offset);
|
|
}
|
|
|
|
static public int ToChars(long value, byte[] chars, int offset)
|
|
{
|
|
int count = ToCharsR(value, chars, offset + MaxInt64Chars);
|
|
Buffer.BlockCopy(chars, offset + MaxInt64Chars - count, chars, offset, count);
|
|
return count;
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
static unsafe bool IsNegativeZero(float value)
|
|
{
|
|
// Simple equals function will report that -0 is equal to +0, so compare bits instead
|
|
float negativeZero = -0e0F;
|
|
return (*(Int32*)&value == *(Int32*)&negativeZero);
|
|
}
|
|
|
|
[Fx.Tag.SecurityNote(Critical = "Contains unsafe code.",
|
|
Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
|
|
[SecuritySafeCritical]
|
|
static unsafe bool IsNegativeZero(double value)
|
|
{
|
|
// Simple equals function will report that -0 is equal to +0, so compare bits instead
|
|
double negativeZero = -0e0;
|
|
return (*(Int64*)&value == *(Int64*)&negativeZero);
|
|
}
|
|
|
|
static int ToInfinity(bool isNegative, byte[] buffer, int offset)
|
|
{
|
|
if (isNegative)
|
|
{
|
|
buffer[offset + 0] = (byte)'-';
|
|
buffer[offset + 1] = (byte)'I';
|
|
buffer[offset + 2] = (byte)'N';
|
|
buffer[offset + 3] = (byte)'F';
|
|
return 4;
|
|
}
|
|
else
|
|
{
|
|
buffer[offset + 0] = (byte)'I';
|
|
buffer[offset + 1] = (byte)'N';
|
|
buffer[offset + 2] = (byte)'F';
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
static int ToZero(bool isNegative, byte[] buffer, int offset)
|
|
{
|
|
if (isNegative)
|
|
{
|
|
buffer[offset + 0] = (byte)'-';
|
|
buffer[offset + 1] = (byte)'0';
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
buffer[offset] = (byte)'0';
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static public int ToChars(double value, byte[] buffer, int offset)
|
|
{
|
|
if (double.IsInfinity(value))
|
|
return ToInfinity(double.IsNegativeInfinity(value), buffer, offset);
|
|
if (value == 0.0)
|
|
return ToZero(IsNegativeZero(value), buffer, offset);
|
|
return ToAsciiChars(value.ToString("R", NumberFormatInfo.InvariantInfo), buffer, offset);
|
|
}
|
|
|
|
static public int ToChars(float value, byte[] buffer, int offset)
|
|
{
|
|
if (float.IsInfinity(value))
|
|
return ToInfinity(float.IsNegativeInfinity(value), buffer, offset);
|
|
if (value == 0.0)
|
|
return ToZero(IsNegativeZero(value), buffer, offset);
|
|
return ToAsciiChars(value.ToString("R", NumberFormatInfo.InvariantInfo), buffer, offset);
|
|
}
|
|
|
|
static public int ToChars(decimal value, byte[] buffer, int offset)
|
|
{
|
|
return ToAsciiChars(value.ToString(null, NumberFormatInfo.InvariantInfo), buffer, offset);
|
|
}
|
|
|
|
static public int ToChars(UInt64 value, byte[] buffer, int offset)
|
|
{
|
|
return ToAsciiChars(value.ToString(null, NumberFormatInfo.InvariantInfo), buffer, offset);
|
|
}
|
|
|
|
static int ToAsciiChars(string s, byte[] buffer, int offset)
|
|
{
|
|
for (int i = 0; i < s.Length; i++)
|
|
{
|
|
Fx.Assert(s[i] < 128, "");
|
|
buffer[offset++] = (byte)s[i];
|
|
}
|
|
return s.Length;
|
|
}
|
|
|
|
static int ToCharsD2(int value, byte[] chars, int offset)
|
|
{
|
|
Fx.Assert(value >= 0 && value < 100, "");
|
|
if (value < 10)
|
|
{
|
|
chars[offset + 0] = (byte)'0';
|
|
chars[offset + 1] = (byte)('0' + value);
|
|
}
|
|
else
|
|
{
|
|
int valueDiv10 = value / 10;
|
|
chars[offset + 0] = (byte)('0' + valueDiv10);
|
|
chars[offset + 1] = (byte)('0' + value - valueDiv10 * 10);
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
static int ToCharsD4(int value, byte[] chars, int offset)
|
|
{
|
|
Fx.Assert(value >= 0 && value < 10000, "");
|
|
ToCharsD2(value / 100, chars, offset + 0);
|
|
ToCharsD2(value % 100, chars, offset + 2);
|
|
return 4;
|
|
}
|
|
|
|
static int ToCharsD7(int value, byte[] chars, int offset)
|
|
{
|
|
Fx.Assert(value >= 0 && value < 10000000, "");
|
|
int zeroCount = 7 - ToCharsR(value, chars, offset + 7);
|
|
for (int i = 0; i < zeroCount; i++)
|
|
chars[offset + i] = (byte)'0';
|
|
int count = 7;
|
|
while (count > 0 && chars[offset + count - 1] == '0')
|
|
count--;
|
|
return count;
|
|
}
|
|
|
|
static public int ToChars(DateTime value, byte[] chars, int offset)
|
|
{
|
|
const long TicksPerMillisecond = 10000;
|
|
const long TicksPerSecond = TicksPerMillisecond * 1000;
|
|
int offsetMin = offset;
|
|
// "yyyy-MM-ddTHH:mm:ss.fffffff";
|
|
offset += ToCharsD4(value.Year, chars, offset);
|
|
chars[offset++] = (byte)'-';
|
|
offset += ToCharsD2(value.Month, chars, offset);
|
|
chars[offset++] = (byte)'-';
|
|
offset += ToCharsD2(value.Day, chars, offset);
|
|
chars[offset++] = (byte)'T';
|
|
offset += ToCharsD2(value.Hour, chars, offset);
|
|
chars[offset++] = (byte)':';
|
|
offset += ToCharsD2(value.Minute, chars, offset);
|
|
chars[offset++] = (byte)':';
|
|
offset += ToCharsD2(value.Second, chars, offset);
|
|
int ms = (int)(value.Ticks % TicksPerSecond);
|
|
if (ms != 0)
|
|
{
|
|
chars[offset++] = (byte)'.';
|
|
offset += ToCharsD7(ms, chars, offset);
|
|
}
|
|
switch (value.Kind)
|
|
{
|
|
case DateTimeKind.Unspecified:
|
|
break;
|
|
case DateTimeKind.Local:
|
|
// +"zzzzzz";
|
|
TimeSpan ts = TimeZoneInfo.Local.GetUtcOffset(value);
|
|
if (ts.Ticks < 0)
|
|
chars[offset++] = (byte)'-';
|
|
else
|
|
chars[offset++] = (byte)'+';
|
|
offset += ToCharsD2(Math.Abs(ts.Hours), chars, offset);
|
|
chars[offset++] = (byte)':';
|
|
offset += ToCharsD2(Math.Abs(ts.Minutes), chars, offset);
|
|
break;
|
|
case DateTimeKind.Utc:
|
|
// +"Z"
|
|
chars[offset++] = (byte)'Z';
|
|
break;
|
|
default:
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
}
|
|
return offset - offsetMin;
|
|
}
|
|
|
|
static public bool IsWhitespace(string s)
|
|
{
|
|
for (int i = 0; i < s.Length; i++)
|
|
{
|
|
if (!IsWhitespace(s[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static public bool IsWhitespace(char ch)
|
|
{
|
|
return (ch <= ' ' && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'));
|
|
}
|
|
|
|
static public string StripWhitespace(string s)
|
|
{
|
|
int count = s.Length;
|
|
for (int i = 0; i < s.Length; i++)
|
|
{
|
|
if (IsWhitespace(s[i]))
|
|
{
|
|
count--;
|
|
}
|
|
}
|
|
if (count == s.Length)
|
|
return s;
|
|
char[] chars = new char[count];
|
|
count = 0;
|
|
for (int i = 0; i < s.Length; i++)
|
|
{
|
|
char ch = s[i];
|
|
if (!IsWhitespace(ch))
|
|
{
|
|
chars[count++] = ch;
|
|
}
|
|
}
|
|
return new string(chars);
|
|
}
|
|
|
|
static string Trim(string s)
|
|
{
|
|
int i;
|
|
for (i = 0; i < s.Length && IsWhitespace(s[i]); i++);
|
|
|
|
int j;
|
|
for (j = s.Length; j > 0 && IsWhitespace(s[j - 1]); j--);
|
|
|
|
if (i == 0 && j == s.Length)
|
|
return s;
|
|
else if (j == 0)
|
|
return string.Empty;
|
|
else
|
|
return s.Substring(i, j - i);
|
|
}
|
|
}
|
|
}
|