public void ReadWriteSequentialWithoutSegmentOverflow()
        {
            const int segmentSize = 13;

            var source = Enumerable.Range(0, 1000).Select(n => unchecked ((byte)n)).ToArray();
            var target = new byte[source.Length];
            var buffer = new ReadWriteBuffer(segmentSize);

            for (int i = 0; i < 100; i++)
            {
                var rnd = new Random(i);

                Array.Clear(target, 0, target.Length);
                int sourcePosition = 0;
                int targetPosition = 0;

                int readWriteFactor = i % 3 + 2;
                while (sourcePosition != source.Length)
                {
                    var read = rnd.Next(readWriteFactor) == 0;
                    if (read)
                    {
                        var availableBytes = buffer.Read();
                        if (!availableBytes.IsEmpty)
                        {
                            var availableLen = Math.Min((int)availableBytes.Length, Math.Min(rnd.Next(segmentSize * 3) + 1, target.Length - targetPosition));
                            var targetSlice  = new Span <byte>(target).Slice(targetPosition, availableLen);
                            availableBytes.Slice(0, availableLen).CopyTo(targetSlice);

                            targetPosition += availableLen;
                            buffer.ConfirmRead(availableLen);
                            continue;
                        }
                    }

                    var len = Math.Min(source.Length - sourcePosition, rnd.Next(segmentSize * 3) + 1);
                    while (len != 0)
                    {
                        var memory     = buffer.GetMemory();
                        var currentLen = Math.Min(len, memory.Length);
                        new ReadOnlySpan <byte>(source).Slice(sourcePosition, currentLen).CopyTo(memory.Span.Slice(0, currentLen));

                        buffer.ConfirmWrite(currentLen);
                        sourcePosition += currentLen;
                        len            -= currentLen;
                    }

                    buffer.Flush();
                }

                var lastBlock = buffer.Read();
                Assert.Equal(target.Length - targetPosition, lastBlock.Length);

                lastBlock.CopyTo(new Span <byte>(target, targetPosition, target.Length - targetPosition));
                Assert.Equal(source, target);

                buffer.ConfirmRead((int)lastBlock.Length);
            }
        }
        public void WriteBlocks()
        {
            var source = Enumerable.Range(0, 1000).Select(n => unchecked ((byte)n)).ToArray();

            for (int i = 0; i < 10; i++)
            {
                var buffer = new ReadWriteBuffer(7);
                var rnd    = new Random(i);

                var position = 0;
                while (position < source.Length)
                {
                    var blockSize = Math.Min(rnd.Next(1, 18), source.Length - position);

                    var memory = buffer.GetMemory(blockSize);
                    new ReadOnlySpan <byte>(source, position, blockSize).CopyTo(memory.Span);
                    buffer.ConfirmWrite(blockSize);

                    position += blockSize;
                }

                var empty = buffer.Read();
                Assert.True(empty.IsEmpty);

                buffer.Flush();

                var firstPart = buffer.Read();
                var array     = firstPart.Slice(0, 500).ToArray();
                buffer.ConfirmRead(array.Length);

                Assert.Equal(source.Take(500), array);

                var secondPart = buffer.Read();
                secondPart.CopyTo(array);
                buffer.ConfirmRead(array.Length);

                Assert.Equal(source.Skip(500), array);

                empty = buffer.Read();
                Assert.True(empty.IsEmpty);
            }
        }
        public async ValueTask Flush(bool async, CancellationToken cancellationToken)
        {
            if (_currentCompression != CompressionAlgorithm.None)
            {
                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, "Internal error. The stream can't be flushed because it's compression is not completed.");
            }

            _buffer.Flush();

            var readResult = _buffer.Read();

            if (readResult.IsEmpty)
            {
                return;
            }

            foreach (var buffer in readResult)
            {
                if (async)
                {
                    await _stream.WriteAsync(buffer, cancellationToken);
                }
                else
                {
                    _stream.Write(buffer.Span);
                }
            }

            _buffer.ConfirmRead((int)readResult.Length);

            if (async)
            {
                await _stream.FlushAsync(cancellationToken);
            }
            else
            {
                _stream.Flush();
            }
        }
        public void WriteExactThreeBlocks()
        {
            var source = Enumerable.Range(0, 7 * 3).Select(n => (byte)n).ToArray();
            var buffer = new ReadWriteBuffer(7);

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    var block = buffer.GetMemory(7);
                    new Span <byte>(source, j * 7, 7).CopyTo(block.Span);
                    buffer.ConfirmWrite(7);
                }

                buffer.Flush();

                var result = buffer.Read().ToArray();
                Assert.Equal(source, result);

                buffer.ConfirmRead(result.Length);
                var empty = buffer.Read();
                Assert.True(empty.IsEmpty);
            }
        }