Expression Container()

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);
                });
        }