/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Shared/Buffers.MemoryPool/UnmanagedBufferAllocator.cs
174 строки
6 KB
Aaron Robinson
Remove GCHandles from SendHeaders() code path (#44561)
24 окт 2022, 08:48
Не верифицирован
24 окт 2022, 08:48
a5ae53a
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; /// <summary> /// Allocator that manages blocks of unmanaged memory. /// </summary> internal unsafe struct UnmanagedBufferAllocator : IDisposable { private readonly int _blockSize; private int _currentBlockCount; private void** _currentAlloc; private byte* _currentBlock; /// <summary> /// The default block size for the allocator. /// </summary> /// <remarks> /// This size assumes a common page size and provides an accommodation /// for the pointer chain used to track allocated blocks. /// </remarks> public static int DefaultBlockSize => 4096 - sizeof(void*); /// <summary> /// Instantiate an <see cref="UnmanagedBufferAllocator"/> instance. /// </summary> /// <param name="blockSize">The unmanaged memory block size in bytes.</param> public UnmanagedBufferAllocator(int blockSize = 0) { Debug.Assert(_blockSize >= 0); _blockSize = blockSize == 0 ? DefaultBlockSize : blockSize; _currentBlockCount = -1; _currentAlloc = null; _currentBlock = null; } /// <summary> /// Allocate the requested amount of space from the allocator. /// </summary> /// <typeparam name="T">The type requested</typeparam> /// <param name="count">The count in <typeparamref name="T"/> units</param> /// <returns>A pointer to the reserved memory.</returns> /// <remarks> /// The allocated memory is uninitialized. /// </remarks> public T* AllocAsPointer<T>(int count) where T : unmanaged { int toAlloc = checked(count * sizeof(T)); Span<byte> alloc = GetSpan(toAlloc, out bool mustCommit); if (mustCommit) { Commit(toAlloc); } return (T*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(alloc)); } /// <summary> /// Allocate the requested amount of space from the allocator. /// </summary> /// <typeparam name="T">The type requested</typeparam> /// <param name="count">The count in <typeparamref name="T"/> units</param> /// <returns>A Span to the reserved memory.</returns> /// <remarks> /// The allocated memory is uninitialized. /// </remarks> public Span<T> AllocAsSpan<T>(int count) where T : unmanaged { return new Span<T>(AllocAsPointer<T>(count), count); } /// <summary> /// Get pointer to bytes for the supplied string in UTF-8. /// </summary> /// <param name="myString">The string</param> /// <param name="length">The length of the returned byte buffer.</param> /// <returns>A pointer to the buffer of bytes</returns> public byte* GetHeaderEncodedBytes(string myString, out int length) { Debug.Assert(myString is not null); // Compute the maximum amount of bytes needed for the given string. // Include an extra byte for the null terminator. int maxAlloc = checked(Encoding.UTF8.GetMaxByteCount(myString.Length) + 1); Span<byte> buffer = GetSpan(maxAlloc, out bool mustCommit); length = Encoding.UTF8.GetBytes(myString, buffer); // Write a null terminator - the GetBytes() API doesn't add one. buffer[length] = 0; if (mustCommit) { // Let the writer know how much was used. Plus 1 for the null terminator above. Commit(length + 1); } return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); } /// <inheritdoc /> public void Dispose() { void** curr = _currentAlloc; while (curr != null) { // Follow the pointer chain to delete all allocations. void** next = (void**)*curr; NativeMemory.Free(curr); curr = next; } } private Span<byte> GetSpan(int sizeHint, out bool mustCommit) { Debug.Assert(sizeHint >= 0); // Allocation request that is beyond the block size if (sizeHint > _blockSize) { // When the block size is exceeded, the caller // can ignore committing because the shared block isn't // being used. Instead we are allocating an exclusive block. mustCommit = false; return new Span<byte>(Alloc(sizeHint), sizeHint); } // Check if there is enough room in the current block if (sizeHint > _currentBlockCount) { NewBlock(); } mustCommit = true; return new Span<byte>(_currentBlock, _currentBlockCount); } private void Commit(int count) { Debug.Assert(count >= 0); // We always consume pointer alignment sizes to ensure // the next space to return is always at least pointer aligned. count = (count + sizeof(void*) - 1) & ~(sizeof(void*) - 1); _currentBlockCount -= count; if (_currentBlockCount > 0) { _currentBlock += count; } } private byte* Alloc(int size) { // Allocate an extra pointer to create the allocation chain var newBlock = (void**)NativeMemory.Alloc((nuint)(size + sizeof(void*))); // Use the first pointer in the allocation to store the // previous block address. *newBlock = _currentAlloc; _currentAlloc = newBlock; return (byte*)&newBlock[1]; } private void NewBlock() { _currentBlock = Alloc(_blockSize); _currentBlockCount = _blockSize; } }