Use singleton for zero-element arrays.

Create an easily-inlinable method that accesses the array used
by Enumerable.Empty (and hence not just shared within this project
but likely with the client assembly or other libraries it uses)
and use it anywhere a zero-element array is used, reducing allocations.
This commit is contained in:
Jon Hanna
2017-01-09 01:50:03 +00:00
parent 218c25c859
commit ff3bfaf51b
11 changed files with 69 additions and 39 deletions
+1 -1
View File
@@ -544,7 +544,7 @@ namespace Newtonsoft.Json
Guid g;
if (s.Length == 0)
{
data = new byte[0];
data = CollectionUtils.ArrayEmpty<byte>();
}
else if (ConvertUtils.TryConvertGuid(s, out g))
{
+1 -1
View File
@@ -146,7 +146,7 @@ namespace Newtonsoft.Json
byte[] data;
if (_stringReference.Length == 0)
{
data = new byte[0];
data = CollectionUtils.ArrayEmpty<byte>();
}
else if (_stringReference.Length == 36 && ConvertUtils.TryConvertGuid(_stringReference.ToString(), out g))
{
@@ -83,7 +83,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
return JPath.Evaluate(pathFilters, root, t, false);
}
return new JToken[0];
return CollectionUtils.ArrayEmpty<JToken>();
}
public override bool IsMatch(JToken root, JToken t)
@@ -2274,7 +2274,7 @@ namespace Newtonsoft.Json.Serialization
return CreateObjectUsingCreatorWithParameters(reader, objectContract, containerMember, objectContract.OverrideCreator, id);
}
newObject = objectContract.OverrideCreator(new object[0]);
newObject = objectContract.OverrideCreator(CollectionUtils.ArrayEmpty<object>());
}
else if (objectContract.DefaultCreator != null &&
(!objectContract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || objectContract.ParameterizedCreator == null))
@@ -745,7 +745,7 @@ namespace Newtonsoft.Json.Serialization
bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, containerProperty);
SerializeMultidimensionalArray(writer, values, contract, member, writer.Top, new int[0]);
SerializeMultidimensionalArray(writer, values, contract, member, writer.Top, CollectionUtils.ArrayEmpty<int>());
if (hasWrittenMetadataObject)
{
@@ -29,12 +29,16 @@ using System.Collections.ObjectModel;
using System.Reflection;
using System.Text;
using System.Collections;
using System.Diagnostics;
#if NET20
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
#endif
using System.Globalization;
#if !(NET20 || NET35 || NET40 || PORTABLE40)
using System.Runtime.CompilerServices;
#endif
using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Utilities
@@ -340,9 +344,30 @@ namespace Newtonsoft.Json.Utilities
}
Array multidimensionalArray = Array.CreateInstance(type, dimensions.ToArray());
CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, new int[0]);
CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, ArrayEmpty<int>());
return multidimensionalArray;
}
// 4.6 has Array.Empty<T> to return a cached empty array. Lacking that in other
// frameworks, Enumerable.Empty<T> happens to be implemented as a cached empty
// array in all versions (in .NET Core the same instance as Array.Empty<T>).
// This includes the internal Linq bridge for 2.0.
// Since this method is simple and only 11 bytes long in a release build it's
// pretty much guaranteed to be inlined, giving us fast access of that cached
// array. With 4.5 and up we use AggressiveInlining just to be sure, so it's
// effectively the same as calling Array.Empty<T> even when not available.
#if !(NET20 || NET35 || NET40 || PORTABLE40)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static T[] ArrayEmpty<T>()
{
T[] array = Enumerable.Empty<T>() as T[];
Debug.Assert(array != null);
// Defensively guard against a version of Linq where Enumerable.Empty<T> doesn't
// return an array, but throw in debug versions so a better strategy can be
// used if that ever happens.
return array ?? new T[0];
}
}
}
@@ -37,7 +37,7 @@ namespace Newtonsoft.Json.Utilities
{
public virtual IEnumerable<string> GetDynamicMemberNames(T instance)
{
return new string[0];
return CollectionUtils.ArrayEmpty<string>();
}
public virtual bool TryBinaryOperation(T instance, BinaryOperationBinder binder, object arg, out object result)
@@ -176,7 +176,7 @@ namespace Newtonsoft.Json.Utilities
private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion);
private static readonly Expression[] NoArgs = new Expression[0];
private static Expression[] NoArgs => CollectionUtils.ArrayEmpty<Expression>();
private static Expression[] GetArgs(params DynamicMetaObject[] args)
{
@@ -158,7 +158,7 @@ namespace Newtonsoft.Json.Utilities
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { });
DynamicMetaObject retMetaObject = _innerBinder.Bind(target, CollectionUtils.ArrayEmpty<DynamicMetaObject>());
NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor();
Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);
@@ -87,40 +87,45 @@ namespace Newtonsoft.Json.Utilities
{
ParameterInfo[] parametersInfo = method.GetParameters();
Expression[] argsExpression = new Expression[parametersInfo.Length];
IList<ByRefParameter> refParameterMap = new List<ByRefParameter>();
for (int i = 0; i < parametersInfo.Length; i++)
Expression[] argsExpression;
IList<ByRefParameter> refParameterMap;
if (parametersInfo.Length == 0)
{
ParameterInfo parameter = parametersInfo[i];
Type parameterType = parameter.ParameterType;
bool isByRef = false;
if (parameterType.IsByRef)
argsExpression = CollectionUtils.ArrayEmpty<Expression>();
refParameterMap = CollectionUtils.ArrayEmpty<ByRefParameter>();
}
else
{
argsExpression = new Expression[parametersInfo.Length];
refParameterMap = new List<ByRefParameter>();
for (int i = 0; i < parametersInfo.Length; i++)
{
parameterType = parameterType.GetElementType();
isByRef = true;
}
Expression indexExpression = Expression.Constant(i);
Expression paramAccessorExpression = Expression.ArrayIndex(argsParameterExpression, indexExpression);
Expression argExpression = EnsureCastExpression(paramAccessorExpression, parameterType, !isByRef);
if (isByRef)
{
ParameterExpression variable = Expression.Variable(parameterType);
refParameterMap.Add(new ByRefParameter
ParameterInfo parameter = parametersInfo[i];
Type parameterType = parameter.ParameterType;
bool isByRef = false;
if (parameterType.IsByRef)
{
Value = argExpression,
Variable = variable,
IsOut = parameter.IsOut
});
parameterType = parameterType.GetElementType();
isByRef = true;
}
argExpression = variable;
Expression indexExpression = Expression.Constant(i);
Expression paramAccessorExpression = Expression.ArrayIndex(argsParameterExpression, indexExpression);
Expression argExpression = EnsureCastExpression(paramAccessorExpression, parameterType, !isByRef);
if (isByRef)
{
ParameterExpression variable = Expression.Variable(parameterType);
refParameterMap.Add(new ByRefParameter {Value = argExpression, Variable = variable, IsOut = parameter.IsOut});
argExpression = variable;
}
argsExpression[i] = argExpression;
}
argsExpression[i] = argExpression;
}
Expression callExpression;
@@ -90,7 +90,7 @@ namespace Newtonsoft.Json.Utilities
#if !(PORTABLE40 || PORTABLE)
EmptyTypes = Type.EmptyTypes;
#else
EmptyTypes = new Type[0];
EmptyTypes = CollectionUtils.ArrayEmpty<Type>();
#endif
}