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)); }
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); }
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); } }
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)); }
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]); }
// 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()); } }
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); } }
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); } }
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();
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); }
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); } }
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)); }
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); } }
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); }
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())); }
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()); }
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); } }