Expression Container(IParser parser, RuntimeSchema schema) { var expectedValueType = schema.HasValue ? schema.TypeDef.element.id : (BondDataType?)null; return(parser.Container(expectedValueType, (valueParser, elementType, next, count) => { var body = ControlExpression.While(next, Expression.Block( writer.WriteItemBegin(), schema.HasValue ? Value(valueParser, elementType, schema.GetElementSchema()) : Value(valueParser, elementType), writer.WriteItemEnd())); var blob = parser.Blob(count); if (blob != null) { body = PrunedExpression.IfThenElse( Expression.Equal(elementType, Expression.Constant(BondDataType.BT_INT8)), writer.WriteBytes(blob), body); // For binary protocols we can write blob directly using protocols's WriteBytes // even if the container is not a blob (blob is BT_LIST of BT_INT8). if (binaryWriter) { body = PrunedExpression.IfThenElse( Expression.Equal(elementType, Expression.Constant(BondDataType.BT_UINT8)), writer.WriteBytes(blob), body); } } return Expression.Block( writer.WriteContainerBegin(count, elementType), body, writer.WriteContainerEnd()); })); }
Expression Container(IParser parser, Expression container, Type schemaType, bool initialize) { var itemSchemaType = schemaType.GetValueType(); return(parser.Container(itemSchemaType.GetBondDataType(), (valueParser, elementType, next, count) => { 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"); beforeLoop = Expression.Block( Expression.Assign(index, Expression.Constant(0)), Expression.Assign(array, Expression.NewArrayBounds(typeof(byte), count))); // If parser didn't provide real item 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) { beforeLoop = Expression.Assign(container, newContainer(container.Type, schemaType, count)); } 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) { beforeLoop = Expression.Assign(container, newContainer(container.Type, schemaType, count)); } else { var capacity = container.Type.GetDeclaredProperty("Capacity", count.Type); if (capacity != null) { beforeLoop = Expression.Assign(Expression.Property(container, capacity), count); } } 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); })); }