private async Task CompleteBatch(bool flushBatch, CancellationToken token)
 {
     if (flushBatch || currentBatch.WrittenCount >= desiredBatchSize)
     {
         foreach (var bw in fifow.AddWorkItem(currentBatch, token))
         {
             await BatchToStreamAsync(bw, token).ConfigureAwait(false);
         }
         currentBatch     = objPoolOutputBatch.Get();
         desiredBatchSize = batchEstimator.RecomendedBatchSize;
     }
 }
 byte[] BuildMessage <T>(int methodId, T value)
 {
     using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter())
     {
         var writer = new MessagePackWriter(buffer);
         writer.WriteArrayHeader(2);
         writer.WriteInt32(methodId);
         MessagePackSerializer.Serialize(ref writer, value, serializerOptions);
         writer.Flush();
         return(buffer.WrittenSpan.ToArray());
     }
 }
        public async Task Test_IBufferWriterStream_WriteAsync_Memory()
        {
            ArrayPoolBufferWriter <byte> writer = new ArrayPoolBufferWriter <byte>();
            Stream stream = ((IBufferWriter <byte>)writer).AsStream();

            Memory <byte> data = Test_MemoryStream.CreateRandomData(64);

            // Same as the other asynchronous test above, but writing from a Memory<T>
            await stream.WriteAsync(data);

            Assert.AreEqual(writer.WrittenCount, data.Length);
            Assert.IsTrue(data.Span.SequenceEqual(writer.WrittenSpan));
        }
Esempio n. 4
0
        private async Task BufferFromStreamAsync(Stream stream, ArrayPoolBufferWriter <byte> buf, int length, CancellationToken token)
        {
            WriteHeader(buf, length);
            Memory <byte> mem     = buf.GetMemory(length).Slice(0, length);
            int           totRead = await stream.ReadAsync(mem, token).ConfigureAwait(false);

            token.ThrowIfCancellationRequested();
            if (totRead != length)
            {
                throw new StreamSerializationException($"Unexpected length read while deserializing body. Expected {length}, got {totRead}");
            }
            buf.Advance(length);
        }
Esempio n. 5
0
 public override void SetPayloadLength(int payloadLength)
 {
     if (buffer_ != null)
     {
         throw new InvalidOperationException(
                   "SetPayloadLength is called after the buffer is already created!");
     }
     // Length might be -1 (i.e. "unknown"), don't pre-create a buffer in this case.
     if (payloadLength > 0)
     {
         buffer_ = new ArrayPoolBufferWriter <byte>(payloadLength);
     }
 }
Esempio n. 6
0
 public byte[] GetDataArray(bool decompress)
 {
     if (decompress && IsCompressed)
     {
         using var bufferWriter     = new ArrayPoolBufferWriter <byte>(Convert.ToInt32(Size));
         using var decompressStream = GetDecompressDataStream(true);
         decompressStream.CopyTo(bufferWriter.AsStream());
         return(bufferWriter.WrittenSpan.ToArray());
     }
     else
     {
         return(_data);
     }
 }
        public void Serialize(ref MessagePackWriter writer, Frame <T> value, MessagePackSerializerOptions options)
        {
            ArrayPoolBufferWriter <byte> bodyWriter = objPoolBufferWriterBodies.Get();

            try
            {
                var formatterT = options.Resolver.GetFormatterWithVerify <T>();
                Serialize(ref writer, value.Item, options, bodyWriter, formatterT);
            }
            finally
            {
                objPoolBufferWriterBodies.Return(bodyWriter);
            }
        }
        public void Test_IBufferWriterStream_Write_Span()
        {
            ArrayPoolBufferWriter <byte> writer = new ArrayPoolBufferWriter <byte>();
            Stream stream = ((IBufferWriter <byte>)writer).AsStream();

            Memory <byte> data = Test_MemoryStream.CreateRandomData(64);

            // This will use the extension when on .NET Standard 2.0,
            // as the Stream class doesn't have Spam<T> or Memory<T>
            // public APIs there. This is the case eg. on UWP as well.
            stream.Write(data.Span);

            Assert.AreEqual(writer.WrittenCount, data.Length);
            Assert.IsTrue(data.Span.SequenceEqual(writer.WrittenSpan));
        }
