-Avoid box allocations when enumerating IDictionary

This commit is contained in:
James Newton-King
2016-04-07 15:47:53 +12:00
parent 93ba0c4b84
commit b23ead8d23
3 changed files with 65 additions and 34 deletions
@@ -2041,9 +2041,19 @@ namespace Newtonsoft.Json.Serialization
IDictionary targetDictionary = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(createdObjectDictionary) : (IDictionary)createdObjectDictionary;
IDictionary newValues = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(value) : (IDictionary)value;
foreach (DictionaryEntry newValue in newValues)
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
IDictionaryEnumerator e = newValues.GetEnumerator();
try
{
targetDictionary.Add(newValue.Key, newValue.Value);
while (e.MoveNext())
{
DictionaryEntry entry = e.Entry;
targetDictionary.Add(entry.Key, entry.Value);
}
}
finally
{
(e as IDisposable)?.Dispose();
}
}
}
@@ -1050,49 +1050,60 @@ namespace Newtonsoft.Json.Serialization
int initialDepth = writer.Top;
foreach (DictionaryEntry entry in values)
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
IDictionaryEnumerator e = values.GetEnumerator();
try
{
bool escape;
string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out escape);
propertyName = (contract.DictionaryKeyResolver != null)
? contract.DictionaryKeyResolver(propertyName)
: propertyName;
try
while (e.MoveNext())
{
object value = entry.Value;
JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
DictionaryEntry entry = e.Entry;
if (ShouldWriteReference(value, null, valueContract, contract, member))
bool escape;
string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out escape);
propertyName = (contract.DictionaryKeyResolver != null)
? contract.DictionaryKeyResolver(propertyName)
: propertyName;
try
{
writer.WritePropertyName(propertyName, escape);
WriteReference(writer, value);
}
else
{
if (!CheckForCircularReference(writer, value, null, valueContract, contract, member))
object value = entry.Value;
JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
if (ShouldWriteReference(value, null, valueContract, contract, member))
{
continue;
writer.WritePropertyName(propertyName, escape);
WriteReference(writer, value);
}
else
{
if (!CheckForCircularReference(writer, value, null, valueContract, contract, member))
{
continue;
}
writer.WritePropertyName(propertyName, escape);
writer.WritePropertyName(propertyName, escape);
SerializeValue(writer, value, valueContract, null, contract, member);
SerializeValue(writer, value, valueContract, null, contract, member);
}
}
}
catch (Exception ex)
{
if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
catch (Exception ex)
{
HandleError(writer, initialDepth);
}
else
{
throw;
if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
{
HandleError(writer, initialDepth);
}
else
{
throw;
}
}
}
}
finally
{
(e as IDisposable)?.Dispose();
}
writer.WriteEndObject();
@@ -299,9 +299,19 @@ namespace Newtonsoft.Json.Utilities
{
if (_dictionary != null)
{
foreach (DictionaryEntry item in _dictionary)
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
IDictionaryEnumerator e = _dictionary.GetEnumerator();
try
{
array[arrayIndex++] = new KeyValuePair<TKey, TValue>((TKey)item.Key, (TValue)item.Value);
while (e.MoveNext())
{
DictionaryEntry entry = e.Entry;
array[arrayIndex++] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
}
finally
{
(e as IDisposable)?.Dispose();
}
}
#if !(NET40 || NET35 || NET20 || PORTABLE40)