Add FileSystemBufferManager

This commit is contained in:
Alex Barney 2020-11-27 00:37:14 -07:00
parent 8e1eb0d057
commit 32a3750a92
5 changed files with 679 additions and 56 deletions

View File

@ -1,16 +1,28 @@
using System;
using System.ComponentModel;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs
{
public readonly struct Buffer
public readonly struct Buffer : IEquatable<Buffer>
{
public static Buffer Empty => default;
public Memory<byte> Memory { get; }
public Span<byte> Span => Memory.Span;
public int Length => Memory.Length;
public bool IsNull => Memory.IsEmpty;
public Buffer(Memory<byte> buffer)
{
Memory = buffer;
}
public static bool operator ==(Buffer left, Buffer right) => left.Memory.Equals(right.Memory);
public static bool operator !=(Buffer left, Buffer right) => !(left == right);
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => obj is Buffer other && Equals(other);
public bool Equals(Buffer other) => Memory.Equals(other.Memory);
public override int GetHashCode() => Memory.GetHashCode();
}
}

View File

@ -20,33 +20,33 @@ namespace LibHac.Fs
public const int BufferLevelMin = 0;
public (UIntPtr address, nuint size) AllocateBuffer(nuint size, BufferAttribute attribute) =>
public Buffer AllocateBuffer(int size, BufferAttribute attribute) =>
DoAllocateBuffer(size, attribute);
public void DeallocateBuffer(UIntPtr address, nuint size) => DoDeallocateBuffer(address, size);
public void DeallocateBuffer(Buffer buffer) => DoDeallocateBuffer(buffer);
public CacheHandle RegisterCache(UIntPtr address, nuint size, BufferAttribute attribute) =>
DoRegisterCache(address, size, attribute);
public CacheHandle RegisterCache(Buffer buffer, BufferAttribute attribute) =>
DoRegisterCache(buffer, attribute);
public (UIntPtr address, nuint size) AcquireCache(CacheHandle handle) => DoAcquireCache(handle);
public nuint GetTotalSize() => DoGetTotalSize();
public nuint GetFreeSize() => DoGetFreeSize();
public nuint GetTotalAllocatableSize() => DoGetTotalAllocatableSize();
public nuint GetFreeSizePeak() => DoGetFreeSizePeak();
public nuint GetTotalAllocatableSizePeak() => DoGetTotalAllocatableSizePeak();
public nuint GetRetriedCount() => DoGetRetriedCount();
public Buffer AcquireCache(CacheHandle handle) => DoAcquireCache(handle);
public int GetTotalSize() => DoGetTotalSize();
public int GetFreeSize() => DoGetFreeSize();
public int GetTotalAllocatableSize() => DoGetTotalAllocatableSize();
public int GetFreeSizePeak() => DoGetFreeSizePeak();
public int GetTotalAllocatableSizePeak() => DoGetTotalAllocatableSizePeak();
public int GetRetriedCount() => DoGetRetriedCount();
public void ClearPeak() => DoClearPeak();
protected abstract (UIntPtr address, nuint size) DoAllocateBuffer(nuint size, BufferAttribute attribute);
protected abstract void DoDeallocateBuffer(UIntPtr address, nuint size);
protected abstract CacheHandle DoRegisterCache(UIntPtr address, nuint size, BufferAttribute attribute);
protected abstract (UIntPtr address, nuint size) DoAcquireCache(CacheHandle handle);
protected abstract nuint DoGetTotalSize();
protected abstract nuint DoGetFreeSize();
protected abstract nuint DoGetTotalAllocatableSize();
protected abstract nuint DoGetFreeSizePeak();
protected abstract nuint DoGetTotalAllocatableSizePeak();
protected abstract nuint DoGetRetriedCount();
protected abstract Buffer DoAllocateBuffer(int size, BufferAttribute attribute);
protected abstract void DoDeallocateBuffer(Buffer buffer);
protected abstract CacheHandle DoRegisterCache(Buffer buffer, BufferAttribute attribute);
protected abstract Buffer DoAcquireCache(CacheHandle handle);
protected abstract int DoGetTotalSize();
protected abstract int DoGetFreeSize();
protected abstract int DoGetTotalAllocatableSize();
protected abstract int DoGetFreeSizePeak();
protected abstract int DoGetTotalAllocatableSizePeak();
protected abstract int DoGetRetriedCount();
protected abstract void DoClearPeak();
protected virtual void Dispose(bool disposing) { }

