public void EmitDeserialize(CompilerContext context, Local value)
        {
            var elementType     = value.LocalType.GetElementType();
            var emptyArrayLabel = context.Emit.DefineLabel();
            var endLabel        = context.Emit.DefineLabel();

            using (var length = context.Emit.DeclareLocal <int>("length"))
            {
                // length = ProudNetBinaryReaderExtensions.ReadScalar(reader)
                context.Emit.LoadReaderOrWriterParam();
                context.Emit.Call(ReflectionHelper.GetMethod((BinaryReader _) => _.ReadScalar()));
                context.Emit.StoreLocal(length);

                // if(length < 1) {
                //  value = Array.Empty<>()
                //  return
                // }
                context.Emit.LoadLocal(length);
                context.Emit.LoadConstant(1);
                context.Emit.BranchIfLess(emptyArrayLabel);

                // value = new [length]
                context.Emit.LoadLocal(length);
                context.Emit.NewArray(elementType);
                context.Emit.StoreLocal(value);

                // Little optimization for byte arrays
                if (elementType == typeof(byte))
                {
                    // value = reader.ReadBytes(length);
                    context.Emit.LoadReaderOrWriterParam();
                    context.Emit.LoadLocal(length);
                    context.Emit.Call(ReflectionHelper.GetMethod((BinaryReader _) => _.ReadBytes(default(int))));
                    context.Emit.StoreLocal(value);
                }
                else
                {
                    var loopLabel      = context.Emit.DefineLabel();
                    var loopCheckLabel = context.Emit.DefineLabel();

                    using (var element = context.Emit.DeclareLocal(elementType, "element"))
                        using (var i = context.Emit.DeclareLocal <int>("i"))
                        {
                            context.Emit.MarkLabel(loopLabel);
                            context.EmitDeserialize(element);

                            // value[i] = element
                            context.Emit.LoadLocal(value);
                            context.Emit.LoadLocal(i);
                            context.Emit.LoadLocal(element);
                            context.Emit.StoreElement(elementType);

                            // ++i
                            context.Emit.LoadLocal(i);
                            context.Emit.LoadConstant(1);
                            context.Emit.Add();
                            context.Emit.StoreLocal(i);

                            // i < length
                            context.Emit.MarkLabel(loopCheckLabel);
                            context.Emit.LoadLocal(i);
                            context.Emit.LoadLocal(length);
                            context.Emit.BranchIfLess(loopLabel);
                        }
                }

                context.Emit.Branch(endLabel);
            }

            // value = Array.Empty<>()
            context.Emit.MarkLabel(emptyArrayLabel);
            context.Emit.Call(typeof(Array)
                              .GetMethod(nameof(Array.Empty))
                              .GetGenericMethodDefinition()
                              .MakeGenericMethod(elementType));
            context.Emit.StoreLocal(value);
            context.Emit.MarkLabel(endLabel);
        }
예제 #2
0
        public void EmitDeserialize(CompilerContext context, Local value)
        {
            var elementType = value.LocalType.GetElementType();
            var emptyArray  = context.Emit.DefineLabel();
            var end         = context.Emit.DefineLabel();

            using (var length = context.Emit.DeclareLocal <int>("length"))
            {
                context.EmitDeserialize(length);

                // if(length < 1) {
                //  value = Array.Empty<>()
                //  return
                // }
                context.Emit.LoadLocal(length);
                context.Emit.LoadConstant(1);
                context.Emit.BranchIfLess(emptyArray);

                // value = new [length]
                context.Emit.LoadLocal(length);
                context.Emit.NewArray(elementType);
                context.Emit.StoreLocal(value);

                var loop      = context.Emit.DefineLabel();
                var loopCheck = context.Emit.DefineLabel();

                using (var element = context.Emit.DeclareLocal(elementType, "element"))
                    using (var i = context.Emit.DeclareLocal <int>("i"))
                    {
                        context.Emit.MarkLabel(loop);

                        // reader.ReadByte() -> index
                        context.Emit.LoadReaderOrWriterParam();
                        context.Emit.CallVirtual(ReflectionHelper.GetMethod((BinaryReader _) => _.ReadByte()));
                        context.Emit.Pop();

                        context.EmitDeserialize(element);

                        // value[i] = element
                        context.Emit.LoadLocal(value);
                        context.Emit.LoadLocal(i);
                        context.Emit.LoadLocal(element);
                        context.Emit.StoreElement(elementType);

                        // ++i
                        context.Emit.LoadLocal(i);
                        context.Emit.LoadConstant(1);
                        context.Emit.Add();
                        context.Emit.StoreLocal(i);

                        // i < length
                        context.Emit.MarkLabel(loopCheck);
                        context.Emit.LoadLocal(i);
                        context.Emit.LoadLocal(length);
                        context.Emit.BranchIfLess(loop);
                    }

                context.Emit.Branch(end);
            }

            // value = Array.Empty<>()
            context.Emit.MarkLabel(emptyArray);
            context.Emit.Call(typeof(Array)
                              .GetMethod(nameof(Array.Empty))
                              .GetGenericMethodDefinition()
                              .MakeGenericMethod(elementType));
            context.Emit.StoreLocal(value);
            context.Emit.MarkLabel(end);
        }