diff --git a/Src/Newtonsoft.Json.TestConsole/Program.cs b/Src/Newtonsoft.Json.TestConsole/Program.cs index e6a2a515..3f5f8d79 100644 --- a/Src/Newtonsoft.Json.TestConsole/Program.cs +++ b/Src/Newtonsoft.Json.TestConsole/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -15,14 +16,35 @@ namespace Newtonsoft.Json.TestConsole Console.ReadKey(); Console.WriteLine("Doing stuff..."); - PerformanceTests t = new PerformanceTests(); + //PerformanceTests t = new PerformanceTests(); + //t.DeserializeLargeJson(); - //t.BenchmarkSerializeMethod(PerformanceTests.SerializeMethod.JsonNet, new { hello = "world" }); - t.DeserializeLargeJson(); + WriteLargeJson(); Console.WriteLine(); + Console.WriteLine("Finished"); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } + + public static void WriteLargeJson() + { + var json = System.IO.File.ReadAllText("large.json"); + + IList o = JsonConvert.DeserializeObject>(json); + + Console.WriteLine("Press any key to start serialize"); + Console.ReadKey(); + + for (int i = 0; i < 100; i++) + { + using (StreamWriter file = System.IO.File.CreateText("largewrite.json")) + { + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + serializer.Serialize(file, o); + } + } + } } } diff --git a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs index 809d2138..b780afa3 100644 --- a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("6.0.0.0")] [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.5.17725")] +[assembly: AssemblyFileVersion("6.0.5.17728")] diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index 731d9c37..50f606ae 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -82,6 +82,60 @@ namespace Newtonsoft.Json.Tests } } + public class NameTableTestClass + { + public string Value { get; set; } + } + + public class NameTableTestClassConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + reader.Read(); + reader.Read(); + + JsonTextReader jsonTextReader = (JsonTextReader)reader; + Assert.IsNotNull(jsonTextReader.NameTable); + + string s = serializer.Deserialize(reader); + Assert.AreEqual("hi", s); + Assert.IsNotNull(jsonTextReader.NameTable); + + NameTableTestClass o = new NameTableTestClass + { + Value = s + }; + + return o; + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(NameTableTestClass); + } + } + + [Test] + public void NameTableTest() + { + StringReader sr = new StringReader("{'property':'hi'}"); + JsonTextReader jsonTextReader = new JsonTextReader(sr); + + Assert.IsNull(jsonTextReader.NameTable); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new NameTableTestClassConverter()); + NameTableTestClass o = serializer.Deserialize(jsonTextReader); + + Assert.IsNull(jsonTextReader.NameTable); + Assert.AreEqual("hi", o.Value); + } + [Test] public void DefaultSettings_Example() { diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index d997f2ca..69e629f2 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -160,7 +160,7 @@ namespace Newtonsoft.Json.Tests public Guid guid { get; set; } public bool isActive { get; set; } public string balance { get; set; } - public string picture { get; set; } + public Uri picture { get; set; } public int age { get; set; } public string eyeColor { get; set; } public string name { get; set; } @@ -170,7 +170,7 @@ namespace Newtonsoft.Json.Tests public string phone { get; set; } public string address { get; set; } public string about { get; set; } - public string registered { get; set; } + public DateTime registered { get; set; } public double latitude { get; set; } public decimal longitude { get; set; } public List tags { get; set; } @@ -182,15 +182,9 @@ namespace Newtonsoft.Json.Tests [Test] public void DeserializeLargeJson() { - for (int i = 0; i < 5; i++) - { - using (var fs = System.IO.File.OpenText("large.json")) - using (JsonTextReader jsonTextReader = new JsonTextReader(fs)) - { - JsonSerializer serializer = new JsonSerializer(); - serializer.Deserialize>(jsonTextReader); - } - } + var json = System.IO.File.ReadAllText("large.json"); + + BenchmarkDeserializeMethod>(SerializeMethod.JsonNet, json, 100, false); } [Test] @@ -812,15 +806,18 @@ If attributes are not mentioned, default values are used in each case. #endregion #region Deserialize - public void BenchmarkDeserializeMethod(SerializeMethod method, object json) + public void BenchmarkDeserializeMethod(SerializeMethod method, object json, int? iterations = null, bool warmUp = true) { - Deserialize(method, json); + if (warmUp) + Deserialize(method, json); Stopwatch timed = new Stopwatch(); timed.Start(); + iterations = iterations ?? Iterations; + T value = default(T); - for (int x = 0; x < Iterations; x++) + for (int x = 0; x < iterations.Value; x++) { value = Deserialize(method, json); } @@ -950,15 +947,14 @@ If attributes are not mentioned, default values are used in each case. public T DeserializeWebExtensions(string json) { - JavaScriptSerializer ser = new JavaScriptSerializer(); + JavaScriptSerializer ser = new JavaScriptSerializer { MaxJsonLength = int.MaxValue }; return ser.Deserialize(json); } public T DeserializeDataContractJson(string json) { - DataContractJsonSerializer dataContractSerializer - = new DataContractJsonSerializer(typeof(T)); + DataContractJsonSerializer dataContractSerializer = new DataContractJsonSerializer(typeof(T)); MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 42021c5a..d5718aa7 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -74,4 +74,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.5.17725")] +[assembly: AssemblyFileVersion("6.0.5.17728")] diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index 865c0e74..e607ae68 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -36,7 +36,6 @@ using Newtonsoft.Json.Utilities; using Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; - #endif namespace Newtonsoft.Json.Converters diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index ade9cfff..3176dcff 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -355,15 +355,17 @@ namespace Newtonsoft.Json internal static string ToString(Guid value, char quoteChar) { - string text = null; - + string text; + string qc; #if !(NETFX_CORE || PORTABLE40 || PORTABLE) text = value.ToString("D", CultureInfo.InvariantCulture); + qc = quoteChar.ToString(CultureInfo.InvariantCulture); #else text = value.ToString("D"); + qc = quoteChar.ToString(); #endif - return quoteChar + text + quoteChar; + return qc + text + qc; } /// diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index bcf6ba12..10c42978 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -778,6 +778,14 @@ namespace Newtonsoft.Json { previousDateFormatString = null; } + + JsonTextReader textReader = reader as JsonTextReader; + if (textReader != null) + { + DefaultContractResolver resolver = _contractResolver as DefaultContractResolver; + if (resolver != null) + textReader.NameTable = resolver.GetState().NameTable; + } } private void ResetReader(JsonReader reader, CultureInfo previousCulture, DateTimeZoneHandling? previousDateTimeZoneHandling, DateParseHandling? previousDateParseHandling, FloatParseHandling? previousFloatParseHandling, int? previousMaxDepth, string previousDateFormatString) @@ -795,6 +803,10 @@ namespace Newtonsoft.Json reader.MaxDepth = previousMaxDepth; if (_dateFormatStringSet) reader.DateFormatString = previousDateFormatString; + + JsonTextReader textReader = reader as JsonTextReader; + if (textReader != null) + textReader.NameTable = null; } /// diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index b56b72bc..c667714e 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -29,7 +29,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using System.IO; -using System.Xml; using System.Globalization; using Newtonsoft.Json.Utilities; @@ -65,6 +64,7 @@ namespace Newtonsoft.Json private bool _isEndOfFile; private StringBuffer _buffer; private StringReference _stringReference; + internal PropertyNameTable NameTable; /// /// Initializes a new instance of the class with the specified . @@ -174,7 +174,7 @@ namespace Newtonsoft.Json private void ShiftBufferIfNeeded() { - // once in the last 10% of the buffer shift the remainling content to the start to avoid + // once in the last 10% of the buffer shift the remaining content to the start to avoid // unnessesarly increasing the buffer size when reading numbers/strings int length = _chars.Length; if (length - _charPos <= length * 0.1) @@ -844,7 +844,20 @@ namespace Newtonsoft.Json throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); } - string propertyName = _stringReference.ToString(); + string propertyName; + + if (NameTable != null) + { + propertyName = NameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length); + + // no match in name table + if (propertyName == null) + propertyName = _stringReference.ToString(); + } + else + { + propertyName = _stringReference.ToString(); + } EatWhitespace(false); diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index 771fbac0..28792f2e 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 char[] _indentChars; private Base64Encoder Base64Encoder { @@ -98,7 +99,14 @@ namespace Newtonsoft.Json public char IndentChar { get { return _indentChar; } - set { _indentChar = value; } + set + { + if (value != _indentChar) + { + _indentChar = value; + _indentChars = null; + } + } } /// @@ -266,14 +274,19 @@ namespace Newtonsoft.Json // levels of indentation multiplied by the indent count int currentIndentCount = Top * _indentation; - while (currentIndentCount > 0) + if (currentIndentCount > 0) { - // write up to a max of 10 characters at once to avoid creating too many new strings - int writeCount = Math.Min(currentIndentCount, 10); + if (_indentChars == null) + _indentChars = new string(_indentChar, 10).ToCharArray(); - _writer.Write(new string(_indentChar, writeCount)); + while (currentIndentCount > 0) + { + int writeCount = Math.Min(currentIndentCount, 10); - currentIndentCount -= writeCount; + _writer.Write(_indentChars, 0, writeCount); + + currentIndentCount -= writeCount; + } } } @@ -620,7 +633,18 @@ namespace Newtonsoft.Json public override void WriteValue(Guid value) { InternalWriteValue(JsonToken.String); - WriteValueInternal(JsonConvert.ToString(value, _quoteChar), JsonToken.String); + + string text = null; + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + text = value.ToString("D", CultureInfo.InvariantCulture); +#else + text = value.ToString("D"); +#endif + + _writer.Write(_quoteChar); + _writer.Write(text); + _writer.Write(_quoteChar); } /// @@ -630,7 +654,17 @@ namespace Newtonsoft.Json public override void WriteValue(TimeSpan value) { InternalWriteValue(JsonToken.String); - WriteValueInternal(JsonConvert.ToString(value, _quoteChar), JsonToken.String); + + string text; +#if (NET35 || NET20) + text = value.ToString(); +#else + text = value.ToString(null, CultureInfo.InvariantCulture); +#endif + + _writer.Write(_quoteChar); + _writer.Write(text); + _writer.Write(_quoteChar); } /// @@ -646,7 +680,7 @@ namespace Newtonsoft.Json else { InternalWriteValue(JsonToken.String); - WriteValueInternal(JsonConvert.ToString(value, _quoteChar), JsonToken.String); + WriteEscapedString(value.OriginalString, true); } } #endregion diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 0e7c6d57..900041a8 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -234,6 +234,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 56acebba..21819979 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -245,6 +245,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index fca9d44b..7e56cb31 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -245,6 +245,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index 5d03794b..17642ace 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -221,6 +221,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index 30e2c739..164b407e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -225,6 +225,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 7566a596..94fbc38c 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -213,6 +213,7 @@ + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 0c646530..457359d6 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.5.17725")] +[assembly: AssemblyFileVersion("6.0.5.17728")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 9530d9fa..5e3b20e2 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -83,6 +83,12 @@ namespace Newtonsoft.Json.Serialization } } + internal class DefaultContractResolverState + { + public Dictionary ContractCache; + public PropertyNameTable NameTable = new PropertyNameTable(); + } + /// /// Used by to resolves a for a given . /// @@ -122,10 +128,10 @@ namespace Newtonsoft.Json.Serialization new RegexConverter() }; - private static Dictionary _sharedContractCache; - private static readonly object _typeContractCacheLock = new object(); + private static readonly object TypeContractCacheLock = new object(); - private Dictionary _instanceContractCache; + private static readonly DefaultContractResolverState _sharedState = new DefaultContractResolverState(); + private readonly DefaultContractResolverState _instanceState = new DefaultContractResolverState(); private readonly bool _sharedCache; /// @@ -190,9 +196,9 @@ namespace Newtonsoft.Json.Serialization /// /// /// If set to true the will use a cached shared with other resolvers of the same type. - /// Sharing the cache will significantly performance because expensive reflection will only happen once but could cause unexpected - /// behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly - /// recommended to reuse instances with the . + /// Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only + /// happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different + /// results. When set to false it is highly recommended to reuse instances with the . /// public DefaultContractResolver(bool shareCache) { @@ -208,20 +214,12 @@ namespace Newtonsoft.Json.Serialization _sharedCache = shareCache; } - private Dictionary GetCache() + internal DefaultContractResolverState GetState() { if (_sharedCache) - return _sharedContractCache; + return _sharedState; else - return _instanceContractCache; - } - - private void UpdateCache(Dictionary cache) - { - if (_sharedCache) - _sharedContractCache = cache; - else - _instanceContractCache = cache; + return _instanceState; } /// @@ -234,24 +232,25 @@ namespace Newtonsoft.Json.Serialization if (type == null) throw new ArgumentNullException("type"); + DefaultContractResolverState state = GetState(); + JsonContract contract; ResolverContractKey key = new ResolverContractKey(GetType(), type); - Dictionary cache = GetCache(); + Dictionary cache = state.ContractCache; if (cache == null || !cache.TryGetValue(key, out contract)) { contract = CreateContract(type); // avoid the possibility of modifying the cache dictionary while another thread is accessing it - lock (_typeContractCacheLock) + lock (TypeContractCacheLock) { - cache = GetCache(); - Dictionary updatedCache = - (cache != null) - ? new Dictionary(cache) - : new Dictionary(); + cache = state.ContractCache; + Dictionary updatedCache = (cache != null) + ? new Dictionary(cache) + : new Dictionary(); updatedCache[key] = contract; - UpdateCache(updatedCache); + state.ContractCache = updatedCache; } } @@ -1111,7 +1110,17 @@ namespace Newtonsoft.Json.Serialization JsonProperty property = CreateProperty(member, memberSerialization); if (property != null) + { + DefaultContractResolverState state = GetState(); + + // nametable is not thread-safe for multiple writers + lock (state.NameTable) + { + property.PropertyName = state.NameTable.Add(property.PropertyName); + } + properties.AddProperty(property); + } } IList orderedProperties = properties.OrderBy(p => p.Order ?? -1).ToList(); diff --git a/Src/Newtonsoft.Json/Utilities/PropertyNameTable.cs b/Src/Newtonsoft.Json/Utilities/PropertyNameTable.cs new file mode 100644 index 00000000..fd593a27 --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/PropertyNameTable.cs @@ -0,0 +1,160 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Utilities +{ + internal class PropertyNameTable + { + // used to defeat hashtable DoS attack where someone passes in lots of strings that hash to the same hash code + private static readonly int HashCodeRandomizer; + + private int _count; + private Entry[] _entries; + private int _mask = 31; + + static PropertyNameTable() + { + HashCodeRandomizer = Environment.TickCount; + } + + public PropertyNameTable() + { + _entries = new Entry[_mask + 1]; + } + + public string Get(char[] key, int start, int length) + { + if (length == 0) + return string.Empty; + + int hashCode = length + HashCodeRandomizer; + hashCode += (hashCode << 7) ^ key[start]; + int end = start + length; + for (int i = start + 1; i < end; i++) + { + hashCode += (hashCode << 7) ^ key[i]; + } + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next) + { + if (entry.HashCode == hashCode && TextEquals(entry.Value, key, start, length)) + return entry.Value; + } + + return null; + } + + public string Add(string key) + { + if (key == null) + throw new ArgumentNullException("key"); + + int length = key.Length; + if (length == 0) + return string.Empty; + + int hashCode = length + HashCodeRandomizer; + for (int i = 0; i < key.Length; i++) + { + hashCode += (hashCode << 7) ^ key[i]; + } + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next) + { + if (entry.HashCode == hashCode && entry.Value.Equals(key)) + return entry.Value; + } + + return AddEntry(key, hashCode); + } + + + private string AddEntry(string str, int hashCode) + { + int index = hashCode & _mask; + Entry entry = new Entry(str, hashCode, _entries[index]); + _entries[index] = entry; + if (_count++ == _mask) + { + Grow(); + } + return entry.Value; + } + + private void Grow() + { + Entry[] entries = _entries; + int newMask = (_mask * 2) + 1; + Entry[] newEntries = new Entry[newMask + 1]; + + for (int i = 0; i < entries.Length; i++) + { + Entry next; + for (Entry entry = entries[i]; entry != null; entry = next) + { + int index = entry.HashCode & newMask; + next = entry.Next; + entry.Next = newEntries[index]; + newEntries[index] = entry; + } + } + _entries = newEntries; + _mask = newMask; + } + + private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length) + { + if (str1.Length != str2Length) + return false; + + for (int i = 0; i < str1.Length; i++) + { + if (str1[i] != str2[str2Start + i]) + return false; + } + return true; + } + + private class Entry + { + internal readonly string Value; + internal readonly int HashCode; + internal Entry Next; + + internal Entry(string value, int hashCode, Entry next) + { + Value = value; + HashCode = hashCode; + Next = next; + } + } + } +} \ No newline at end of file