diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 16f39448..230570f7 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -301,6 +301,77 @@ namespace Newtonsoft.Json.Tests return result; } + [Test] + public void LargeArrayJTokenPathPerformance() + { + JArray a = new JArray(); + for (int i = 0; i < 100000; i++) + { + a.Add(i); + } + + JToken first = a.First; + JToken last = a.Last; + + int interations = 1000; + + TimeOperation(() => + { + string p = null; + for (int i = 0; i < interations; i++) + { + p = first.Path; + } + + return p; + }, "First"); + + TimeOperation(() => + { + string p = null; + for (int i = 0; i < interations; i++) + { + p = last.Path; + } + + return p; + }, "Last"); + } + + [Test] + public void LargeArrayAddPerformance() + { + JArray a1 = new JArray(); + + JArray a2 = new JArray(); + for (int i = 0; i < 100000; i++) + { + a2.Add(i); + } + + int interations = 1000; + + TimeOperation(() => + { + for (int i = 0; i < interations; i++) + { + a1.Add(interations); + } + + return a1; + }, "Small"); + + TimeOperation(() => + { + for (int i = 0; i < interations; i++) + { + a2.Add(interations); + } + + return a2; + }, "Large"); + } + [Test] public void BuildJObject() { diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 5ca9a165..ad73e028 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -270,6 +270,11 @@ namespace Newtonsoft.Json.Linq set { SetItem(index, value); } } + internal override int IndexOfItem(JToken item) + { + return _values.IndexOfReference(item); + } + internal override void MergeItem(object content, JsonMergeSettings settings) { IEnumerable a = (IsMultiContent(content) || content is JArray) diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index 0a49a93e..1411caf6 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -48,6 +48,11 @@ namespace Newtonsoft.Json.Linq get { return _values; } } + internal override int IndexOfItem(JToken item) + { + return _values.IndexOfReference(item); + } + internal override void MergeItem(object content, JsonMergeSettings settings) { JConstructor c = content as JConstructor; diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 6e902010..02bb0555 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -351,30 +351,7 @@ namespace Newtonsoft.Json.Linq return item; } - private class JTokenReferenceEqualityComparer : IEqualityComparer - { - public static readonly JTokenReferenceEqualityComparer Instance = new JTokenReferenceEqualityComparer(); - - public bool Equals(JToken x, JToken y) - { - return ReferenceEquals(x, y); - } - - public int GetHashCode(JToken obj) - { - if (obj == null) - { - return 0; - } - - return obj.GetHashCode(); - } - } - - internal int IndexOfItem(JToken item) - { - return ChildrenTokens.IndexOf(item, JTokenReferenceEqualityComparer.Instance); - } + internal abstract int IndexOfItem(JToken item); internal virtual void InsertItem(int index, JToken item, bool skipParentCheck) { diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 7e7d49c1..ea74795c 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -128,6 +128,11 @@ namespace Newtonsoft.Json.Linq return _properties.Compare(t._properties); } + internal override int IndexOfItem(JToken item) + { + return _properties.IndexOfReference(item); + } + internal override void InsertItem(int index, JToken item, bool skipParentCheck) { // don't add comments to JObject, no name to reference comment by diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index a1049cf0..c68502a0 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -235,6 +235,11 @@ namespace Newtonsoft.Json.Linq throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); } + internal override int IndexOfItem(JToken item) + { + return _content.IndexOf(item); + } + internal override void InsertItem(int index, JToken item, bool skipParentCheck) { // don't add comments to JProperty diff --git a/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs b/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs index 867d741c..527fdab5 100644 --- a/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs +++ b/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq { @@ -35,6 +36,10 @@ namespace Newtonsoft.Json.Linq private Dictionary _dictionary; + public JPropertyKeyedCollection() : base(new List()) + { + } + private void AddKey(string key, JToken item) { EnsureDictionary(); @@ -220,6 +225,11 @@ namespace Newtonsoft.Json.Linq } } + public int IndexOfReference(JToken t) + { + return ((List)Items).IndexOfReference(t); + } + public bool Compare(JPropertyKeyedCollection other) { if (this == other) diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 7a162847..d6d0830c 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1883,7 +1883,7 @@ namespace Newtonsoft.Json.Linq /// An that can be used to read this token and its descendants. public JsonReader CreateReader() { - return new JTokenReader(this, Path); + return new JTokenReader(this); } internal static JToken FromObjectInternal(object o, JsonSerializer jsonSerializer) diff --git a/Src/Newtonsoft.Json/Linq/JTokenReader.cs b/Src/Newtonsoft.Json/Linq/JTokenReader.cs index 5f0ca876..d3595198 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenReader.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenReader.cs @@ -33,8 +33,8 @@ namespace Newtonsoft.Json.Linq /// public class JTokenReader : JsonReader, IJsonLineInfo { - private readonly string _initialPath; private readonly JToken _root; + private string _initialPath; private JToken _parent; private JToken _current; @@ -57,6 +57,7 @@ namespace Newtonsoft.Json.Linq _root = token; } + // this is used by json.net schema internal JTokenReader(JToken token, string initialPath) : this(token) { @@ -306,6 +307,11 @@ namespace Newtonsoft.Json.Linq { string path = base.Path; + if (_initialPath == null) + { + _initialPath = _root.Path; + } + if (!string.IsNullOrEmpty(_initialPath)) { if (string.IsNullOrEmpty(path)) diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 6b84fc34..1ff8d237 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -214,11 +214,11 @@ namespace Newtonsoft.Json.Utilities return -1; } - public static bool Contains(this IEnumerable list, object value, IEqualityComparer comparer) + public static bool Contains(this List list, T value, IEqualityComparer comparer) { - foreach (object item in list) + for (int i = 0; i < list.Count; i++) { - if (comparer.Equals(item, value)) + if (comparer.Equals(value, list[i])) { return true; } @@ -226,24 +226,14 @@ namespace Newtonsoft.Json.Utilities return false; } - /// - /// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}. - /// - /// The type of the elements of source. - /// A sequence in which to locate a value. - /// The object to locate in the sequence - /// An equality comparer to compare values. - /// The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, –1. - public static int IndexOf(this IEnumerable list, TSource value, IEqualityComparer comparer) + public static int IndexOfReference(this List list, T item) { - int index = 0; - foreach (TSource item in list) + for (int i = 0; i < list.Count; i++) { - if (comparer.Equals(item, value)) + if (ReferenceEquals(item, list[i])) { - return index; + return i; } - index++; } return -1; }