Esempio n. 9
0
        private static void Test_IBufferWriterExtensions_WriteReadItem <T>(T a, T b)
            where T : IEquatable <T>
        {
            ArrayPoolBufferWriter <T> writer = new ArrayPoolBufferWriter <T>();

            writer.Write(a);
            writer.Write(b);

            Assert.AreEqual(2, writer.WrittenCount);

            ReadOnlySpan <T> span = writer.WrittenSpan;

            Assert.AreEqual(a, span[0]);
            Assert.AreEqual(b, span[1]);
        }
Esempio n. 10
0
            // Write a marker that is the beginning of the stream.
            // NOTE: To prevent buffering by AWS ALB or reverse-proxy.
            static byte[] BuildMarkerResponse()
            {
                using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter())
                {
                    var writer = new MessagePackWriter(buffer);

                    // response:  [messageId, methodId, response]
                    // HACK: If the ID of the message is `-1`, the client will ignore the message.
                    writer.WriteArrayHeader(3);
                    writer.Write(-1);
                    writer.Write(0);
                    writer.WriteNil();
                    writer.Flush();
                    return(buffer.WrittenSpan.ToArray());
                }
            }
Esempio n. 11
0
        public static byte[] BuildRemoteResponseError(int messageId, string exception, MessagePackSerializerOptions options)
        {
            using (var bufferWriter = new ArrayPoolBufferWriter())
            {
                var writer = new MessagePackWriter(bufferWriter);
                writer.WriteArrayHeader(3);
                writer.Write((byte)MessageType.RemoteError);
                MessagePackSerializer.Serialize(ref writer, messageId, options);
                MessagePackSerializer.Serialize(ref writer, exception, options);
                writer.Flush();

                var finalBuffer = new byte[4 + bufferWriter.WrittenCount];
                Unsafe.WriteUnaligned(ref finalBuffer[0], bufferWriter.WrittenCount);
                bufferWriter.WrittenSpan.CopyTo(finalBuffer.AsSpan(4));
                return(finalBuffer);
            }
        }
Esempio n. 12
0
        public static byte[] BuildRemoteRequestMessage <TRequest>(Type requestType, Type responseType, int messageId, TRequest message, MessagePackSerializerOptions options)
        {
            using (var bufferWriter = new ArrayPoolBufferWriter())
            {
                var writer = new MessagePackWriter(bufferWriter);
                writer.WriteArrayHeader(3);
                writer.Write((byte)MessageType.RemoteRequest);
                MessagePackSerializer.Serialize(ref writer, new RequestHeader(messageId, requestType.FullName !, responseType.FullName !), options);
                MessagePackSerializer.Serialize(ref writer, message, options);
                writer.Flush();

                var finalBuffer = new byte[4 + bufferWriter.WrittenCount];
                Unsafe.WriteUnaligned(ref finalBuffer[0], bufferWriter.WrittenCount);
                bufferWriter.WrittenSpan.CopyTo(finalBuffer.AsSpan(4));
                return(finalBuffer);
            }
        }
