in cs/src/core/expressions/DeserializerTransform.cs [258:427]
Expression Container(IParser parser, Expression container, Type schemaType, bool initialize)
{
var itemSchemaType = schemaType.GetValueType();
return parser.Container(itemSchemaType.GetBondDataType(),
(valueParser, elementType, next, count, arraySegment) =>
{
Expression addItem;
ParameterExpression[] parameters;
Expression beforeLoop = Expression.Empty();
Expression afterLoop = Expression.Empty();
if (schemaType.IsBondBlob())
{
var blob = parser.Blob(count);
if (blob != null)
return typeAlias.Assign(container, blob);
// Parser doesn't provide optimized read for blob so we will have to read byte-by-byte.
var index = Expression.Variable(typeof(int), "index");
var array = Expression.Variable(typeof(byte[]), "array");
var cappedCount = Expression.Variable(typeof(int), container + "_count");
beforeLoop = ApplyCountCap(
count,
cappedCount,
DeserializerControls.Active.MaxPreallocatedBlobBytes,
Expression.Block(
Expression.Assign(index, Expression.Constant(0)),
Expression.Assign(array, Expression.NewArrayBounds(typeof(byte), cappedCount))));
// If parser didn't provide real element count we may need to resize the array
var newSize = Expression.Condition(
Expression.GreaterThan(index, Expression.Constant(512)),
Expression.Multiply(index, Expression.Constant(2)),
Expression.Constant(1024));
addItem = Expression.Block(
Expression.IfThen(
Expression.GreaterThanOrEqual(index, Expression.ArrayLength(array)),
Expression.Call(null, arrayResize.MakeGenericMethod(typeof(byte)), array, newSize)),
valueParser.Scalar(elementType, BondDataType.BT_INT8, value => Expression.Assign(
Expression.ArrayAccess(array, Expression.PostIncrementAssign(index)),
Expression.Convert(value, typeof(byte)))));
afterLoop = typeAlias.Assign(
container,
Expression.New(arraySegmentCtor, array, Expression.Constant(0), index));
parameters = new[] { index, array };
}
else if (container.Type.IsArray)
{
var arrayElemType = container.Type.GetValueType();
var containerResizeMethod = arrayResize.MakeGenericMethod(arrayElemType);
if (initialize)
{
ParameterExpression cappedCount = Expression.Variable(typeof(int), container + "_count");
beforeLoop = ApplyCountCap(
count,
cappedCount,
DeserializerControls.Active.MaxPreallocatedContainerElements,
Expression.Assign(container, newContainer(container.Type, schemaType, cappedCount)));
}
if (arrayElemType == typeof(byte))
{
var parseBlob = parser.Blob(count);
if (parseBlob != null)
{
var blob = Expression.Variable(typeof(ArraySegment<byte>), "blob");
return Expression.Block(
new[] { blob },
beforeLoop,
Expression.Assign(blob, parseBlob),
Expression.Call(null, bufferBlockCopy, new[]
{
Expression.Property(blob, "Array"),
Expression.Property(blob, "Offset"),
container,
Expression.Constant(0),
count
}));
}
}
var i = Expression.Variable(typeof(int), "i");
beforeLoop = Expression.Block(
beforeLoop,
Expression.Assign(i, Expression.Constant(0)));
// Resize the array if we've run out of room
var maybeResize =
Expression.IfThen(
Expression.Equal(i, Expression.ArrayLength(container)),
Expression.Call(
containerResizeMethod,
container,
Expression.Multiply(
Expression.Condition(
Expression.LessThan(i, Expression.Constant(32)),
Expression.Constant(32),
i),
Expression.Constant(2))));
// Puts a single element into the array.
addItem = Expression.Block(
maybeResize,
Value(
valueParser,
Expression.ArrayAccess(container, i),
elementType,
itemSchemaType,
initialize: true),
Expression.PostIncrementAssign(i));
// Expanding the array potentially leaves many blank
// entries; this resize will get rid of them.
afterLoop = Expression.IfThen(
Expression.GreaterThan(Expression.ArrayLength(container), i),
Expression.Call(containerResizeMethod, container, i));
parameters = new[] { i };
}
else
{
var item = Expression.Variable(container.Type.GetValueType(), container + "_item");
if (initialize)
{
var cappedCount = Expression.Variable(typeof(int), container + "_count");
beforeLoop = ApplyCountCap(
count,
cappedCount,
DeserializerControls.Active.MaxPreallocatedContainerElements,
Expression.Assign(container, newContainer(container.Type, schemaType, cappedCount)));
}
else
{
var capacity = container.Type.GetDeclaredProperty("Capacity", count.Type);
if (capacity != null)
{
var cappedCount = Expression.Variable(typeof(int), container + "_count");
beforeLoop = ApplyCountCap(
count,
cappedCount,
DeserializerControls.Active.MaxPreallocatedContainerElements,
Expression.Assign(Expression.Property(container, capacity), cappedCount));
}
}
var add = container.Type.GetMethod(typeof(ICollection<>), "Add", item.Type);
addItem = Expression.Block(
Value(valueParser, item, elementType, itemSchemaType, initialize: true),
Expression.Call(container, add, item));
parameters = new[] { item };
}
return Expression.Block(
parameters,
beforeLoop,
ControlExpression.While(next,
addItem),
afterLoop);
});
}