-Reduced string allocations

This commit is contained in:
James Newton-King
2013-04-22 23:55:21 +12:00
parent c70eaaa004
commit 6d109a4c55
8 changed files with 176 additions and 79 deletions
@@ -1183,7 +1183,8 @@ _____'propertyName': NaN
Console.WriteLine("Position: " + (int)c);
StringWriter swNew = new StringWriter();
JavaScriptUtils.WriteEscapedJavaScriptString(swNew, c.ToString(), '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default);
char[] buffer = null;
JavaScriptUtils.WriteEscapedJavaScriptString(swNew, c.ToString(), '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, ref buffer);
StringWriter swOld = new StringWriter();
WriteEscapedJavaScriptStringOld(swOld, c.ToString(), '"', true);
@@ -324,7 +324,8 @@ If attributes are not mentioned, default values are used in each case.
{
using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(text) ?? 16))
{
JavaScriptUtils.WriteEscapedJavaScriptString(w, text, '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default);
char[] buffer = null;
JavaScriptUtils.WriteEscapedJavaScriptString(w, text, '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, ref buffer);
}
}
@@ -75,4 +75,4 @@ using System.Security;
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
[assembly: AssemblyFileVersion("5.0.4.16021")]
[assembly: AssemblyFileVersion("5.0.4.16022")]
+28 -21
View File
@@ -648,6 +648,11 @@ namespace Newtonsoft.Json
if (ReadData(true) == 0)
return;
}
else
{
_charPos = charPos - 1;
return;
}
break;
case '-':
case '+':
@@ -1187,7 +1192,7 @@ namespace Newtonsoft.Json
}
else
{
numberValue = ConvertUtils.IntParseFast(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
numberValue = ConvertUtils.Int32Parse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
}
numberType = JsonToken.Integer;
@@ -1238,11 +1243,17 @@ namespace Newtonsoft.Json
}
else
{
string number = _stringReference.ToString();
// it's faster to do 3 indexof with single characters than an indexofany
if (number.IndexOf('.') != -1 || number.IndexOf('E') != -1 || number.IndexOf('e') != -1)
long value;
ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value);
if (parseResult == ParseResult.Success)
{
numberValue = value;
numberType = JsonToken.Integer;
}
else if (parseResult == ParseResult.Invalid)
{
string number = _stringReference.ToString();
if (_floatParseHandling == FloatParseHandling.Decimal)
numberValue = decimal.Parse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
else
@@ -1250,24 +1261,20 @@ namespace Newtonsoft.Json
numberType = JsonToken.Float;
}
else if (parseResult == ParseResult.Overflow)
{
#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE40 || PORTABLE)
string number = _stringReference.ToString();
numberValue = BigInteger.Parse(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Integer;
#else
// todo - validate number was a valid integer to make sure overflow was the reason for failure
throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
#endif
}
else
{
long value;
if (long.TryParse(number, out value))
{
numberValue = value;
}
else
{
#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE40 || PORTABLE)
numberValue = BigInteger.Parse(number, CultureInfo.InvariantCulture);
#else
// todo - validate number was a valid integer to make sure overflow was the reason for failure
throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, number));
#endif
}
numberType = JsonToken.Integer;
throw JsonReaderException.Create(this, "Unknown error parsing integer.");
}
}
}
+9 -3
View File
@@ -214,7 +214,7 @@ namespace Newtonsoft.Json
{
InternalWritePropertyName(name);
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName, _charEscapeFlags, StringEscapeHandling);
WriteEscapedString(name);
_writer.Write(':');
}
@@ -230,7 +230,7 @@ namespace Newtonsoft.Json
if (escape)
{
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName, _charEscapeFlags, StringEscapeHandling);
WriteEscapedString(name);
}
else
{
@@ -364,7 +364,13 @@ namespace Newtonsoft.Json
if (value == null)
WriteValueInternal(JsonConvert.Null, JsonToken.Null);
else
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true, _charEscapeFlags, StringEscapeHandling);
WriteEscapedString(value);
}
private void WriteEscapedString(string value)
{
EnsureWriteBuffer();
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true, _charEscapeFlags, StringEscapeHandling, ref _writeBuffer);
}
/// <summary>
@@ -92,6 +92,6 @@ using System.Security;
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
[assembly: AssemblyFileVersion("5.0.4.16021")]
[assembly: AssemblyFileVersion("5.0.4.16022")]
[assembly: CLSCompliant(true)]
+98 -36
View File
@@ -98,6 +98,14 @@ namespace Newtonsoft.Json.Utilities
public PrimitiveTypeCode TypeCode { get; set; }
}
internal enum ParseResult
{
None,
Success,
Overflow,
Invalid
}
internal static class ConvertUtils
{
private static readonly Dictionary<Type, PrimitiveTypeCode> TypeCodeMap =
@@ -205,7 +213,7 @@ namespace Newtonsoft.Json.Utilities
#if !(NETFX_CORE || PORTABLE)
public static TypeInformation GetTypeInformation(IConvertible convertable)
{
TypeInformation typeInformation = PrimitiveTypeCodes[(int) convertable.GetTypeCode()];
TypeInformation typeInformation = PrimitiveTypeCodes[(int)convertable.GetTypeCode()];
return typeInformation;
}
#endif
@@ -224,7 +232,7 @@ namespace Newtonsoft.Json.Utilities
public static TimeSpan ParseTimeSpan(string input)
{
#if !(NET35 || NET20 || PORTABLE40)
return TimeSpan.Parse((string) input, CultureInfo.InvariantCulture);
return TimeSpan.Parse((string)input, CultureInfo.InvariantCulture);
#else
return TimeSpan.Parse((string)input);
#endif
@@ -316,14 +324,14 @@ namespace Newtonsoft.Json.Utilities
public static object FromBigInteger(BigInteger i, Type targetType)
{
if (targetType == typeof (decimal))
return (decimal) i;
if (targetType == typeof (double))
return (double) i;
if (targetType == typeof (float))
return (float) i;
if (targetType == typeof (ulong))
return (ulong) i;
if (targetType == typeof(decimal))
return (decimal)i;
if (targetType == typeof(double))
return (double)i;
if (targetType == typeof(float))
return (float)i;
if (targetType == typeof(ulong))
return (ulong)i;
try
{
@@ -337,6 +345,7 @@ namespace Newtonsoft.Json.Utilities
#endif
#region Convert
/// <summary>
/// Converts the value to the specified type.
/// </summary>
@@ -377,18 +386,18 @@ namespace Newtonsoft.Json.Utilities
#endif
if (initialValue is byte[] && targetType == typeof(Guid))
return new Guid((byte[]) initialValue);
return new Guid((byte[])initialValue);
if (initialValue is string)
{
if (targetType == typeof (Guid))
return new Guid((string) initialValue);
if (targetType == typeof (Uri))
return new Uri((string) initialValue, UriKind.RelativeOrAbsolute);
if (targetType == typeof(Guid))
return new Guid((string)initialValue);
if (targetType == typeof(Uri))
return new Uri((string)initialValue, UriKind.RelativeOrAbsolute);
if (targetType == typeof(TimeSpan))
return ParseTimeSpan((string) initialValue);
return ParseTimeSpan((string)initialValue);
if (typeof(Type).IsAssignableFrom(targetType))
return Type.GetType((string) initialValue, true);
return Type.GetType((string)initialValue, true);
}
#if !(NET20 || NET35 || SILVERLIGHT || PORTABLE40 || PORTABLE)
@@ -431,7 +440,7 @@ namespace Newtonsoft.Json.Utilities
{
if (ReflectionUtils.IsNullable(targetType))
return EnsureTypeAssignable(null, initialType, targetType);
throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType));
}
#endif
@@ -446,6 +455,7 @@ namespace Newtonsoft.Json.Utilities
#endregion
#region TryConvert
/// <summary>
/// Converts the value to the specified type.
/// </summary>
@@ -469,9 +479,11 @@ namespace Newtonsoft.Json.Utilities
return false;
}
}
#endregion
#region ConvertOrCast
/// <summary>
/// Converts the value to the specified type. If the value is unable to be converted, the
/// value is checked whether it assignable to the specified type.
@@ -498,6 +510,7 @@ namespace Newtonsoft.Json.Utilities
return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType);
}
#endregion
private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
@@ -567,12 +580,12 @@ namespace Newtonsoft.Json.Utilities
}
}
public static int IntParseFast(char[] value, int start, int length)
public static int Int32Parse(char[] chars, int start, int length)
{
if (length == 0)
throw new FormatException("Input string was not in a correct format.");
bool isNegative = (value[start] == '-');
bool isNegative = (chars[start] == '-');
if (isNegative)
{
@@ -584,27 +597,76 @@ namespace Newtonsoft.Json.Utilities
length--;
}
checked
int result = 0;
int end = start + length;
for (int i = start; i < end; i++)
{
int result = 0;
int end = start + length;
for (int i = start; i < end; i++)
{
char c = value[i];
int c = chars[i] - '0';
if (c < '0' || c > '9')
throw new FormatException("Input string was not in a correct format.");
if (c < 0 || c > 9)
throw new FormatException("Input string was not in a correct format.");
result = 10*result + -(c - 48);
}
int newValue = (10*result) - c;
// go from negative to positive to avoids overflow
// negative can be slightly bigger than positive
if (!isNegative)
result = -result;
// overflow has caused the number to loop around
if (newValue > result)
throw new OverflowException();
return result;
result = newValue;
}
// go from negative to positive to avoids overflow
// negative can be slightly bigger than positive
if (!isNegative)
result = -result;
return result;
}
public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value)
{
value = 0;
if (length == 0)
return ParseResult.Invalid;
bool isNegative = (chars[start] == '-');
if (isNegative)
{
// text just a negative sign
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int end = start + length;
for (int i = start; i < end; i++)
{
int c = chars[i] - '0';
if (c < 0 || c > 9)
return ParseResult.Invalid;
long newValue = (10*value) - c;
// overflow has caused the number to loop around
if (newValue > value)
return ParseResult.Overflow;
value = newValue;
}
// go from negative to positive to avoids overflow
// negative can be slightly bigger than positive
if (!isNegative)
value = -value;
return ParseResult.Success;
}
}
}
}
@@ -71,7 +71,8 @@ namespace Newtonsoft.Json.Utilities
private const string EscapedUnicodeText = "!";
public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters, bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling)
public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters,
bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, ref char[] writeBuffer)
{
// leading delimiter
if (appendDelimiters)
@@ -79,8 +80,6 @@ namespace Newtonsoft.Json.Utilities
if (s != null)
{
char[] chars = null;
char[] unicodeBuffer = null;
int lastWritePosition = 0;
for (int i = 0; i < s.Length; i++)
@@ -134,10 +133,10 @@ namespace Newtonsoft.Json.Utilities
}
else
{
if (unicodeBuffer == null)
unicodeBuffer = new char[6];
if (writeBuffer == null)
writeBuffer = new char[6];
StringUtils.ToCharAsUnicode(c, unicodeBuffer);
StringUtils.ToCharAsUnicode(c, writeBuffer);
// slightly hacky but it saves multiple conditions in if test
escapedValue = EscapedUnicodeText;
@@ -153,20 +152,36 @@ namespace Newtonsoft.Json.Utilities
if (escapedValue == null)
continue;
bool isEscapedUnicodeText = string.Equals(escapedValue, EscapedUnicodeText);
if (i > lastWritePosition)
{
if (chars == null)
chars = s.ToCharArray();
int length = i - lastWritePosition + ((isEscapedUnicodeText) ? 6 : 0);
int start = (isEscapedUnicodeText) ? 6 : 0;
if (writeBuffer == null || writeBuffer.Length < length)
{
char[] newBuffer = new char[length];
// the unicode text is already in the buffer
// copy it over when creating new buffer
if (isEscapedUnicodeText)
Array.Copy(writeBuffer, newBuffer, 6);
writeBuffer = newBuffer;
}
s.CopyTo(lastWritePosition, writeBuffer, start, length - start);
// write unchanged chars before writing escaped text
writer.Write(chars, lastWritePosition, i - lastWritePosition);
writer.Write(writeBuffer, start, length - start);
}
lastWritePosition = i + 1;
if (!string.Equals(escapedValue, EscapedUnicodeText))
if (!isEscapedUnicodeText)
writer.Write(escapedValue);
else
writer.Write(unicodeBuffer);
writer.Write(writeBuffer, 0, 6);
}
if (lastWritePosition == 0)
@@ -176,11 +191,15 @@ namespace Newtonsoft.Json.Utilities
}
else
{
if (chars == null)
chars = s.ToCharArray();
int length = s.Length - lastWritePosition;
if (writeBuffer == null || writeBuffer.Length < length)
writeBuffer = new char[length];
s.CopyTo(lastWritePosition, writeBuffer, 0, length);
// write remaining text
writer.Write(chars, lastWritePosition, s.Length - lastWritePosition);
writer.Write(writeBuffer, 0, length);
}
}
@@ -193,7 +212,8 @@ namespace Newtonsoft.Json.Utilities
{
using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16))
{
WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, (delimiter == '"') ? DoubleQuoteCharEscapeFlags : SingleQuoteCharEscapeFlags, StringEscapeHandling.Default);
char[] buffer = null;
WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, (delimiter == '"') ? DoubleQuoteCharEscapeFlags : SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, ref buffer);
return w.ToString();
}
}