Esempio n. 13
0
        static async Task ParseFile(string filePath)
        {
            using (FileStream stream = File.OpenRead(filePath))
            {
                PipeReader pipe   = PipeReader.Create(stream);
                var        result = await pipe.ReadAsync();

                while (!result.IsCompleted)
                {
                    pipe.AdvanceTo(result.Buffer.Start, result.Buffer.End);
                    result = await pipe.ReadAsync();
                }
                ArrayPoolBufferWriter <char> buffer = new ArrayPoolBufferWriter <char>();
                Encoding.UTF8.GetChars(result.Buffer, buffer);
                ParseSelf(filePath, new ReadOnlySequence <char>(buffer.WrittenMemory));
            }
        }
    public void Setup()
    {
        _item =
            L(
                L(),
                U1(MemoryOwner <byte> .Allocate(ItemCount)),
                U2(MemoryOwner <ushort> .Allocate(ItemCount)),
                U4(MemoryOwner <uint> .Allocate(ItemCount)),
                F4(MemoryOwner <float> .Allocate(ItemCount)),
                A(CreateString(ItemCount, Encoding.ASCII)),
                //J(CreateString(Math.Min(ItemCount, 512))), //JIS encoding cost more memory in coreclr
                F8(MemoryOwner <double> .Allocate(ItemCount)),
                L(
                    I1(MemoryOwner <sbyte> .Allocate(ItemCount)),
                    I2(MemoryOwner <short> .Allocate(ItemCount)),
                    I4(MemoryOwner <int> .Allocate(ItemCount)),
                    F4(MemoryOwner <float> .Allocate(ItemCount)),
                    L(
                        I1(MemoryOwner <sbyte> .Allocate(ItemCount)),
                        I2(MemoryOwner <short> .Allocate(ItemCount)),
                        I4(MemoryOwner <int> .Allocate(ItemCount)),
                        F4(MemoryOwner <float> .Allocate(ItemCount)),
                        Boolean(MemoryOwner <bool> .Allocate(ItemCount)),
                        B(MemoryOwner <byte> .Allocate(ItemCount)),
                        L(
                            A(CreateString(ItemCount, Encoding.ASCII)),
                            //J(CreateString(Math.Min(ItemCount, 512))),
                            Boolean(MemoryOwner <bool> .Allocate(ItemCount)),
                            B(MemoryOwner <byte> .Allocate(ItemCount))),
                        F8(MemoryOwner <double> .Allocate(ItemCount))),
                    Boolean(MemoryOwner <bool> .Allocate(ItemCount)),
                    B(MemoryOwner <byte> .Allocate(ItemCount)),
                    L(
                        A(CreateString(ItemCount, Encoding.ASCII)),
                        //J(CreateString(Math.Min(ItemCount, 512))),
                        Boolean(MemoryOwner <bool> .Allocate(ItemCount)),
                        B(MemoryOwner <byte> .Allocate(ItemCount))),
                    F8(MemoryOwner <double> .Allocate(ItemCount))),
                U1(MemoryOwner <byte> .Allocate(ItemCount)),
                U2(MemoryOwner <ushort> .Allocate(ItemCount)),
                U4(MemoryOwner <uint> .Allocate(ItemCount)),
                F4(MemoryOwner <float> .Allocate(ItemCount)));

        using var buffer = new ArrayPoolBufferWriter <byte>();
        _item.EncodeTo(buffer);
        _encodedBytes = buffer.WrittenMemory.ToArray();
Esempio n. 15
0
        public static byte[] BuildPubSubMessage <TKey, TMessaege>(TKey key, TMessaege message, MessagePackSerializerOptions options)
        {
            using (var bufferWriter = new ArrayPoolBufferWriter())
            {
                var writer = new MessagePackWriter(bufferWriter);
                writer.WriteArrayHeader(3);
                writer.Write((byte)MessageType.PubSub);
                MessagePackSerializer.Serialize(ref writer, key, options);
                MessagePackSerializer.Serialize(ref writer, message, options);
                writer.Flush();

                var finalBuffer = new byte[4 + bufferWriter.WrittenCount];
                Unsafe.WriteUnaligned(ref finalBuffer[0], bufferWriter.WrittenCount);
                bufferWriter.WrittenSpan.CopyTo(finalBuffer.AsSpan(4));
                return(finalBuffer);
            }
        }
            static T?FindDescendantWithBreadthFirstSearch(DependencyObject element, ref TPredicate predicate)
            {
                // We're using a pooled buffer writer to amortize allocations for the temporary collection of children
                // to visit for each level. The underlying array is deliberately just of type object and not DependencyObject
                // to reduce the number of generic instantiations and allow the rented arrays to be reused more.
                using ArrayPoolBufferWriter <object> bufferWriter = ArrayPoolBufferWriter <object> .Create();

                int childrenCount = VisualTreeHelper.GetChildrenCount(element);

                // Add the top level children
                for (int i = 0; i < childrenCount; i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(element, i);

                    if (child is T result && predicate.Match(result))
                    {
                        return(result);
                    }

                    bufferWriter.Add(child);
                }

                // Explore each depth level
                for (int i = 0; i < bufferWriter.Count; i++)
                {
                    DependencyObject parent = (DependencyObject)bufferWriter[i];

                    childrenCount = VisualTreeHelper.GetChildrenCount(parent);

                    for (int j = 0; j < childrenCount; j++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(parent, j);

                        if (child is T result && predicate.Match(result))
                        {
                            return(result);
                        }

                        bufferWriter.Add(child);
                    }
                }

                return(null);
            }
Esempio n. 17
0
        public void Test_ArrayPoolBufferWriterOfT_BufferSize(int request, int expected)
        {
            using var writer = new ArrayPoolBufferWriter <byte>();

            // Request a Span<T> of a specified size and discard it. We're just invoking this
            // method to force the ArrayPoolBufferWriter<T> instance to internally resize the
            // buffer to ensure it can contain at least this number of items. After this, we
            // can use reflection to get the internal array and ensure the size equals the
            // expected one, which matches the "round up to power of 2" logic we need. This
            // is documented within the resize method in ArrayPoolBufferWriter<T>, and it's
            // done to prevent repeated allocations of arrays in some scenarios.
            _ = writer.GetSpan(request);

            var arrayFieldInfo = typeof(ArrayPoolBufferWriter <byte>).GetField("array", BindingFlags.Instance | BindingFlags.NonPublic);

            byte[] array = (byte[])arrayFieldInfo !.GetValue(writer);

            Assert.AreEqual(array !.Length, expected);
        }
 public CollectionSerializerAsync(Stream stream, FIFOWorkerConfig fifowConfig, BatchSizeEstimatorConfig estimatorConfig, MessagePackSerializerOptions w_opts)
 {
     this.stream               = stream;
     fifow                     = new FIFOWorker <ArrayPoolBufferWriter <T>, BatchWithBufferWriters>(fifowConfig, HandleWorkerOutput);
     batchEstimator            = new BatchSizeEstimator(estimatorConfig);
     this.w_opts               = w_opts;
     objPoolBufferWriterBodies = new DefaultObjectPool <ArrayPoolBufferWriter <byte> >(
         new ArrayPoolBufferWriterObjectPoolPolicy <byte>(Math.Max(1024 * 64, estimatorConfig.DesiredBatchSize_bytes)),
         fifowConfig.MaxQueuedItems);
     objPoolBufferWriterBodyLengths = new DefaultObjectPool <ArrayPoolBufferWriter <int> >(
         new ArrayPoolBufferWriterObjectPoolPolicy <int>(1024),
         fifowConfig.MaxQueuedItems);
     objPoolOutputBatch = new DefaultObjectPool <ArrayPoolBufferWriter <T> >(
         new ArrayPoolBufferWriterObjectPoolPolicy <T>(1024),
         fifowConfig.MaxQueuedItems);
     currentBatch     = objPoolOutputBatch.Get();
     desiredBatchSize = 1;
     formatterT       = w_opts.Resolver.GetFormatterWithVerify <T>();
 }
        public void Test_ArrayPoolBufferWriterOfT_AllocateFromCustomPoolAndGetMemoryAndSpan()
        {
            var pool = new TrackingArrayPool <byte>();

            using (var writer = new ArrayPoolBufferWriter <byte>(pool))
            {
                Assert.AreEqual(pool.RentedArrays.Count, 1);

                Assert.AreEqual(writer.Capacity, 256);
                Assert.AreEqual(writer.FreeCapacity, 256);
                Assert.AreEqual(writer.WrittenCount, 0);
                Assert.IsTrue(writer.WrittenMemory.IsEmpty);
                Assert.IsTrue(writer.WrittenSpan.IsEmpty);

                Span <byte> span = writer.GetSpan(43);

                Assert.IsTrue(span.Length >= 43);

                writer.Advance(43);

                Assert.AreEqual(writer.Capacity, 256);
                Assert.AreEqual(writer.FreeCapacity, 256 - 43);
                Assert.AreEqual(writer.WrittenCount, 43);
                Assert.AreEqual(writer.WrittenMemory.Length, 43);
                Assert.AreEqual(writer.WrittenSpan.Length, 43);

                Assert.ThrowsException <ArgumentOutOfRangeException>(() => writer.Advance(-1));
                Assert.ThrowsException <ArgumentOutOfRangeException>(() => writer.GetMemory(-1));
                Assert.ThrowsException <ArgumentException>(() => writer.Advance(1024));

                writer.Dispose();

                Assert.ThrowsException <ObjectDisposedException>(() => writer.WrittenMemory);
                Assert.ThrowsException <ObjectDisposedException>(() => writer.WrittenSpan.Length);
                Assert.ThrowsException <ObjectDisposedException>(() => writer.Capacity);
                Assert.ThrowsException <ObjectDisposedException>(() => writer.FreeCapacity);
                Assert.ThrowsException <ObjectDisposedException>(() => writer.Clear());
                Assert.ThrowsException <ObjectDisposedException>(() => writer.Advance(1));
            }

            Assert.AreEqual(pool.RentedArrays.Count, 0);
        }
 protected virtual void Dispose(bool disposing)
 {
     if (disposing)
     {
         FlushAsync().Wait();
         fifow?.Dispose();
         if (currentBatch != null)
         {
             objPoolOutputBatch?.Return(currentBatch);
         }
     }
     w_opts = null;
     objPoolBufferWriterBodies      = null;
     objPoolBufferWriterBodyLengths = null;
     objPoolOutputBatch             = null;
     fifow          = null;
     batchEstimator = null;
     currentBatch   = null;
     formatterT     = null;
 }
        private void SerializeSynchronous(ref MessagePackWriter writer, TFrameList list, MessagePackSerializerOptions options)
        {
            int count = list.Count;

            writer.WriteArrayHeader(count);
            var formatterT = options.Resolver.GetFormatterWithVerify <T>();
            ArrayPoolBufferWriter <byte> bodyWriter = objPoolBufferWriterBodies.Get();

            try
            {
                foreach (Frame <T> item in list)
                {
                    FrameItemFormatter <T> .Serialize(ref writer, item, options, bodyWriter, formatterT);
                }
            }
            finally
            {
                objPoolBufferWriterBodies.Return(bodyWriter);
            }
        }
Esempio n. 22
0
        public void Test_ArrayPoolBufferWriterOfT_Clear()
        {
            using var writer = new ArrayPoolBufferWriter <byte>();

            Span <byte> span = writer.GetSpan(4).Slice(0, 4);

            byte[] data = { 1, 2, 3, 4 };

            data.CopyTo(span);

            writer.Advance(4);

            Assert.AreEqual(writer.WrittenCount, 4);
            Assert.IsTrue(span.SequenceEqual(data));

            writer.Clear();

            Assert.AreEqual(writer.WrittenCount, 0);
            Assert.IsTrue(span.ToArray().All(b => b == 0));
        }
Esempio n. 23
0
        byte[] BuildMessage <T>(int methodId, T value, Guid[] connectionIds, bool isExcept)
        {
            using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter())
            {
                // redis-format: [isExcept, [connectionIds], [raw-bloadcast-format]]
                var writer = new MessagePackWriter(buffer);

                writer.WriteArrayHeader(3);
                writer.Write(isExcept);
                NativeGuidArrayFormatter.Serialize(ref writer, connectionIds);

                writer.WriteArrayHeader(2);
                writer.WriteInt32(methodId);
                MessagePackSerializer.Serialize(ref writer, value, serializerOptions);

                writer.Flush();
                var result = buffer.WrittenSpan.ToArray();
                return(result);
            }
        }
