Files
UnrealEngineUWP/Engine/Source/Programs/Shared/EpicGames.UHT/Utils/UhtBuffer.cs
Tim Smith f813631481 More coding standard fixes
#rnx
#preflight 6270399a645c64f3a2571585

[CL 20014974 by Tim Smith in ue5-main branch]
2022-05-02 16:23:21 -04:00

223 lines
6.6 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Numerics;
using System.Text;
namespace EpicGames.UHT.Utils
{
/// <summary>
/// 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.
/// </summary>
public class UhtBuffer
{
/// <summary>
/// Any requests of the given size or smaller will be placed in bucket zero with the given size.
/// </summary>
private const int MinSize = 1024 * 16;
/// <summary>
/// Adjustment to the bucket index to account for the minimum bucket size
/// </summary>
private static readonly int s_buckedAdjustment = BitOperations.Log2((uint)UhtBuffer.MinSize);
/// <summary>
/// Total number of supported buckets
/// </summary>
private static readonly int s_bucketCount = 32 - UhtBuffer.s_buckedAdjustment;
/// <summary>
/// Bucket lookaside list
/// </summary>
private static readonly UhtBuffer?[] s_lookAsideArray = new UhtBuffer?[UhtBuffer.s_bucketCount];
/// <summary>
/// The bucket index associated with the buffer
/// </summary>
private int Bucket { get; }
/// <summary>
/// Single list link to the next cached buffer
/// </summary>
private UhtBuffer? NextBuffer { get; set; } = null;
/// <summary>
/// The backing character block. The size of the array will normally be larger than the
/// requested size.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
public char[] Block;
/// <summary>
/// Memory region sized to the requested size
/// </summary>
public Memory<char> Memory { get; set; }
/// <summary>
/// Construct a new buffer
/// </summary>
/// <param name="size">The initial size of the buffer</param>
/// <param name="bucket">The bucket associated with the buffer</param>
/// <param name="bucketSize">The size all blocks in this bucket</param>
private UhtBuffer(int size, int bucket, int bucketSize)
{
this.Block = new char[bucketSize];
this.Bucket = bucket;
Reset(size);
}
/// <summary>
/// Reset the memory region to the given size
/// </summary>
/// <param name="size"></param>
public void Reset(int size)
{
this.Memory = new Memory<char>(this.Block, 0, size);
}
/// <summary>
/// Borrow a new buffer of the given size
/// </summary>
/// <param name="size">Size of the buffer</param>
/// <returns>Buffer that should be returned with a call to Return</returns>
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);
}
}
/// <summary>
/// Return a buffer initialized with the string builder.
/// </summary>
/// <param name="builder">Source builder content</param>
/// <returns>Buffer that should be returned with a call to Return</returns>
public static UhtBuffer Borrow(StringBuilder builder)
{
int length = builder.Length;
UhtBuffer buffer = Borrow(length);
builder.CopyTo(0, buffer.Memory.Span, length);
return buffer;
}
/// <summary>
/// Return a buffer initialized with the string builder sub string.
/// </summary>
/// <param name="builder">Source builder content</param>
/// <param name="startIndex">Starting index in the builder</param>
/// <param name="length">Length of the content</param>
/// <returns>Buffer that should be returned with a call to Return</returns>
public static UhtBuffer Borrow(StringBuilder builder, int startIndex, int length)
{
UhtBuffer buffer = Borrow(length);
builder.CopyTo(startIndex, buffer.Memory.Span, length);
return buffer;
}
/// <summary>
/// Return the buffer to the cache. The buffer should no longer be accessed.
/// </summary>
/// <param name="buffer">The buffer to be returned.</param>
public static void Return(UhtBuffer buffer)
{
lock (UhtBuffer.s_lookAsideArray)
{
buffer.NextBuffer = UhtBuffer.s_lookAsideArray[buffer.Bucket];
UhtBuffer.s_lookAsideArray[buffer.Bucket] = buffer;
}
}
/// <summary>
/// Internal helper to allocate a buffer
/// </summary>
/// <param name="size">The initial size of the buffer</param>
/// <param name="bucket">The bucket associated with the buffer</param>
/// <param name="bucketSize">The size all blocks in this bucket</param>
/// <returns>The allocated buffer</returns>
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);
}
}
/// <summary>
/// Helper class for using pattern to borrow and return a buffer.
/// </summary>
public struct UhtBorrowBuffer : IDisposable
{
/// <summary>
/// The borrowed buffer
/// </summary>
public UhtBuffer Buffer { get; set; }
/// <summary>
/// Borrow a buffer with the given size
/// </summary>
/// <param name="size">The size to borrow</param>
public UhtBorrowBuffer(int size)
{
this.Buffer = UhtBuffer.Borrow(size);
}
/// <summary>
/// Borrow a buffer populated with the builder contents
/// </summary>
/// <param name="builder">Initial contents of the buffer</param>
public UhtBorrowBuffer(StringBuilder builder)
{
this.Buffer = UhtBuffer.Borrow(builder);
}
/// <summary>
/// Borrow a buffer populated with the builder contents
/// </summary>
/// <param name="builder">Initial contents of the buffer</param>
/// <param name="startIndex">Starting index into the builder</param>
/// <param name="length">Length of the data in the builder</param>
public UhtBorrowBuffer(StringBuilder builder, int startIndex, int length)
{
this.Buffer = UhtBuffer.Borrow(builder, startIndex, length);
}
/// <summary>
/// Return the borrowed buffer to the cache
/// </summary>
public void Dispose()
{
UhtBuffer.Return(this.Buffer);
}
}
}