-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 targetDictionary = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(createdObjectDictionary) : (IDictionary)createdObjectDictionary;
IDictionary newValues = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(value) : (IDictionary)value; 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; 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; while (e.MoveNext())
string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out escape);
propertyName = (contract.DictionaryKeyResolver != null)
? contract.DictionaryKeyResolver(propertyName)
: propertyName;
try
{ {
object value = entry.Value; DictionaryEntry entry = e.Entry;
JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
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); object value = entry.Value;
WriteReference(writer, value); JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
}
else if (ShouldWriteReference(value, null, valueContract, contract, member))
{
if (!CheckForCircularReference(writer, 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)
catch (Exception ex)
{
if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
{ {
HandleError(writer, initialDepth); if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
} {
else HandleError(writer, initialDepth);
{ }
throw; else
{
throw;
}
} }
} }
} }
finally
{
(e as IDisposable)?.Dispose();
}
writer.WriteEndObject(); writer.WriteEndObject();
@@ -299,9 +299,19 @@ namespace Newtonsoft.Json.Utilities
{ {
if (_dictionary != null) 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) #if !(NET40 || NET35 || NET20 || PORTABLE40)