Expression Nullable(IParser parser, Expression var, Type schemaType, bool initialize) { return(parser.Container(schemaType.GetBondDataType(), (valueParser, valueType, next, count, arraySegment) => { var body = new List <Expression>(); if (initialize) { body.Add(Expression.Assign(var, Expression.Default(var.Type))); } body.Add(ControlExpression.While(next, Value(valueParser, var, valueType, schemaType, initialize: true))); return Expression.Block(body); })); }
Expression Container(IParser parser, RuntimeSchema schema) { var expectedValueType = schema.HasValue ? schema.TypeDef.element.id : (BondDataType?)null; return(parser.Container(expectedValueType, (valueParser, elementType, next, count, arraySegment) => { 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) || (arraySegment != null)) { body = PrunedExpression.IfThenElse( Expression.Equal(elementType, Expression.Constant(BondDataType.BT_INT8)), writer.WriteBytes(arraySegment ?? 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(arraySegment ?? blob), body); } } return Expression.Block( writer.WriteContainerBegin(count, elementType), body, writer.WriteContainerEnd()); })); }
Expression Map(IParser parser, Expression map, Type schemaType, bool initialize) { var itemSchemaType = schemaType.GetKeyValueType(); return(parser.Map(itemSchemaType.Key.GetBondDataType(), itemSchemaType.Value.GetBondDataType(), (keyParser, valueParser, keyType, valueType, nextKey, nextValue, count) => { Expression init = Expression.Empty(); var itemType = map.Type.GetKeyValueType(); var key = Expression.Variable(itemType.Key, map + "_key"); var value = Expression.Variable(itemType.Value, map + "_value"); if (initialize) { var cappedCount = Expression.Variable(typeof(int), map + "_count"); // TODO: should we use non-default Comparer init = ApplyCountCap( count, cappedCount, DeserializerControls.Active.MaxPreallocatedContainerElements, Expression.Assign(map, newContainer(map.Type, schemaType, cappedCount))); } var add = map.Type.GetDeclaredProperty(typeof(IDictionary <,>), "Item", value.Type); Expression addItem = Expression.Block( Value(keyParser, key, keyType, itemSchemaType.Key, initialize: true), nextValue, Value(valueParser, value, valueType, itemSchemaType.Value, initialize: true), Expression.Assign(Expression.Property(map, add, new Expression[] { key }), value)); return Expression.Block( new[] { key, value }, init, ControlExpression.While(nextKey, addItem)); })); }
Expression Map(IParser parser, RuntimeSchema schema) { var expectedValueType = schema.HasValue ? schema.TypeDef.element.id : (BondDataType?)null; var expectedKeyType = schema.HasValue ? schema.TypeDef.key.id : (BondDataType?)null; return(parser.Map(expectedKeyType, expectedValueType, (keyParser, valueParser, keyType, valueType, nextKey, nextValue, count) => Expression.Block( writer.WriteContainerBegin(count, keyType, valueType), ControlExpression.While(nextKey, Expression.Block( writer.WriteItemBegin(), schema.HasValue ? Value(keyParser, keyType, schema.GetKeySchema()) : Value(keyParser, keyType), writer.WriteItemEnd(), nextValue, writer.WriteItemBegin(), schema.HasValue ? Value(valueParser, valueType, schema.GetElementSchema()) : Value(valueParser, valueType), writer.WriteItemEnd())), 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, 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); })); }