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) { // TODO: should we use non-default Comparer init = Expression.Assign(map, newContainer(map.Type, schemaType, count)); } 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 SkipMap() { return(Map(null, null, (keyParser, valueParser, keyType, valueType, nextKey, nextValue, count) => ControlExpression.While(nextKey, Expression.Block( keyParser.Skip(keyType), valueParser.Skip(valueType))))); }
Expression SkipList() { return(Container(null, (valueParser, elementType, next, count) => { Debug.Assert(elementType is ConstantExpression); var elementDataType = (BondDataType)(elementType as ConstantExpression).Value; if (elementDataType == BondDataType.BT_UINT8 || elementDataType == BondDataType.BT_INT8) { return reader.SkipBytes(count); } else { return ControlExpression.While(next, valueParser.Skip(elementType)); } })); }
Expression Nullable(IParser parser, Expression var, Type schemaType, bool initialize) { return(parser.Container(schemaType.GetBondDataType(), (valueParser, valueType, next, count) => { 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) => { 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 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 SkipSet() { return(Container(null, (valueParser, elementType, next, count) => ControlExpression.While(next, valueParser.Skip(elementType)))); }
public Expression Apply(ITransform transform) { var fieldId = Expression.Variable(typeof(UInt16), "fieldId"); var fieldType = Expression.Variable(typeof(BondDataType), "fieldType"); var endLabel = Expression.Label("end"); var breakLoop = Expression.Break(endLabel); // (int)fieldType > (int)BT_STOP_BASE var notEndOrEndBase = Expression.GreaterThan( Expression.Convert(fieldType, typeof(int)), Expression.Constant((int)BondDataType.BT_STOP_BASE)); var notEnd = isBase ? notEndOrEndBase : Expression.NotEqual(fieldType, Expression.Constant(BondDataType.BT_STOP)); var isEndBase = Expression.Equal(fieldType, Expression.Constant(BondDataType.BT_STOP_BASE)); var body = new List <Expression> { isBase?reader.ReadBaseBegin() : reader.ReadStructBegin(), transform.Begin, transform.Base(baseParser), reader.ReadFieldBegin(fieldType, fieldId) }; // known fields body.AddRange( from f in transform.Fields select Expression.Loop( Expression.IfThenElse(notEndOrEndBase, Expression.Block( Expression.IfThenElse( Expression.Equal(fieldId, Expression.Constant(f.Id)), Expression.Block( f.Value(fieldParser, fieldType), reader.ReadFieldEnd(), reader.ReadFieldBegin(fieldType, fieldId), breakLoop), Expression.IfThenElse( Expression.GreaterThan(fieldId, Expression.Constant(f.Id)), Expression.Block( f.Omitted, breakLoop), transform.UnknownField(fieldParser, fieldType, fieldId) ?? Skip(fieldType))), reader.ReadFieldEnd(), reader.ReadFieldBegin(fieldType, fieldId), Expression.IfThen( Expression.GreaterThan(fieldId, Expression.Constant(f.Id)), breakLoop)), Expression.Block( f.Omitted, breakLoop)), endLabel)); // unknown fields body.Add( ControlExpression.While(notEnd, Expression.Block( Expression.IfThenElse( isEndBase, transform.UnknownEnd, Expression.Block( transform.UnknownField(fieldParser, fieldType, fieldId) ?? Skip(fieldType), reader.ReadFieldEnd())), reader.ReadFieldBegin(fieldType, fieldId)))); body.Add(isBase ? reader.ReadBaseEnd() : reader.ReadStructEnd()); body.Add(transform.End); return(Expression.Block( new[] { fieldType, fieldId }, body)); }
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); })); }