View File

@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Util;
using Buffer = LibHac.Fs.Buffer;
// ReSharper disable once CheckNamespace
namespace LibHac.FsSystem
@ -25,9 +26,6 @@ namespace LibHac.FsSystem
private PageList* ExternalFreeLists { get; set; }
private PageList[] InternalFreeLists { get; set; }
// Addition: Handle to allow initialization with a Memory<byte>
private MemoryHandle PinnedMemoryHandle { get; set; }
private struct PageList
{
private PageEntry* FirstPageEntry { get; set; }
@ -157,7 +155,8 @@ namespace LibHac.FsSystem
FreeLists = null;
ExternalFreeLists = null;
InternalFreeLists = null;
PinnedMemoryHandle.Dispose();
PinnedHeapMemoryHandle.Dispose();
PinnedWorkMemoryHandle.Dispose();
}
public static int GetBlockCountFromOrder(int order)
@ -192,16 +191,6 @@ namespace LibHac.FsSystem
}
}
public Result Initialize(Memory<byte> heapBuffer, uint blockSize, int orderMax)
{
PinnedMemoryHandle = heapBuffer.Pin();
var address = (UIntPtr)PinnedMemoryHandle.Pointer;
var size = (nuint)heapBuffer.Length;
return Initialize(address, size, blockSize, orderMax);
}
public Result Initialize(UIntPtr address, nuint size, nuint blockSize, void* workBuffer, nuint workBufferSize)
{
return Initialize(address, size, blockSize, QueryOrderMax(size, blockSize), workBuffer, workBufferSize);
@ -250,14 +239,14 @@ namespace LibHac.FsSystem
Assert.True(maxPageCount > 0);
// Setup the free lists
if (ExternalFreeLists is not null)
if (ExternalFreeLists != null)
{
Assert.Null(InternalFreeLists);
FreeLists = ExternalFreeLists;
}
else
{
InternalFreeLists = new PageList[OrderMax + 1];
InternalFreeLists = GC.AllocateArray<PageList>(OrderMax + 1, true);
FreeLists = (PageList*)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(InternalFreeLists));
if (InternalFreeLists == null)
return ResultFs.AllocationFailureInFileSystemBuddyHeapA.Log();
@ -405,20 +394,6 @@ namespace LibHac.FsSystem
return BlockSize;
}
// Not in original
public int GetPageBlockCountMax()
{
Assert.True(FreeLists != null);
return 1 << GetOrderMax();
}
// Not in original
public nuint GetPageSizeMax()
{
Assert.True(FreeLists != null);
return (nuint)GetPageBlockCountMax() * GetBlockSize();
}
private void DivideBuddies(PageEntry* pageEntry, int requiredOrder, int chosenOrder)
{
Assert.True(FreeLists != null);
@ -591,5 +566,81 @@ namespace LibHac.FsSystem
{
return Alignment.IsAlignedPow2(GetIndexFromPageEntry(pageEntry), (uint)GetBlockCountFromOrder(order));
}
// Addition: The below fields and methods allow using Memory<byte> with the class instead
// of raw pointers.
private MemoryHandle PinnedHeapMemoryHandle { get; set; }
private Memory<byte> HeapBuffer { get; set; }
private MemoryHandle PinnedWorkMemoryHandle { get; set; }
private Memory<byte> WorkBuffer { get; set; }
public Result Initialize(Memory<byte> heapBuffer, int blockSize, Memory<byte> workBuffer)
{
return Initialize(heapBuffer, blockSize, QueryOrderMax((nuint)heapBuffer.Length, (nuint)blockSize),
workBuffer);
}
public Result Initialize(Memory<byte> heapBuffer, int blockSize, int orderMax, Memory<byte> workBuffer)
{
PinnedWorkMemoryHandle = workBuffer.Pin();
WorkBuffer = workBuffer;
PinnedHeapMemoryHandle = heapBuffer.Pin();
HeapBuffer = heapBuffer;
var heapAddress = (UIntPtr)PinnedHeapMemoryHandle.Pointer;
var heapSize = (nuint)heapBuffer.Length;
void* workAddress = PinnedHeapMemoryHandle.Pointer;
var workSize = (nuint)heapBuffer.Length;
return Initialize(heapAddress, heapSize, (nuint)blockSize, orderMax, workAddress, workSize);
}
public Result Initialize(Memory<byte> heapBuffer, int blockSize)
{
return Initialize(heapBuffer, blockSize, QueryOrderMax((nuint)heapBuffer.Length, (nuint)blockSize));
}
public Result Initialize(Memory<byte> heapBuffer, int blockSize, int orderMax)
{
PinnedHeapMemoryHandle = heapBuffer.Pin();
HeapBuffer = heapBuffer;
var address = (UIntPtr)PinnedHeapMemoryHandle.Pointer;
var size = (nuint)heapBuffer.Length;
return Initialize(address, size, (nuint)blockSize, orderMax);
}
public Buffer AllocateBufferByOrder(int order)
{
Assert.True(!HeapBuffer.IsEmpty);
void* address = AllocateByOrder(order);
if (address == null)
return Buffer.Empty;
nuint size = GetBytesFromOrder(order);
Assert.True(size <= int.MaxValue);
// Get the offset relative to the heap start
nuint offset = (nuint)address - (nuint)PinnedHeapMemoryHandle.Pointer;
Assert.True(offset <= (nuint)HeapBuffer.Length);
// Get a slice of the Memory<byte> containing the entire heap
return new Buffer(HeapBuffer.Slice((int)offset, (int)size));
}
public void Free(Buffer buffer)
{
Assert.True(!HeapBuffer.IsEmpty);
Assert.True(!buffer.IsNull);
int order = GetOrderFromBytes((nuint)buffer.Length);
void* pointer = Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer.Span));
Free(pointer, order);
}
}
}

