diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 9749c905..297651e3 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -44,6 +44,11 @@ using NUnit.Framework; #endif using Newtonsoft.Json; using System.IO; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif using System.Xml; using Newtonsoft.Json.Utilities; @@ -1168,6 +1173,74 @@ third line", jsonTextReader.Value); Assert.AreEqual("type", reader.Value); } + public class FakeBufferPool : IJsonBufferPool + { + public readonly IList FreeBuffers = new List(); + public readonly IList UsedBuffers = new List(); + + public char[] RentBuffer(int minSize) + { + char[] buffer = FreeBuffers.FirstOrDefault(b => b.Length >= minSize); + if (buffer != null) + { + FreeBuffers.Remove(buffer); + UsedBuffers.Add(buffer); + + return buffer; + } + + buffer = new char[minSize]; + UsedBuffers.Add(buffer); + + return buffer; + } + + public void ReturnBuffer(ref char[] buffer) + { + UsedBuffers.Remove(buffer); + if (!FreeBuffers.Contains(buffer)) + { + FreeBuffers.Add(buffer); + } + + buffer = null; + } + } + + [Test] + public void BufferTest() + { + string json = @"{ + ""CPU"": ""Intel"", + ""Description"": ""Amazing!\nBuy now!"", + ""Drives"": [ + ""DVD read/writer"", + ""500 gigabyte hard drive"" + ] + }"; + + FakeBufferPool bufferPool = new FakeBufferPool(); + + for (int i = 0; i < 1000; i++) + { + using (JsonTextReader reader = new JsonTextReader(new StringReader(json))) + { + reader.BufferPool = bufferPool; + + while (reader.Read()) + { + } + } + + if ((i + 1) % 100 == 0) + { + Console.WriteLine("Allocated buffers: " + bufferPool.FreeBuffers.Count); + } + } + + Assert.AreEqual(2, bufferPool.FreeBuffers.Count); + } + [Test] public void ReadNullTerminatorStrings() { diff --git a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs index 248b9994..6ca293cb 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs @@ -1271,7 +1271,7 @@ _____'propertyName': NaN, { StringWriter swNew = new StringWriter(); char[] buffer = null; - JavaScriptUtils.WriteEscapedJavaScriptString(swNew, c.ToString(), '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, ref buffer); + JavaScriptUtils.WriteEscapedJavaScriptString(swNew, c.ToString(), '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer); StringWriter swOld = new StringWriter(); WriteEscapedJavaScriptStringOld(swOld, c.ToString(), '"', true); diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 131a46bc..8d56f959 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -430,7 +430,7 @@ If attributes are not mentioned, default values are used in each case. using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(text) ?? 16)) { char[] buffer = null; - JavaScriptUtils.WriteEscapedJavaScriptString(w, text, '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, ref buffer); + JavaScriptUtils.WriteEscapedJavaScriptString(w, text, '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer); } } diff --git a/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs b/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs index 49fe1aab..515a344b 100644 --- a/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs +++ b/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs @@ -136,5 +136,60 @@ namespace Newtonsoft.Json.Tests.Utilities } } } + + [Test] + public void HexParse() + { + HexParseSame("0000"); + HexParseSame("1234"); + HexParseSame("4321"); + HexParseSame("abcd"); + HexParseSame("dcba"); + HexParseSame("ffff"); + HexParseSame("ABCD"); + HexParseSame("DCBA"); + HexParseSame("FFFF"); + } + + [Test] + public void HexParseOffset() + { + int value = ConvertUtils.HexTextToInt("!0000".ToCharArray(), 1, 5); + Assert.AreEqual(0, value); + } + + [Test] + public void HexParseError() + { + ExceptionAssert.Throws(() => + { + ConvertUtils.HexTextToInt("-100".ToCharArray(), 0, 4); + }, "Invalid hex character: -"); + ExceptionAssert.Throws(() => + { + ConvertUtils.HexTextToInt("000g".ToCharArray(), 0, 4); + }, "Invalid hex character: g"); + ExceptionAssert.Throws(() => + { + ConvertUtils.HexTextToInt(" ssd".ToCharArray(), 0, 4); + }, "Invalid hex character: "); + ExceptionAssert.Throws(() => + { + ConvertUtils.HexTextToInt("000:".ToCharArray(), 0, 4); + }, "Invalid hex character: :"); + ExceptionAssert.Throws(() => + { + ConvertUtils.HexTextToInt("000G".ToCharArray(), 0, 4); + }, "Invalid hex character: G"); + } + + private void HexParseSame(string text) + { + int v1 = int.Parse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + + int v2 = ConvertUtils.HexTextToInt(text.ToCharArray(), 0, 4); + + Assert.AreEqual(v1, v2, "Invalid result when parsing hex text: " + text); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/project.json b/Src/Newtonsoft.Json.Tests/project.json index b80ebb6e..e16136e2 100644 --- a/Src/Newtonsoft.Json.Tests/project.json +++ b/Src/Newtonsoft.Json.Tests/project.json @@ -3,7 +3,9 @@ "exclude": [ ], "dependencies": { - "Newtonsoft.Json": "" + "Newtonsoft.Json": { + "target": "project" + } }, "commands": { "test": "xunit.runner.dnx -parallel none" diff --git a/Src/Newtonsoft.Json/IJsonBufferPool.cs b/Src/Newtonsoft.Json/IJsonBufferPool.cs new file mode 100644 index 00000000..36d4b47d --- /dev/null +++ b/Src/Newtonsoft.Json/IJsonBufferPool.cs @@ -0,0 +1,22 @@ +namespace Newtonsoft.Json +{ + /// + /// Provides an interface for using pooled buffers. + /// + /// The buffer type content. + public interface IJsonBufferPool + { + /// + /// Rent a buffer from the pool. This buffer must be returned when it is no longer needed. + /// + /// The minimum required size of the buffer. The returned buffer may be larger. + /// The rented buffer from the pool. + T[] RentBuffer(int minSize); + + /// + /// Return a buffer to the pool. + /// + /// The buffer that is being returned. + void ReturnBuffer(ref T[] buffer); + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index f77ff23e..5e1a3861 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -173,7 +173,15 @@ namespace Newtonsoft.Json public DateTimeZoneHandling DateTimeZoneHandling { get { return _dateTimeZoneHandling; } - set { _dateTimeZoneHandling = value; } + set + { + if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind) + { + throw new ArgumentOutOfRangeException("value"); + } + + _dateTimeZoneHandling = value; + } } /// @@ -182,7 +190,21 @@ namespace Newtonsoft.Json public DateParseHandling DateParseHandling { get { return _dateParseHandling; } - set { _dateParseHandling = value; } + set + { + if (value < DateParseHandling.None || +#if !NET20 + value > DateParseHandling.DateTimeOffset +#else + value > DateParseHandling.DateTime +#endif + ) + { + throw new ArgumentOutOfRangeException("value"); + } + + _dateParseHandling = value; + } } /// @@ -191,7 +213,15 @@ namespace Newtonsoft.Json public FloatParseHandling FloatParseHandling { get { return _floatParseHandling; } - set { _floatParseHandling = value; } + set + { + if (value < FloatParseHandling.Double || value > FloatParseHandling.Decimal) + { + throw new ArgumentOutOfRangeException("value"); + } + + _floatParseHandling = value; + } } /// diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index c02c7799..c274d903 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -54,7 +54,6 @@ namespace Newtonsoft.Json { private const char UnicodeReplacementChar = '\uFFFD'; private const int MaximumJavascriptIntegerCharacterLength = 380; - private readonly TextReader _reader; private char[] _chars; private int _charsUsed; @@ -62,8 +61,9 @@ namespace Newtonsoft.Json private int _lineStartPos; private int _lineNumber; private bool _isEndOfFile; - private StringBuffer _buffer; + private StringBuffer _stringBuffer; private StringReference _stringReference; + private IJsonBufferPool _bufferPool; internal PropertyNameTable NameTable; /// @@ -77,7 +77,6 @@ namespace Newtonsoft.Json _reader = reader; _lineNumber = 1; - _chars = new char[1025]; } #if DEBUG @@ -87,18 +86,29 @@ namespace Newtonsoft.Json } #endif - private StringBuffer GetBuffer() + /// + /// Gets or sets the reader's character buffer pool. + /// + public IJsonBufferPool BufferPool { - if (_buffer == null) + get { return _bufferPool; } + set { - _buffer = new StringBuffer(1025); - } - else - { - _buffer.Position = 0; - } + if (value == null) + { + throw new ArgumentNullException("value"); + } - return _buffer; + _bufferPool = value; + } + } + + private void EnsureBufferNotEmpty() + { + if (_stringBuffer.IsEmpty) + { + _stringBuffer = new StringBuffer(_bufferPool, 1025); + } } private void OnNewLine(int pos) @@ -214,10 +224,12 @@ namespace Newtonsoft.Json int newArrayLength = Math.Max(_chars.Length * 2, _charsUsed + charsRequired + 1); // increase the size of the buffer - char[] dst = new char[newArrayLength]; + char[] dst = BufferUtils.RentBuffer(_bufferPool, newArrayLength); BlockCopyChars(_chars, 0, dst, 0, _chars.Length); + BufferUtils.ReturnBuffer(_bufferPool, ref _chars); + _chars = dst; } else @@ -227,11 +239,13 @@ namespace Newtonsoft.Json if (remainingCharCount + charsRequired + 1 >= _chars.Length) { // the remaining count plus the required is bigger than the current buffer size - char[] dst = new char[remainingCharCount + charsRequired + 1]; + char[] dst = BufferUtils.RentBuffer(_bufferPool, remainingCharCount + charsRequired + 1); if (remainingCharCount > 0) BlockCopyChars(_chars, _charPos, dst, 0, remainingCharCount); + BufferUtils.ReturnBuffer(_bufferPool, ref _chars); + _chars = dst; } else @@ -374,6 +388,11 @@ namespace Newtonsoft.Json internal override bool ReadInternal() { + if (_chars == null) + { + _chars = BufferUtils.RentBuffer(_bufferPool, 1024); + } + while (true) { switch (_currentState) @@ -422,7 +441,7 @@ namespace Newtonsoft.Json int charPos = _charPos; int initialPosition = _charPos; int lastWritePosition = _charPos; - StringBuffer buffer = null; + _stringBuffer.Position = 0; while (true) { @@ -530,10 +549,9 @@ namespace Newtonsoft.Json highSurrogate = UnicodeReplacementChar; } - if (buffer == null) - buffer = GetBuffer(); + EnsureBufferNotEmpty(); - WriteCharToBuffer(buffer, highSurrogate, lastWritePosition, escapeStartPos); + WriteCharToBuffer(highSurrogate, lastWritePosition, escapeStartPos); lastWritePosition = _charPos; } else @@ -553,10 +571,8 @@ namespace Newtonsoft.Json throw JsonReaderException.Create(this, "Bad JSON escape sequence: {0}.".FormatWith(CultureInfo.InvariantCulture, @"\" + currentChar)); } - if (buffer == null) - buffer = GetBuffer(); - - WriteCharToBuffer(buffer, writeChar, lastWritePosition, escapeStartPos); + EnsureBufferNotEmpty(); + WriteCharToBuffer(writeChar, lastWritePosition, escapeStartPos); lastWritePosition = charPos; break; @@ -582,13 +598,14 @@ namespace Newtonsoft.Json } else { - if (buffer == null) - buffer = GetBuffer(); + EnsureBufferNotEmpty(); if (charPos > lastWritePosition) - buffer.Append(_chars, lastWritePosition, charPos - lastWritePosition); + { + _stringBuffer.Append(_bufferPool, _chars, lastWritePosition, charPos - lastWritePosition); + } - _stringReference = new StringReference(buffer.GetInternalBuffer(), 0, buffer.Position); + _stringReference = new StringReference(_stringBuffer.InternalBuffer, 0, _stringBuffer.Position); } charPos++; @@ -600,14 +617,14 @@ namespace Newtonsoft.Json } } - private void WriteCharToBuffer(StringBuffer buffer, char writeChar, int lastWritePosition, int writeToPosition) + private void WriteCharToBuffer(char writeChar, int lastWritePosition, int writeToPosition) { if (writeToPosition > lastWritePosition) { - buffer.Append(_chars, lastWritePosition, writeToPosition - lastWritePosition); + _stringBuffer.Append(_bufferPool, _chars, lastWritePosition, writeToPosition - lastWritePosition); } - buffer.Append(writeChar); + _stringBuffer.Append(_bufferPool, writeChar); } private char ParseUnicode() @@ -615,8 +632,7 @@ namespace Newtonsoft.Json char writeChar; if (EnsureChars(4, true)) { - string hexValues = new string(_chars, _charPos, 4); - char hexChar = Convert.ToChar(int.Parse(hexValues, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); + char hexChar = Convert.ToChar(ConvertUtils.HexTextToInt(_chars, _charPos, _charPos + 4)); writeChar = hexChar; _charPos += 4; @@ -699,9 +715,7 @@ namespace Newtonsoft.Json private void ClearRecentString() { - if (_buffer != null) - _buffer.Position = 0; - + _stringBuffer.Position = 0; _stringReference = new StringReference(); } @@ -1623,6 +1637,11 @@ namespace Newtonsoft.Json { base.Close(); + if (_chars != null) + { + BufferUtils.ReturnBuffer(_bufferPool, ref _chars); + } + if (CloseInput && _reader != null) { #if !(DOTNET || PORTABLE40 || PORTABLE) @@ -1632,8 +1651,7 @@ namespace Newtonsoft.Json #endif } - if (_buffer != null) - _buffer.Clear(); + _stringBuffer.Clear(_bufferPool); } /// diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index 9e29ec8c..17b5da40 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -49,6 +49,7 @@ namespace Newtonsoft.Json private bool _quoteName; private bool[] _charEscapeFlags; private char[] _writeBuffer; + private IJsonBufferPool _bufferPool; private char[] _indentChars; private Base64Encoder Base64Encoder @@ -62,6 +63,23 @@ namespace Newtonsoft.Json } } + /// + /// Gets or sets the writer's character buffer pool. + /// + public IJsonBufferPool BufferPool + { + get { return _bufferPool; } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + _bufferPool = value; + } + } + /// /// Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. /// @@ -151,6 +169,8 @@ namespace Newtonsoft.Json { base.Close(); + BufferUtils.ReturnBuffer(_bufferPool, ref _writeBuffer); + if (CloseOutput && _writer != null) { #if !(DOTNET || PORTABLE40 || PORTABLE) @@ -380,7 +400,7 @@ namespace Newtonsoft.Json private void WriteEscapedString(string value, bool quote) { EnsureWriteBuffer(); - JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, quote, _charEscapeFlags, StringEscapeHandling, ref _writeBuffer); + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, quote, _charEscapeFlags, StringEscapeHandling, _bufferPool, ref _writeBuffer); } /// @@ -714,7 +734,10 @@ namespace Newtonsoft.Json private void EnsureWriteBuffer() { if (_writeBuffer == null) - _writeBuffer = new char[35]; // maximum buffer sized used when writing iso date + { + // maximum buffer sized used when writing iso date + _writeBuffer = BufferUtils.RentBuffer(_bufferPool, 35); + } } private void WriteIntegerValue(long value) diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 80a32cd3..dfc3e494 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -222,7 +222,15 @@ namespace Newtonsoft.Json public Formatting Formatting { get { return _formatting; } - set { _formatting = value; } + set + { + if (value < Formatting.None || value > Formatting.Indented) + { + throw new ArgumentOutOfRangeException("value"); + } + + _formatting = value; + } } /// @@ -231,7 +239,15 @@ namespace Newtonsoft.Json public DateFormatHandling DateFormatHandling { get { return _dateFormatHandling; } - set { _dateFormatHandling = value; } + set + { + if (value < DateFormatHandling.IsoDateFormat || value > DateFormatHandling.MicrosoftDateFormat) + { + throw new ArgumentOutOfRangeException("value"); + } + + _dateFormatHandling = value; + } } /// @@ -240,7 +256,15 @@ namespace Newtonsoft.Json public DateTimeZoneHandling DateTimeZoneHandling { get { return _dateTimeZoneHandling; } - set { _dateTimeZoneHandling = value; } + set + { + if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind) + { + throw new ArgumentOutOfRangeException("value"); + } + + _dateTimeZoneHandling = value; + } } /// @@ -251,6 +275,11 @@ namespace Newtonsoft.Json get { return _stringEscapeHandling; } set { + if (value < StringEscapeHandling.Default || value > StringEscapeHandling.EscapeHtml) + { + throw new ArgumentOutOfRangeException("value"); + } + _stringEscapeHandling = value; OnStringEscapeHandlingChanged(); } @@ -269,7 +298,15 @@ namespace Newtonsoft.Json public FloatFormatHandling FloatFormatHandling { get { return _floatFormatHandling; } - set { _floatFormatHandling = value; } + set + { + if (value < FloatFormatHandling.String || value > FloatFormatHandling.DefaultValue) + { + throw new ArgumentOutOfRangeException("value"); + } + + _floatFormatHandling = value; + } } /// diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 6cb2e837..b021c39f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -103,6 +103,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 5cad145e..992a1bc1 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -116,6 +116,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index 6faff383..c179f238 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -113,6 +113,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index 8cec877a..958ebb72 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -77,6 +77,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index 8119592e..38753c02 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -82,6 +82,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 422963fe..cb3996a4 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -81,6 +81,7 @@ + diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 4ff2fa59..8f05675e 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -858,5 +858,35 @@ namespace Newtonsoft.Json.Utilities return Guid.TryParseExact(s, "D", out g); #endif } + + public static int HexTextToInt(char[] text, int start, int end) + { + int value = 0; + for (int i = start; i < end; i++) + { + value += HexCharToInt(text[i]) << ((end - 1 - i) * 4); + } + return value; + } + + private static int HexCharToInt(char ch) + { + if (ch <= 57 && ch >= 48) + { + return ch - 48; + } + + if (ch <= 70 && ch >= 65) + { + return ch - 55; + } + + if (ch <= 102 && ch >= 97) + { + return ch - 87; + } + + throw new FormatException("Invalid hex character: " + ch); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs index 383d83f5..c1eaebb0 100644 --- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -39,6 +39,46 @@ using System.Linq; namespace Newtonsoft.Json.Utilities { + internal static class BufferUtils + { + public static char[] RentBuffer(IJsonBufferPool bufferPool, int minSize) + { + if (bufferPool == null) + { + return new char[minSize]; + } + + return bufferPool.RentBuffer(minSize); + } + + public static void ReturnBuffer(IJsonBufferPool bufferPool, ref char[] buffer) + { + if (bufferPool == null) + { + buffer = null; + return; + } + + bufferPool.ReturnBuffer(ref buffer); + } + + public static void EnsureBufferSize(IJsonBufferPool bufferPool, int size, ref char[] buffer) + { + if (bufferPool == null) + { + buffer = new char[size]; + return; + } + + if (buffer != null) + { + bufferPool.ReturnBuffer(ref buffer); + } + + buffer = bufferPool.RentBuffer(size); + } + } + internal static class JavaScriptUtils { internal static readonly bool[] SingleQuoteCharEscapeFlags = new bool[128]; @@ -100,7 +140,7 @@ namespace Newtonsoft.Json.Utilities } public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters, - bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, ref char[] writeBuffer) + bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, IJsonBufferPool bufferPool, ref char[] writeBuffer) { // leading delimiter if (appendDelimiters) @@ -162,7 +202,9 @@ namespace Newtonsoft.Json.Utilities else { if (writeBuffer == null || writeBuffer.Length < UnicodeTextLength) - writeBuffer = new char[UnicodeTextLength]; + { + BufferUtils.EnsureBufferSize(bufferPool, UnicodeTextLength, ref writeBuffer); + } StringUtils.ToCharAsUnicode(c, writeBuffer); @@ -189,13 +231,15 @@ namespace Newtonsoft.Json.Utilities if (writeBuffer == null || writeBuffer.Length < length) { - char[] newBuffer = new char[length]; + char[] newBuffer = BufferUtils.RentBuffer(bufferPool, length); // the unicode text is already in the buffer // copy it over when creating new buffer if (isEscapedUnicodeText) Array.Copy(writeBuffer, newBuffer, UnicodeTextLength); + BufferUtils.ReturnBuffer(bufferPool, ref writeBuffer); + writeBuffer = newBuffer; } @@ -248,7 +292,7 @@ namespace Newtonsoft.Json.Utilities using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16)) { char[] buffer = null; - WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, charEscapeFlags, stringEscapeHandling, ref buffer); + WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, charEscapeFlags, stringEscapeHandling, null, ref buffer); return w.ToString(); } } diff --git a/Src/Newtonsoft.Json/Utilities/StringBuffer.cs b/Src/Newtonsoft.Json/Utilities/StringBuffer.cs index 05022391..b28f3fc6 100644 --- a/Src/Newtonsoft.Json/Utilities/StringBuffer.cs +++ b/Src/Newtonsoft.Json/Utilities/StringBuffer.cs @@ -30,60 +30,75 @@ namespace Newtonsoft.Json.Utilities /// /// Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. /// - internal class StringBuffer + internal struct StringBuffer { private char[] _buffer; private int _position; - private static readonly char[] EmptyBuffer = new char[0]; - public int Position { get { return _position; } set { _position = value; } } - public StringBuffer() + public bool IsEmpty { - _buffer = EmptyBuffer; + get { return _buffer == null; } } - public StringBuffer(int initalSize) + public StringBuffer(IJsonBufferPool bufferPool, int initalSize) : this(BufferUtils.RentBuffer(bufferPool, initalSize)) { - _buffer = new char[initalSize]; } - public void Append(char value) + private StringBuffer(char[] buffer) + { + _buffer = buffer; + _position = 0; + } + + public void Append(IJsonBufferPool bufferPool, char value) { // test if the buffer array is large enough to take the value if (_position == _buffer.Length) - EnsureSize(1); + { + EnsureSize(bufferPool, 1); + } // set value and increment poisition _buffer[_position++] = value; } - public void Append(char[] buffer, int startIndex, int count) + public void Append(IJsonBufferPool bufferPool, char[] buffer, int startIndex, int count) { if (_position + count >= _buffer.Length) - EnsureSize(count); + { + EnsureSize(bufferPool, count); + } Array.Copy(buffer, startIndex, _buffer, _position, count); _position += count; } - public void Clear() + public void Clear(IJsonBufferPool bufferPool) { - _buffer = EmptyBuffer; + if (_buffer != null) + { + BufferUtils.ReturnBuffer(bufferPool, ref _buffer); + _buffer = null; + } _position = 0; } - private void EnsureSize(int appendLength) + private void EnsureSize(IJsonBufferPool bufferPool, int appendLength) { - char[] newBuffer = new char[(_position + appendLength) * 2]; + char[] newBuffer = BufferUtils.RentBuffer(bufferPool, (_position + appendLength) * 2); - Array.Copy(_buffer, newBuffer, _position); + if (_buffer != null) + { + Array.Copy(_buffer, newBuffer, _position); + BufferUtils.ReturnBuffer(bufferPool, ref _buffer); + } _buffer = newBuffer; } @@ -99,9 +114,9 @@ namespace Newtonsoft.Json.Utilities return new string(_buffer, start, length); } - public char[] GetInternalBuffer() + public char[] InternalBuffer { - return _buffer; + get { return _buffer; } } } } \ No newline at end of file