Esempio n. 24
0
        public void Test_ArrayPoolBufferWriterOfT_AsStream()
        {
            const int GuidSize = 16;

            var writer = new ArrayPoolBufferWriter <byte>();
            var guid   = Guid.NewGuid();

            // Here we first get a stream with the extension targeting ArrayPoolBufferWriter<T>.
            // This will wrap it into a custom internal stream type and produce a write-only
            // stream that essentially mirrors the IBufferWriter<T> functionality as a stream.
            using (Stream writeStream = writer.AsStream())
            {
                writeStream.Write(guid);
            }

            Assert.AreEqual(writer.WrittenCount, GuidSize);

            // Here we get a readable stream instead, and read from it to ensure
            // the previous data was written correctly from the writeable stream.
            using (Stream stream = writer.WrittenMemory.AsStream())
            {
                Assert.AreEqual(stream.Length, GuidSize);

                byte[] result = new byte[GuidSize];

                stream.Read(result, 0, result.Length);

                // Read the guid data and ensure it matches our initial guid
                Assert.IsTrue(new Guid(result).Equals(guid));
            }

            // Do a dummy write just to ensure the writer isn't disposed here.
            // This is because we got a stream from a memory, not a memory owner.
            writer.Write((byte)42);
            writer.Advance(1);

            writer.Dispose();

            // Now check that the writer is actually disposed instead
            Assert.ThrowsException <ObjectDisposedException>(() => writer.Capacity);
        }