View File

@ -0,0 +1,562 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Util;
using Buffer = LibHac.Fs.Buffer;
using CacheHandle = System.Int64;
// ReSharper disable once CheckNamespace
namespace LibHac.FsSystem
{
public class FileSystemBufferManager : IBufferManager
{
private class CacheHandleTable
{
private struct Entry
{
private CacheHandle _handle;
private Buffer _buffer;
private BufferAttribute _attribute;
public void Initialize(CacheHandle handle, Buffer buffer, BufferAttribute attribute)
{
_handle = handle;
_buffer = buffer;
_attribute = attribute;
}
public readonly CacheHandle GetHandle() => _handle;
public readonly Buffer GetBuffer() => _buffer;
public readonly int GetSize() => _buffer.Length;
public readonly BufferAttribute GetBufferAttribute() => _attribute;
}
private struct AttrInfo
{
private int _level;
private int _cacheCount;
private int _cacheSize;
public AttrInfo(int level, int cacheCount, int cacheSize)
{
_level = level;
_cacheCount = cacheCount;
_cacheSize = cacheSize;
}
public int GetLevel() => _level;
public int GetCacheCount() => _cacheCount;
public void IncrementCacheCount() => _cacheCount++;
public void DecrementCacheCount() => _cacheCount--;
public int GetCacheSize() => _cacheSize;
public void AddCacheSize(int diff) => _cacheSize += diff;
public void SubtractCacheSize(int diff)
{
Assert.True(_cacheSize >= diff);
_cacheSize -= diff;
}
}
private Entry[] Entries { get; set; }
private int EntryCount { get; set; }
private int EntryCountMax { get; set; }
private LinkedList<AttrInfo> AttrList { get; set; } = new();
private int CacheCountMin { get; set; }
private int CacheSizeMin { get; set; }
private int TotalCacheSize { get; set; }
private CacheHandle CurrentHandle { get; set; }
// ReSharper disable once UnusedMember.Local
// We can't use an external buffer in C# without ensuring all allocated buffers are pinned.
// This function is left here anyway for completion's sake.
public static int QueryWorkBufferSize(int maxCacheCount)
{
Assert.True(maxCacheCount > 0);
int entryAlignment = sizeof(CacheHandle);
int attrInfoAlignment = Unsafe.SizeOf<nuint>();
int entrySize = Unsafe.SizeOf<Entry>() * maxCacheCount;
int attrListSize = Unsafe.SizeOf<AttrInfo>() * 0x100;
return (int)Alignment.AlignUpPow2(
(ulong)(entrySize + attrListSize + entryAlignment + attrInfoAlignment), 8);
}
public Result Initialize(int maxCacheCount)
{
// Validate pre-conditions.
Assert.True(Entries == null);
// Note: We don't have the option of using an external Entry buffer like the original
// because Entry includes managed references so we can't cast a byte* to Entry* without pinning.
// If we don't have an external buffer, try to allocate an internal one.
Entries = new Entry[maxCacheCount];
if (Entries == null)
{
return ResultFs.AllocationFailureInFileSystemBufferManagerA.Log();
}
// Set entries.
EntryCount = 0;
EntryCountMax = maxCacheCount;
Assert.True(Entries != null);
CacheCountMin = maxCacheCount / 16;
CacheSizeMin = CacheCountMin * 0x100;
return Result.Success;
}
// ReSharper disable once UnusedParameter.Local
private int GetCacheCountMin(BufferAttribute attr)
{
return CacheCountMin;
}
// ReSharper disable once UnusedParameter.Local
private int GetCacheSizeMin(BufferAttribute attr)
{
return CacheSizeMin;
}
public bool Register(out CacheHandle handle, Buffer buffer, BufferAttribute attr)
{
Unsafe.SkipInit(out handle);
// Validate pre-conditions.
Assert.True(Entries != null);
Assert.True(!Unsafe.IsNullRef(ref handle));
// Get the entry.
ref Entry entry = ref AcquireEntry(buffer, attr);
// If we don't have an entry, we can't register.
if (Unsafe.IsNullRef(ref entry))
return false;
// Get the attr info. If we have one, increment.
ref AttrInfo attrInfo = ref FindAttrInfo(attr);
if (!Unsafe.IsNullRef(ref attrInfo))
{
attrInfo.IncrementCacheCount();
attrInfo.AddCacheSize(buffer.Length);
}
else
{
// Make a new attr info and add it to the list.
// Note: Not using attr info buffer
var newInfo = new AttrInfo(attr.Level, 1, buffer.Length);
AttrList.AddLast(newInfo);
}
TotalCacheSize += buffer.Length;
handle = entry.GetHandle();
return true;
}
public bool Unregister(out Buffer buffer, CacheHandle handle)
{
Unsafe.SkipInit(out buffer);
// Validate pre-conditions.
Assert.True(Entries != null);
Assert.True(!Unsafe.IsNullRef(ref buffer));
// Find the lower bound for the entry.
for (int i = 0; i < EntryCount; i++)
{
if (Entries[i].GetHandle() == handle)
{
UnregisterCore(out buffer, ref Entries[i]);
return true;
}
}
return false;
}
// ReSharper disable UnusedParameter.Local
public bool UnregisterOldest(out Buffer buffer, BufferAttribute attr, int requiredSize = 0)
// ReSharper restore UnusedParameter.Local
{
Unsafe.SkipInit(out buffer);
// Validate pre-conditions.
Assert.True(Entries != null);
Assert.True(!Unsafe.IsNullRef(ref buffer));
// If we have no entries, we can't unregister any.
if (EntryCount == 0)
{
return false;
}
static bool CanUnregister(CacheHandleTable table, ref Entry entry)
{
ref AttrInfo attrInfo = ref table.FindAttrInfo(entry.GetBufferAttribute());
Assert.True(!Unsafe.IsNullRef(ref attrInfo));
int ccm = table.GetCacheCountMin(entry.GetBufferAttribute());
int csm = table.GetCacheSizeMin(entry.GetBufferAttribute());
return ccm < attrInfo.GetCacheCount() && csm + entry.GetSize() <= attrInfo.GetCacheSize();
}
// Find an entry, falling back to the first entry.
ref Entry entry = ref Unsafe.NullRef<Entry>();
for (int i = 0; i < EntryCount; i++)
{
if (CanUnregister(this, ref Entries[i]))
{
entry = ref Entries[i];
}
}
if (Unsafe.IsNullRef(ref entry))
{
entry = ref Entries[0];
}
Assert.True(!Unsafe.IsNullRef(ref entry));
UnregisterCore(out buffer, ref entry);
return true;
}
private void UnregisterCore(out Buffer buffer, ref Entry entry)
{
Unsafe.SkipInit(out buffer);
// Validate pre-conditions.
Assert.True(Entries != null);
Assert.True(!Unsafe.IsNullRef(ref buffer));
Assert.True(!Unsafe.IsNullRef(ref entry));
// Get the attribute info.
ref AttrInfo attrInfo = ref FindAttrInfo(entry.GetBufferAttribute());
Assert.True(!Unsafe.IsNullRef(ref attrInfo));
Assert.True(attrInfo.GetCacheCount() > 0);
Assert.True(attrInfo.GetCacheSize() >= entry.GetSize());
// Release from the attr info.
attrInfo.DecrementCacheCount();
attrInfo.SubtractCacheSize(entry.GetSize());
// Release from cached size.
Assert.True(TotalCacheSize >= entry.GetSize());
TotalCacheSize -= entry.GetSize();
// Release the entry.
buffer = entry.GetBuffer();
ReleaseEntry(ref entry);
}
public CacheHandle PublishCacheHandle()
{
Assert.True(Entries != null);
return ++CurrentHandle;
}
public int GetTotalCacheSize()
{
return TotalCacheSize;
}
private ref Entry AcquireEntry(Buffer buffer, BufferAttribute attr)
{
// Validate pre-conditions.
Assert.True(Entries != null);
ref Entry entry = ref Unsafe.NullRef<Entry>();
if (EntryCount < EntryCountMax)
{
entry = ref Entries[EntryCount];
entry.Initialize(PublishCacheHandle(), buffer, attr);
EntryCount++;
Assert.True(EntryCount == 1 || Entries[EntryCount - 1].GetHandle() < entry.GetHandle());
}
return ref entry;
}
private void ReleaseEntry(ref Entry entry)
{
// Validate pre-conditions.
Assert.True(Entries != null);
Assert.True(!Unsafe.IsNullRef(ref entry));
// Ensure the entry is valid.
Span<Entry> entryBuffer = Entries;
Assert.True(Unsafe.IsAddressGreaterThan(ref entry, ref MemoryMarshal.GetReference(entryBuffer)));
Assert.True(Unsafe.IsAddressLessThan(ref entry,
ref Unsafe.Add(ref MemoryMarshal.GetReference(entryBuffer), entryBuffer.Length)));
// Get the index of the entry.
int index = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(entryBuffer), ref entry).ToInt32() /
Unsafe.SizeOf<Entry>();
// Copy the entries back by one.
Span<Entry> source = entryBuffer.Slice(index + 1, EntryCount - (index + 1));
Span<Entry> dest = entryBuffer.Slice(index);
source.CopyTo(dest);
// Decrement our entry count.
EntryCount--;
}
private ref AttrInfo FindAttrInfo(BufferAttribute attr)
{
LinkedListNode<AttrInfo> curNode = AttrList.First;
while (curNode != null)
{
if (curNode.ValueRef.GetLevel() == attr.Level)
{
return ref curNode.ValueRef;
}
curNode = curNode.Next;
}
return ref Unsafe.NullRef<AttrInfo>();
}
}
private FileSystemBuddyHeap BuddyHeap { get; } = new();
private CacheHandleTable CacheTable { get; } = new();
private int TotalSize { get; set; }
private int PeakFreeSize { get; set; }
private int PeakTotalAllocatableSize { get; set; }
private int RetriedCount { get; set; }
private object Locker { get; } = new();
protected override void Dispose(bool disposing)
{
if (disposing)
{
BuddyHeap.Dispose();
}
base.Dispose(disposing);
}
public Result Initialize(int maxCacheCount, Memory<byte> heapBuffer, int blockSize, Memory<byte> workBuffer)
{
// Note: We can't use an external buffer for the cache handle table,
// so pass the work buffer directly to the buddy heap.
Result rc = CacheTable.Initialize(maxCacheCount);
if (rc.IsFailure()) return rc;
rc = BuddyHeap.Initialize(heapBuffer, blockSize, workBuffer);
if (rc.IsFailure()) return rc;
TotalSize = (int)BuddyHeap.GetTotalFreeSize();
PeakFreeSize = TotalSize;
PeakTotalAllocatableSize = TotalSize;
return Result.Success;
}
protected override Buffer DoAllocateBuffer(int size, BufferAttribute attribute)
{
lock (Locker)
{
return AllocateBufferImpl(size, attribute);
}
}
private Buffer AllocateBufferImpl(int size, BufferAttribute attribute)
{
int order = BuddyHeap.GetOrderFromBytes((nuint)size);
Assert.True(order >= 0);
// Allocate space on the heap
Buffer buffer;
while ((buffer = BuddyHeap.AllocateBufferByOrder(order)).IsNull)
{
// Not enough space in heap. Deallocate cached buffer and try again.
RetriedCount++;
if (!CacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute, size))
{
// No cached buffers left to deallocate.
return Buffer.Empty;
}
DeallocateBufferImpl(deallocateBuffer);
}
// Successfully allocated a buffer.
int allocatedSize = (int)BuddyHeap.GetBytesFromOrder(order);
Assert.True(size <= allocatedSize);
// Update heap stats
int freeSize = (int)BuddyHeap.GetTotalFreeSize();
PeakFreeSize = Math.Min(PeakFreeSize, freeSize);
int totalAllocatableSize = freeSize + CacheTable.GetTotalCacheSize();
PeakTotalAllocatableSize = Math.Min(PeakTotalAllocatableSize, totalAllocatableSize);
return buffer;
}
protected override void DoDeallocateBuffer(Buffer buffer)
{
lock (Locker)
{
DeallocateBufferImpl(buffer);
}
}
private void DeallocateBufferImpl(Buffer buffer)
{
Assert.True(BitUtil.IsPowerOfTwo(buffer.Length));
BuddyHeap.Free(buffer);
}
protected override CacheHandle DoRegisterCache(Buffer buffer, BufferAttribute attribute)
{
lock (Locker)
{
return RegisterCacheImpl(buffer, attribute);
}
}
private CacheHandle RegisterCacheImpl(Buffer buffer, BufferAttribute attribute)
{
CacheHandle handle;
// Try to register the handle.
while (!CacheTable.Register(out handle, buffer, attribute))
{
// Unregister a buffer and try registering again.
RetriedCount++;
if (!CacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute))
{
// Can't unregister any existing buffers.
// Register the input buffer to /dev/null.
DeallocateBufferImpl(buffer);
return CacheTable.PublishCacheHandle();
}
// Deallocate the unregistered buffer.
DeallocateBufferImpl(deallocateBuffer);
}
return handle;
}
protected override Buffer DoAcquireCache(CacheHandle handle)
{
lock (Locker)
{
return AcquireCacheImpl(handle);
}
}
private Buffer AcquireCacheImpl(CacheHandle handle)
{
if (CacheTable.Unregister(out Buffer range, handle))
{
int totalAllocatableSize = (int)BuddyHeap.GetTotalFreeSize() + CacheTable.GetTotalCacheSize();
PeakTotalAllocatableSize = Math.Min(PeakTotalAllocatableSize, totalAllocatableSize);
}
else
{
range = Buffer.Empty;
}
return range;
}
protected override int DoGetTotalSize()
{
return TotalSize;
}
protected override int DoGetFreeSize()
{
lock (Locker)
{
return GetFreeSizeImpl();
}
}
private int GetFreeSizeImpl()
{
return (int)BuddyHeap.GetTotalFreeSize();
}
protected override int DoGetTotalAllocatableSize()
{
lock (Locker)
{
return GetTotalAllocatableSizeImpl();
}
}
private int GetTotalAllocatableSizeImpl()
{
return GetFreeSizeImpl() + CacheTable.GetTotalCacheSize();
}
protected override int DoGetFreeSizePeak()
{
lock (Locker)
{
return GetFreeSizePeakImpl();
}
}
private int GetFreeSizePeakImpl()
{
return PeakFreeSize;
}
protected override int DoGetTotalAllocatableSizePeak()
{
lock (Locker)
{
return GetTotalAllocatableSizePeakImpl();
}
}
private int GetTotalAllocatableSizePeakImpl()
{
return PeakTotalAllocatableSize;
}
protected override int DoGetRetriedCount()
{
lock (Locker)
{
return GetRetriedCountImpl();
}
}
private int GetRetriedCountImpl()
{
return RetriedCount;
}
protected override void DoClearPeak()
{
lock (Locker)
{
ClearPeakImpl();
}
}
private void ClearPeakImpl()
{
PeakFreeSize = GetFreeSizeImpl();
PeakTotalAllocatableSize = GetTotalAllocatableSizeImpl();
RetriedCount = 0;
}
}
}

View File

@ -7,9 +7,7 @@ using LibHac.Util;
namespace LibHac.FsSystem.RomFs
{
// todo: Change constraint to "unmanaged" after updating to
// a newer SDK https://github.com/dotnet/csharplang/issues/1937
internal class RomFsDictionary<T> where T : struct
internal class RomFsDictionary<T> where T : unmanaged
{
private int _count;
private int _length;
@ -194,7 +192,7 @@ namespace LibHac.FsSystem.RomFs
if (value != _capacity)
{
var newBuffer = new byte[value];
Buffer.BlockCopy(Entries, 0, newBuffer, 0, _length);
System.Buffer.BlockCopy(Entries, 0, newBuffer, 0, _length);
Entries = newBuffer;
_capacity = value;