public override object Read(object value, ProtoReader source) { Array result = null; int[] lengths = new int[_rank]; int[] indexes = new int[_rank]; int deepestRank = 0; int deepestRankLength = 0; int totalLength = 1; _listHelpers.Read( () => { if (source.TryReadFieldHeader(ListHelpers.FieldLength)) { int length = source.ReadInt32(); lengths[deepestRank++] = length; totalLength *= length; return(true); } return(false); }, () => { // count if (deepestRank != _rank) { ThrowWrongRank(); } // last deepestRank--; deepestRankLength = lengths[deepestRank]; if (totalLength > _readLengthLimit) { ArrayDecorator.ThrowExceededLengthLimit(totalLength, _readLengthLimit); } // TODO use same instance when length equals and no AppendCollection, don't forget to NoteObject even for the same instance result = Read_CreateInstance(value, lengths, out indexes[0], source); }, v => { result.SetValue(v, indexes); //Debug.WriteLine(string.Join(",", indexes.Select(x => x.ToString()).ToArray())); int newIndex = ++indexes[deepestRank]; if (newIndex >= deepestRankLength) { int rankIndex = deepestRank; while (rankIndex > 0) { indexes[rankIndex] = 0; --rankIndex; indexes[rankIndex]++; if (indexes[rankIndex] < lengths[rankIndex]) { break; } } } }, source); return(result); }
protected override void EmitRead(AqlaSerializer.Compiler.CompilerContext ctx, AqlaSerializer.Compiler.Local valueFrom) { var g = ctx.G; using (ctx.StartDebugBlockAuto(this)) using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local result = ctx.Local(_arrayType, true)) using (Compiler.Local lengthTemp = ctx.Local(typeof(int))) using (Compiler.Local deepestRank = ctx.Local(typeof(int), true)) using (Compiler.Local totalLength = ctx.Local(typeof(int))) { var lengths = Enumerable.Range(0, _rank).Select(x => g.ctx.Local(typeof(int))).ToArray(); var indexes = Enumerable.Range(0, _rank).Select(x => g.ctx.Local(typeof(int), true)).ToArray(); var indexesOp = indexes.Select(x => (Operand)x).ToArray(); var deepestRankLength = lengths[lengths.Length - 1]; g.Assign(totalLength, 1); _listHelpers.EmitRead( ctx.G, (onSuccess, onFail) => { using (ctx.StartDebugBlockAuto(this, "meta")) { g.If(g.ReaderFunc.TryReadFieldHeader_bool(ListHelpers.FieldLength)); { g.Assign(lengthTemp, g.ReaderFunc.ReadInt32()); g.Switch(deepestRank); { for (int i = 0; i < _rank; i++) { g.Case(i); { g.Assign(lengths[i], lengthTemp); } g.Break(); } g.DefaultCase(); EmitThrowWrongRank(g); } g.End(); g.Increment(deepestRank); g.Assign(totalLength, totalLength.AsOperand * lengthTemp.AsOperand); onSuccess(); } g.Else(); { onFail(); } g.End(); } }, () => { using (ctx.StartDebugBlockAuto(this, "prepareInstance")) { g.If(deepestRank.AsOperand != _rank); { EmitThrowWrongRank(g); } g.End(); //g.Decrement(deepestRank); - not used g.If(totalLength.AsOperand > _readLengthLimit); { ArrayDecorator.EmitThrowExceededLengthLimit(g, totalLength, _readLengthLimit); } g.End(); ctx.MarkDebug("// length read, creating instance"); EmitRead_CreateInstance(g, value, lengths, indexes[0], result); } }, v => { using (ctx.StartDebugBlockAuto(this, "add")) { g.Assign(result.AsOperand[indexesOp], v); var newIndex = indexes[_rank - 1]; g.Increment(newIndex); g.If(newIndex.AsOperand >= deepestRankLength.AsOperand); { // unwrapped loop var breakLabel = g.DefineLabel(); int rankIndex = _rank - 1; while (rankIndex > 0) { g.Assign(indexes[rankIndex], 0); --rankIndex; g.Increment(indexes[rankIndex]); g.If(indexes[rankIndex].AsOperand < lengths[rankIndex].AsOperand); { g.Goto(breakLabel); } g.End(); } g.MarkLabel(breakLabel); } g.End(); } }); foreach (Local local in indexes.Concat(lengths)) { local.Dispose(); } if (EmitReadReturnsValue) { ctx.LoadValue(result); } else { g.Assign(value, result); } } }