using System.Buffers; using System.Net.Quic; using System.Runtime.InteropServices; using Meum.Core.Messages; namespace Meum.Core; public class QuicStreamWrapper : IAsyncDisposable { private QuicStream _stream; public QuicStreamWrapper(QuicStream stream) { _stream = stream; } public async ValueTask ReadStructAsync(CancellationToken ct = default) where T : struct { byte[] buffer = ArrayPool.Shared.Rent(Marshal.SizeOf(typeof(T))); var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { await _stream.ReadExactlyAsync(buffer, ct); return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T))!; } finally { handle.Free(); ArrayPool.Shared.Return(buffer); } } public ValueTask WriteStructAsync(T msg_struct, CancellationToken ct = default) where T : struct { byte[] buffer = ArrayPool.Shared.Rent(Marshal.SizeOf(typeof(T))); var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { Marshal.StructureToPtr(msg_struct, handle.AddrOfPinnedObject(), false); return _stream.WriteAsync(buffer, ct); } finally { handle.Free(); ArrayPool.Shared.Return(buffer); } } public ValueTask ReadAsync(Memory buffer, CancellationToken ct = default) => _stream.ReadAsync(buffer, ct); public ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken ct = default) => _stream.WriteAsync(buffer, ct); public ValueTask WriteCodeMessageAsync(MessageTypeCode messageTypeCode, CancellationToken ct = default) { CodeMessage m = new CodeMessage(messageTypeCode); return WriteStructAsync(m, ct); } public async ValueTask ReadCodeMessageAsync(CancellationToken ct = default) { CodeMessage m = await ReadStructAsync(ct); m.ThrowIfInvalid(); return m.type_code; } public async ValueTask ReadDataMessageHeaderAsync(CancellationToken ct = default) { var m = await ReadStructAsync(ct); m.ThrowIfInvalid(); return m; } //TODO // public async ValueTask<> public async ValueTask DisposeAsync() { _stream.Close(); await _stream.DisposeAsync(); } }