Last active
January 15, 2023 22:08
-
-
Save MiloszKrajewski/75a8c8dca999ba1bae5f81c6b8b5ff44 to your computer and use it in GitHub Desktop.
Revisions
-
MiloszKrajewski revised this gist
Jan 15, 2023 . 1 changed file with 13 additions and 20 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -34,14 +34,11 @@ static async ValueTask<ReadOnlyMemory<byte>> ReceiveStringAsync(WebSocket socket } // as so far we might have worked with rented array we need to copy now return CloneWrittenBytes(dynamicBuffer, fixedBuffer, fixedBufferPosition); } static Memory<byte> GetWritableMemory( int size, ref ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, int fixedBufferPosition) { // above fixed buffer size already if (dynamicBuffer is not null) @@ -52,19 +49,12 @@ static Memory<byte> GetWritableMemory( return fixedBuffer.Slice(fixedBufferPosition, size); // we just ran out of space in fixed buffer dynamicBuffer = new ArrayBufferWriter<byte>(); return dynamicBuffer.GetMemory(size); } static void AdvanceAfterRead( int size, ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, ref int fixedBufferPosition) { if (dynamicBuffer is not null) { @@ -79,10 +69,13 @@ static void AdvanceAfterRead( } } static byte[] CloneWrittenBytes( ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, int fixedBufferPosition) { var fixedBytes = fixedBuffer.Slice(0, fixedBufferPosition); var dynamicBytes = dynamicBuffer?.WrittenMemory; var result = new byte[fixedBytes.Length + dynamicBytes?.Length ?? 0]; fixedBytes.CopyTo(result); dynamicBytes?.CopyTo(result.AsMemory(fixedBufferPosition)); return result; } -
MiloszKrajewski revised this gist
Jan 15, 2023 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -52,8 +52,8 @@ static Memory<byte> GetWritableMemory( return fixedBuffer.Slice(fixedBufferPosition, size); // we just ran out of space in fixed buffer // this copying could be avoided for the price of // more complex "final result composition" (from two sources) dynamicBuffer = new ArrayBufferWriter<byte>(); fixedBuffer.Slice(0, fixedBufferPosition).CopyTo(dynamicBuffer.GetMemory(fixedBufferPosition)); dynamicBuffer.Advance(fixedBufferPosition); -
MiloszKrajewski created this gist
Jan 15, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,88 @@ // * stackalloc does not work, we need pool as consumer is async so it needs Memory not Span // * this might be most likley overcomplicated and ArrayBufferWriter could be enough, // but it really tries to abuse the chance that read chunks are very small so there is // only one rent from pool and one alloc for final result // * these 3 methods could be a struct nicely encapsulating functionality but it is used // from async method so struct would be copied all the time // * these 3 methods could be a class, but I would like to limit allocation to minimum, // so it uses `ref` arguments to delegate `state` to caller and allow keeping it on stack static async ValueTask<ReadOnlyMemory<byte>> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default) { var dynamicBuffer = default(ArrayBufferWriter<byte>); // we don't need it at first var fixedBuffer = ArrayPool<byte>.Shared.Rent(0x4000); // made up size, but no need to keep small var fixedBufferPosition = 0; ValueWebSocketReceiveResult result; do { ct.ThrowIfCancellationRequested(); // get writable chunk of memory from preallocated pool var writable = GetWritableMemory(128, ref dynamicBuffer, fixedBuffer, fixedBufferPosition); // not reading into buffer and then writing to stream, just wrinting straight to this dynamic buffer result = await socket.ReceiveAsync(writable, ct); // updated state after read AdvanceAfterRead(result.Count, dynamicBuffer, fixedBuffer, ref fixedBufferPosition); } while (!result.EndOfMessage); if (result.MessageType != WebSocketMessageType.Text || result.Count == 0) { throw new Exception("Unexpected message"); } // as so far we might have worked with rented array we need to copy now return GetWrittenBytes(dynamicBuffer, fixedBuffer, fixedBufferPosition).ToArray(); } static Memory<byte> GetWritableMemory( int size, ref ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, int fixedBufferPosition) { // above fixed buffer size already if (dynamicBuffer is not null) return dynamicBuffer.GetMemory(size); // still in fixed buffer range if (fixedBufferPosition + size <= fixedBuffer.Length) return fixedBuffer.Slice(fixedBufferPosition, size); // we just ran out of space in fixed buffer // this could be avoided for the price of more complex // "final result composition" (from two sources) dynamicBuffer = new ArrayBufferWriter<byte>(); fixedBuffer.Slice(0, fixedBufferPosition).CopyTo(dynamicBuffer.GetMemory(fixedBufferPosition)); dynamicBuffer.Advance(fixedBufferPosition); return dynamicBuffer.GetMemory(size); } static void AdvanceAfterRead( int size, ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, ref int fixedBufferPosition) { if (dynamicBuffer is not null) { dynamicBuffer.Advance(size); } else { // what just happened? if (fixedBufferPosition + size > fixedBuffer.Length) throw new ArgumentException("No way you can advance by that much!"); fixedBufferPosition += size; } } static ReadOnlyMemory<byte> GetWrittenBytes( ArrayBufferWriter<byte> dynamicBuffer, Memory<byte> fixedBuffer, int fixedBufferPosition) => dynamicBuffer is not null ? dynamicBuffer.WrittenMemory : fixedBuffer.Slice(0, fixedBufferPosition);