private static int GetMarkSizeLength(int mark, int?size = default) => Encode.GetEncodedLength(mark) + (size is { } s?Encode.GetEncodedLength(s) : 0);
private static async ValueTask <InMemoryStorage?> ReadStream(Stream stream, int rollbackLevel, bool shrink, CancellationToken token) { var total = 0; var end = 0; var streamTotal = 0; var streamEnd = 0; var streamLength = (int)stream.Length; if (streamLength == 0) { return(shrink ? null : new InMemoryStorage(false)); } var buf = ArrayPool <byte> .Shared.Rent(streamLength + 8 *MaxInt32Length); try { var memoryOffset = 0; stream.Seek(offset: 0, SeekOrigin.Begin); while (true) { var len = await stream.ReadAsync(buf, memoryOffset, count : 1, token).ConfigureAwait(false); if (len == 0) { break; } var byteMark = buf[memoryOffset]; if (byteMark == FinalMark) { break; } if (byteMark == SkipMark) { continue; } var levelLength = Encode.GetLength(byteMark); if (levelLength > 1 && await stream.ReadAsync(buf, memoryOffset + 1, levelLength - 1, token).ConfigureAwait(false) < levelLength - 1) { throw GetIncorrectDataFormatException(); } var level = Encode.Decode(buf.AsSpan(memoryOffset, levelLength)); if (await stream.ReadAsync(buf, memoryOffset, count: 1, token).ConfigureAwait(false) < 1) { throw GetIncorrectDataFormatException(); } var sizeLength = Encode.GetLength(buf[memoryOffset]); if (sizeLength > 1 && await stream.ReadAsync(buf, memoryOffset + 1, sizeLength - 1, token).ConfigureAwait(false) < sizeLength - 1) { throw GetIncorrectDataFormatException(); } var size = Encode.Decode(buf.AsSpan(memoryOffset, sizeLength)); if (level == SkipBlockMark) { stream.Seek(size, SeekOrigin.Current); continue; } if (await stream.ReadAsync(buf, memoryOffset, size, token).ConfigureAwait(false) < size) { throw GetIncorrectDataFormatException(); } total += size; streamTotal += levelLength + sizeLength + size; memoryOffset += size; if (level >> 1 <= rollbackLevel) { end = total; streamEnd = streamTotal; } } if (!shrink) { if (streamEnd < streamLength) { stream.SetLength(streamEnd); await stream.FlushAsync(token).ConfigureAwait(false); } return(new InMemoryStorage(buf.AsSpan(start: 0, end))); } if (streamTotal < streamLength) { stream.SetLength(streamTotal); } using var baseline = new InMemoryStorage(buf.AsSpan(start: 0, end)); var dataSize = baseline.GetDataSize(); if (dataSize >= end) { return(null); } buf[0] = FinalMark; memoryOffset = FinalMarkLength; var tranSize = streamTotal - streamEnd; var controlDataSize = dataSize > 0 ? GetMarkSizeLength(mark: 1, dataSize) : 0; if (dataSize > 0) { WriteMarkSize(buf.AsSpan(memoryOffset, controlDataSize), mark: 1, dataSize); memoryOffset += controlDataSize; baseline.WriteDataToSpan(buf.AsSpan(memoryOffset, dataSize)); memoryOffset += dataSize; } if (tranSize > 0) { stream.Seek(streamEnd, SeekOrigin.Begin); await stream.ReadAsync(buf, memoryOffset, tranSize, token).ConfigureAwait(false); memoryOffset += tranSize; } buf[memoryOffset] = FinalMark; memoryOffset += FinalMarkLength; stream.Seek(offset: 0, SeekOrigin.End); var extLength = FinalMarkLength + controlDataSize + dataSize + tranSize; await stream.WriteAsync(buf, offset : 0, extLength, token).ConfigureAwait(false); await stream.FlushAsync(token).ConfigureAwait(false); var bypassLength = streamTotal + FinalMarkLength; var initBlockLength1 = GetMarkSizeLength(SkipBlockMark, bypassLength); var initBlockLength = bypassLength < initBlockLength1 ? bypassLength : initBlockLength1; Array.Clear(buf, memoryOffset, initBlockLength); if (bypassLength >= initBlockLength1) { bypassLength -= initBlockLength1; var initBlockLength2 = GetMarkSizeLength(SkipBlockMark, bypassLength); var delta = initBlockLength1 - initBlockLength2; WriteMarkSize(buf.AsSpan(memoryOffset + delta, initBlockLength - delta), SkipBlockMark, bypassLength); } stream.Seek(offset: 0, SeekOrigin.Begin); await stream.WriteAsync(buf, memoryOffset, initBlockLength, token).ConfigureAwait(false); await stream.FlushAsync(token).ConfigureAwait(false); var bufOffset = FinalMarkLength + initBlockLength; var bufLength = controlDataSize + dataSize + tranSize + FinalMarkLength - initBlockLength; if (bufLength > 0) { await stream.WriteAsync(buf, bufOffset, bufLength, token).ConfigureAwait(false); await stream.FlushAsync(token).ConfigureAwait(false); } stream.Seek(offset: 0, SeekOrigin.Begin); await stream.WriteAsync(buf, FinalMarkLength, initBlockLength, token).ConfigureAwait(false); await stream.FlushAsync(token).ConfigureAwait(false); stream.SetLength(controlDataSize + dataSize + tranSize); await stream.FlushAsync(token).ConfigureAwait(false); return(null); } catch (ArgumentOutOfRangeException ex) { throw GetIncorrectDataFormatException(ex); } finally { ArrayPool <byte> .Shared.Return(buf); } }