Esempio n. 25
0
        public void Test_IBufferWriterExtensions_WriteReadOverBytes_ReadOnlySpan()
        {
            int[] buffer = new int[128];

            var random = new Random(42);

            foreach (ref var n in buffer.AsSpan())
            {
                n = random.Next(int.MinValue, int.MaxValue);
            }

            ArrayPoolBufferWriter <byte> writer = new ArrayPoolBufferWriter <byte>();

            writer.Write <int>(buffer);

            Assert.AreEqual(sizeof(int) * buffer.Length, writer.WrittenCount);

            ReadOnlySpan <byte> span = writer.WrittenSpan;

            Assert.IsTrue(span.SequenceEqual(buffer.AsSpan().AsBytes()));
        }
Esempio n. 26
0
        internal async ValueTask WriteErrorMessage(int statusCode, string detail, Exception?ex, bool isReturnExceptionStackTraceInErrorDetail)
        {
            // MessageFormat:
            // error-response:  [messageId, statusCode, detail, StringMessage]
            byte[] BuildMessage()
            {
                using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter())
                {
                    var writer = new MessagePackWriter(buffer);
                    writer.WriteArrayHeader(4);
                    writer.Write(MessageId);
                    writer.Write(statusCode);
                    writer.Write(detail);

                    var msg = (isReturnExceptionStackTraceInErrorDetail && ex != null)
                        ? ex.ToString()
                        : null;

                    if (msg != null)
                    {
                        MessagePackSerializer.Serialize(ref writer, msg, SerializerOptions);
                    }
                    else
                    {
                        writer.WriteNil();
                    }
                    writer.Flush();
                    return(buffer.WrittenSpan.ToArray());
                }
            }

            var result = BuildMessage();

            using (await AsyncWriterLock.LockAsync().ConfigureAwait(false))
            {
                await ServiceContext.ResponseStream !.WriteAsync(result).ConfigureAwait(false);
            }
            responseSize = result.Length;
        }
        public void Test_IBufferWriterStream_WriteByte()
        {
            ArrayPoolBufferWriter <byte> writer = new ArrayPoolBufferWriter <byte>();
            Stream stream = ((IBufferWriter <byte>)writer).AsStream();

            ReadOnlySpan <byte> data = stackalloc byte[] { 1, 128, 255, 32 };

            foreach (var item in data.Enumerate())
            {
                // Since we're enumerating, we can also double check the current written count
                // at each iteration, to ensure the writes are done correctly every time.
                Assert.AreEqual(writer.WrittenCount, item.Index);

                // Write a number of bytes one by one to test this API as well
                stream.WriteByte(item.Value);
            }

            // Validate the final written length and actual data
            Assert.AreEqual(writer.WrittenCount, data.Length);
            Assert.IsTrue(data.SequenceEqual(writer.WrittenSpan));

            Assert.ThrowsException <NotSupportedException>(() => stream.ReadByte());
        }
