// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Buffers; using System.Numerics; using System.Text; namespace EpicGames.UHT.Utils { /// /// Rent and return pooled buffers /// public static class UhtPoolBuffers { /// /// Rent a new buffer /// /// Minimum size of the buffer /// public static UhtPoolBuffer Rent(int size) { T[] block = ArrayPool.Shared.Rent(size); return new UhtPoolBuffer(block, size); } /// /// Return a buffer /// /// Buffer being returned public static void Return(UhtPoolBuffer buffer) { if (buffer.IsSet) { ArrayPool.Shared.Return(buffer.GetBlock()); } } } /// /// Pooled buffers /// /// public struct UhtPoolBuffer { /// /// THe backing array /// private readonly T[]? _block = null; /// /// Memory region sized to the requested size /// public Memory Memory { get; set; } /// /// Return true if we have a pool block /// public readonly bool IsSet => _block != null; /// /// Create a pool buffer /// /// Array block /// Size of the requested block public UhtPoolBuffer(T[] block, int size) { _block = block; Memory = new Memory(block, 0, size); } /// /// The backing array. The size of the array will normally be larger than the requested size. /// public readonly T[] GetBlock() { return _block ?? Array.Empty(); } /// /// Reset the memory region to the given size /// /// public void Reset(int size) { Memory = new Memory(GetBlock(), 0, size); } } /// /// Helper class for using pattern to borrow and return a buffer. /// public struct UhtRentedPoolBuffer : IDisposable { /// /// The borrowed buffer /// public UhtPoolBuffer Buffer { get; set; } /// /// Borrow a buffer with the given size /// /// The size to borrow public UhtRentedPoolBuffer(int size) { Buffer = UhtPoolBuffers.Rent(size); } /// /// Return the borrowed buffer to the cache /// public readonly void Dispose() { UhtPoolBuffers.Return(Buffer); } } /// /// Collection of helper methods to convert string builder to borrow buffers /// public static class UhtPoolBufferStringBuilderExtensions { /// /// Return a buffer initialized with the string builder. /// /// Source builder content /// Buffer that should be returned with a call to Return public static UhtRentedPoolBuffer RentPoolBuffer(this StringBuilder builder) { int length = builder.Length; UhtRentedPoolBuffer buffer = new(length); builder.CopyTo(0, buffer.Buffer.Memory.Span, length); return buffer; } /// /// Return a buffer initialized with the string builder sub string. /// /// Source builder content /// Starting index in the builder /// Length of the content /// Buffer that should be returned with a call to Return public static UhtRentedPoolBuffer RentPoolBuffer(this StringBuilder builder, int startIndex, int length) { UhtRentedPoolBuffer buffer = new(length); builder.CopyTo(startIndex, buffer.Buffer.Memory.Span, length); return buffer; } } /// /// Cached character buffer system. /// /// Invoke UhtBuffer.Borrow method to get a buffer of the given size. /// Invoke UhtBuffer.Return to return the buffer to the cache. /// [Obsolete("Use UhtPoolBuffer instead of UhtBuffer")] public class UhtBuffer { /// /// Any requests of the given size or smaller will be placed in bucket zero with the given size. /// private const int MinSize = 1024 * 16; /// /// Adjustment to the bucket index to account for the minimum bucket size /// private static readonly int s_buckedAdjustment = BitOperations.Log2((uint)UhtBuffer.MinSize); /// /// Total number of supported buckets /// private static readonly int s_bucketCount = 32 - UhtBuffer.s_buckedAdjustment; /// /// Bucket lookaside list /// private static readonly UhtBuffer?[] s_lookAsideArray = new UhtBuffer?[UhtBuffer.s_bucketCount]; /// /// The bucket index associated with the buffer /// private int Bucket { get; } /// /// Single list link to the next cached buffer /// private UhtBuffer? NextBuffer { get; set; } = null; /// /// The backing character block. The size of the array will normally be larger than the /// requested size. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] public char[] Block; /// /// Memory region sized to the requested size /// public Memory Memory { get; set; } /// /// Construct a new buffer /// /// The initial size of the buffer /// The bucket associated with the buffer /// The size all blocks in this bucket private UhtBuffer(int size, int bucket, int bucketSize) { Block = new char[bucketSize]; Bucket = bucket; Reset(size); } /// /// Reset the memory region to the given size /// /// public void Reset(int size) { Memory = new Memory(Block, 0, size); } /// /// Borrow a new buffer of the given size /// /// Size of the buffer /// Buffer that should be returned with a call to Return public static UhtBuffer Borrow(int size) { if (size <= UhtBuffer.MinSize) { return BorrowInternal(size, 0, UhtBuffer.MinSize); } else { // Round up the size to the next larger power of two if it isn't a power of two uint usize = (uint)size; --usize; usize |= usize >> 1; usize |= usize >> 2; usize |= usize >> 4; usize |= usize >> 8; usize |= usize >> 16; ++usize; int bucket = BitOperations.Log2(usize) - UhtBuffer.s_buckedAdjustment; return BorrowInternal(size, bucket, (int)usize); } } /// /// Return a buffer initialized with the string builder. /// /// Source builder content /// Buffer that should be returned with a call to Return public static UhtBuffer Borrow(StringBuilder builder) { int length = builder.Length; UhtBuffer buffer = Borrow(length); builder.CopyTo(0, buffer.Memory.Span, length); return buffer; } /// /// Return a buffer initialized with the string builder sub string. /// /// Source builder content /// Starting index in the builder /// Length of the content /// Buffer that should be returned with a call to Return public static UhtBuffer Borrow(StringBuilder builder, int startIndex, int length) { UhtBuffer buffer = Borrow(length); builder.CopyTo(startIndex, buffer.Memory.Span, length); return buffer; } /// /// Return the buffer to the cache. The buffer should no longer be accessed. /// /// The buffer to be returned. public static void Return(UhtBuffer buffer) { lock (UhtBuffer.s_lookAsideArray) { buffer.NextBuffer = UhtBuffer.s_lookAsideArray[buffer.Bucket]; UhtBuffer.s_lookAsideArray[buffer.Bucket] = buffer; } } /// /// Internal helper to allocate a buffer /// /// The initial size of the buffer /// The bucket associated with the buffer /// The size all blocks in this bucket /// The allocated buffer private static UhtBuffer BorrowInternal(int size, int bucket, int bucketSize) { lock (UhtBuffer.s_lookAsideArray) { if (UhtBuffer.s_lookAsideArray[bucket] != null) { UhtBuffer buffer = UhtBuffer.s_lookAsideArray[bucket]!; UhtBuffer.s_lookAsideArray[bucket] = buffer.NextBuffer; buffer.Reset(size); return buffer; } } return new UhtBuffer(size, bucket, bucketSize); } } /// /// Helper class for using pattern to borrow and return a buffer. /// [Obsolete("Use UhtRentedPoolBuffer instead of UhtBorrowBuffer")] public struct UhtBorrowBuffer : IDisposable { /// /// The borrowed buffer /// public UhtBuffer Buffer { get; set; } /// /// Borrow a buffer with the given size /// /// The size to borrow public UhtBorrowBuffer(int size) { Buffer = UhtBuffer.Borrow(size); } /// /// Borrow a buffer populated with the builder contents /// /// Initial contents of the buffer public UhtBorrowBuffer(StringBuilder builder) { Buffer = UhtBuffer.Borrow(builder); } /// /// Borrow a buffer populated with the builder contents /// /// Initial contents of the buffer /// Starting index into the builder /// Length of the data in the builder public UhtBorrowBuffer(StringBuilder builder, int startIndex, int length) { Buffer = UhtBuffer.Borrow(builder, startIndex, length); } /// /// Return the borrowed buffer to the cache /// public readonly void Dispose() { UhtBuffer.Return(Buffer); } } /// /// Cached character buffer system. /// /// Invoke UhtBuffer.Borrow method to get a buffer of the given size. /// Invoke UhtBuffer.Return to return the buffer to the cache. /// [Obsolete("Use UhtPoolBuffer instead of UhtByteBuffer")] public class UhtByteBuffer { /// /// Any requests of the given size or smaller will be placed in bucket zero with the given size. /// private const int MinSize = 1024 * 16; /// /// Adjustment to the bucket index to account for the minimum bucket size /// private static readonly int s_buckedAdjustment = BitOperations.Log2((uint)UhtByteBuffer.MinSize); /// /// Total number of supported buckets /// private static readonly int s_bucketCount = 32 - UhtByteBuffer.s_buckedAdjustment; /// /// Bucket lookaside list /// private static readonly UhtByteBuffer?[] s_lookAsideArray = new UhtByteBuffer?[UhtByteBuffer.s_bucketCount]; /// /// The bucket index associated with the buffer /// private int Bucket { get; } /// /// Single list link to the next cached buffer /// private UhtByteBuffer? NextBuffer { get; set; } = null; /// /// The backing character block. The size of the array will normally be larger than the /// requested size. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] public byte[] Block; /// /// Memory region sized to the requested size /// public Memory Memory { get; set; } /// /// Construct a new buffer /// /// The initial size of the buffer /// The bucket associated with the buffer /// The size all blocks in this bucket private UhtByteBuffer(int size, int bucket, int bucketSize) { Block = new byte[bucketSize]; Bucket = bucket; Reset(size); } /// /// Reset the memory region to the given size /// /// public void Reset(int size) { Memory = new Memory(Block, 0, size); } /// /// Borrow a new buffer of the given size /// /// Size of the buffer /// Buffer that should be returned with a call to Return public static UhtByteBuffer Borrow(int size) { if (size <= UhtByteBuffer.MinSize) { return BorrowInternal(size, 0, UhtByteBuffer.MinSize); } else { // Round up the size to the next larger power of two if it isn't a power of two uint usize = (uint)size; --usize; usize |= usize >> 1; usize |= usize >> 2; usize |= usize >> 4; usize |= usize >> 8; usize |= usize >> 16; ++usize; int bucket = BitOperations.Log2(usize) - UhtByteBuffer.s_buckedAdjustment; return BorrowInternal(size, bucket, (int)usize); } } /// /// Return the buffer to the cache. The buffer should no longer be accessed. /// /// The buffer to be returned. public static void Return(UhtByteBuffer buffer) { lock (UhtByteBuffer.s_lookAsideArray) { buffer.NextBuffer = UhtByteBuffer.s_lookAsideArray[buffer.Bucket]; UhtByteBuffer.s_lookAsideArray[buffer.Bucket] = buffer; } } /// /// Internal helper to allocate a buffer /// /// The initial size of the buffer /// The bucket associated with the buffer /// The size all blocks in this bucket /// The allocated buffer private static UhtByteBuffer BorrowInternal(int size, int bucket, int bucketSize) { lock (UhtByteBuffer.s_lookAsideArray) { if (UhtByteBuffer.s_lookAsideArray[bucket] != null) { UhtByteBuffer buffer = UhtByteBuffer.s_lookAsideArray[bucket]!; UhtByteBuffer.s_lookAsideArray[bucket] = buffer.NextBuffer; buffer.Reset(size); return buffer; } } return new UhtByteBuffer(size, bucket, bucketSize); } } /// /// Helper class for using pattern to borrow and return a buffer. /// [Obsolete("Use UhtRentedPoolBuffer instead of UhtBorrowByteBuffer")] public struct UhtBorrowByteBuffer : IDisposable { /// /// The borrowed buffer /// public UhtByteBuffer Buffer { get; set; } /// /// Borrow a buffer with the given size /// /// The size to borrow public UhtBorrowByteBuffer(int size) { Buffer = UhtByteBuffer.Borrow(size); } /// /// Return the borrowed buffer to the cache /// public readonly void Dispose() { UhtByteBuffer.Return(Buffer); } } }