예제 #1
0
 private static int GetMarkSizeLength(int mark, int?size = default) => Encode.GetEncodedLength(mark) + (size is { } s?Encode.GetEncodedLength(s) : 0);
예제 #2
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);
            }
        }