Esempio n. 28
0
        protected async Task WriteMessageAsync <T>(int methodId, T message)
        {
            ThrowIfDisposed();

            byte[] BuildMessage()
            {
                using (var buffer = ArrayPoolBufferWriter.RentThreadStaticWriter())
                {
                    var writer = new MessagePackWriter(buffer);
                    writer.WriteArrayHeader(2);
                    writer.Write(methodId);
                    MessagePackSerializer.Serialize(ref writer, message, serializerOptions);
                    writer.Flush();
                    return(buffer.WrittenSpan.ToArray());
                }
            }

            var v = BuildMessage();

            using (await asyncLock.LockAsync())
            {
                await connection.RawStreamingCall.RequestStream.WriteAsync(v).ConfigureAwait(false);
            }
        }
        public void Test_IBufferWriterStream_Lifecycle()
        {
            ArrayPoolBufferWriter <byte> writer = new ArrayPoolBufferWriter <byte>();

            // Get a stream from a buffer writer aand validate that it can only be written to.
            // This is to mirror the same functionality as the IBufferWriter<T> interface.
            Stream stream = ((IBufferWriter <byte>)writer).AsStream();

            Assert.IsFalse(stream.CanRead);
            Assert.IsFalse(stream.CanSeek);
            Assert.IsTrue(stream.CanWrite);

            Assert.ThrowsException <NotSupportedException>(() => stream.Length);
            Assert.ThrowsException <NotSupportedException>(() => stream.Position);

            // Dispose the stream and check that no operation is now allowed
            stream.Dispose();

            Assert.IsFalse(stream.CanRead);
            Assert.IsFalse(stream.CanSeek);
            Assert.IsFalse(stream.CanWrite);
            Assert.ThrowsException <NotSupportedException>(() => stream.Length);
            Assert.ThrowsException <NotSupportedException>(() => stream.Position);
        }
 private BatchWithBufferWriters HandleWorkerOutput(ArrayPoolBufferWriter <T> batch, CancellationToken token)
 {
     try
     {
         BatchWithBufferWriters batchOut = new BatchWithBufferWriters();
         batchOut.concatenatedBodies = objPoolBufferWriterBodies.Get();
         batchOut.lengths            = objPoolBufferWriterBodyLengths.Get();
         MessagePackWriter writerBody = new MessagePackWriter(batchOut.concatenatedBodies)
         {
             OldSpec           = w_opts.OldSpec ?? false,
             CancellationToken = token
         };
         var spanIn = batch.WrittenSpan;
         int prevWrittenBytesCount = 0;
         int sumLen = 0;
         for (int ix = 0; ix < spanIn.Length; ix++)
         {
             formatterT.Serialize(ref writerBody, spanIn[ix], w_opts);
             writerBody.Flush();
             int objLen = batchOut.concatenatedBodies.WrittenCount - prevWrittenBytesCount;
             prevWrittenBytesCount          = batchOut.concatenatedBodies.WrittenCount;
             batchOut.lengths.GetSpan(1)[0] = objLen;
             batchOut.lengths.Advance(1);
             sumLen += objLen;
         }
         if (spanIn.Length > 0)
         {
             batchEstimator.UpdateEstimate((float)sumLen / (float)spanIn.Length); // update with avg instead of updating for every loop item. It's not exact, but it's faster
         }
         return(batchOut);
     }
     finally
     {
         objPoolOutputBatch.Return(batch);
     }
 }