| File: System\Net\Quic\Internal\MsQuicBuffers.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Quic\src\System.Net.Quic.csproj (System.Net.Quic) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using Microsoft.Quic; namespace System.Net.Quic; /// <summary> /// Helper class to convert managed data into QUIC_BUFFER* consumable by MsQuic. /// It also allows reuse with repeated Reset/Initialize calls, e.g. new, Initialize, (use), Reset, Initialize, (use), Reset, Initialize, (use), Dispose. /// Note that since this is struct and there's no finalizer, Dispose must be always called to release the unmanaged memory allocated by this struct. /// </summary> internal unsafe struct MsQuicBuffers : IDisposable { // Native memory block which holds the pinned memory pointers from _handles and can be passed to MsQuic as QUIC_BUFFER*. private QUIC_BUFFER* _buffers; // Number of QUIC_BUFFER instance currently allocated in _buffers, so that we can reuse the memory instead of reallocating. private int _count; public MsQuicBuffers() { _buffers = null; _count = 0; } public QUIC_BUFFER* Buffers => _buffers; public int Count => _count; private void FreeNativeMemory() { QUIC_BUFFER* buffers = _buffers; _buffers = null; _count = 0; NativeMemory.Free(buffers); } private void Reserve(int count) { if (count > _count) { FreeNativeMemory(); _buffers = (QUIC_BUFFER*)NativeMemory.AllocZeroed((nuint)count, (nuint)sizeof(QUIC_BUFFER)); _count = count; } } private void SetBuffer(int index, ReadOnlyMemory<byte> buffer) { Debug.Assert(index < _count); Debug.Assert(_buffers[index].Buffer is null); Debug.Assert(_buffers[index].Length == 0); _buffers[index].Buffer = (byte*)NativeMemory.Alloc((nuint)buffer.Length, (nuint)sizeof(byte)); _buffers[index].Length = (uint)buffer.Length; buffer.Span.CopyTo(_buffers[index].Span); } /// <summary> /// Initializes QUIC_BUFFER* with data from inputs, converted via toBuffer. /// Note that the struct either needs to be freshly created via new or previously cleaned up with Reset. /// </summary> /// <param name="inputs">Inputs to get their byte array, copy them to be passed to MsQuic as QUIC_BUFFER*.</param> /// <param name="toBuffer">Method extracting byte array from the inputs, e.g. applicationProtocol.Protocol.</param> /// <typeparam name="T">The type of the inputs.</typeparam> public void Initialize<T>(IList<T> inputs, Func<T, ReadOnlyMemory<byte>> toBuffer) { Reserve(inputs.Count); for (int i = 0; i < inputs.Count; ++i) { SetBuffer(i, toBuffer(inputs[i])); } } /// <summary> /// Initializes QUIC_BUFFER* with the provided buffer. /// Note that the struct either needs to be freshly created via new or previously cleaned up with Reset. /// </summary> /// <param name="buffer">Buffer to be passed to MsQuic as QUIC_BUFFER*.</param> public void Initialize(ReadOnlyMemory<byte> buffer) { Reserve(1); SetBuffer(0, buffer); } /// <summary> /// Release the native memory of individual buffers and allows reuse of this struct. /// </summary> public void Reset() { for (int i = 0; i < _count; ++i) { if (_buffers[i].Buffer is null) { break; } byte* buffer = _buffers[i].Buffer; _buffers[i].Buffer = null; _buffers[i].Length = 0; NativeMemory.Free(buffer); } } /// <summary> /// Releases all the native memory. /// </summary> public void Dispose() { Reset(); FreeNativeMemory(); } }