// 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);
}
}
}