diff --git a/src/LibHac/Fs/Buffers/Buffer.cs b/src/LibHac/Fs/Buffers/Buffer.cs index 0a4ade51..ae1400c2 100644 --- a/src/LibHac/Fs/Buffers/Buffer.cs +++ b/src/LibHac/Fs/Buffers/Buffer.cs @@ -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 { + public static Buffer Empty => default; public Memory Memory { get; } public Span Span => Memory.Span; + public int Length => Memory.Length; + public bool IsNull => Memory.IsEmpty; public Buffer(Memory 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(); } } diff --git a/src/LibHac/Fs/Buffers/IBufferManager.cs b/src/LibHac/Fs/Buffers/IBufferManager.cs index a397aca3..17fcb32f 100644 --- a/src/LibHac/Fs/Buffers/IBufferManager.cs +++ b/src/LibHac/Fs/Buffers/IBufferManager.cs @@ -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) { } diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs b/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs index 25dbc40d..92ef224b 100644 --- a/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs +++ b/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs @@ -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 - 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 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(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 with the class instead + // of raw pointers. + private MemoryHandle PinnedHeapMemoryHandle { get; set; } + private Memory HeapBuffer { get; set; } + private MemoryHandle PinnedWorkMemoryHandle { get; set; } + private Memory WorkBuffer { get; set; } + + public Result Initialize(Memory heapBuffer, int blockSize, Memory workBuffer) + { + return Initialize(heapBuffer, blockSize, QueryOrderMax((nuint)heapBuffer.Length, (nuint)blockSize), + workBuffer); + } + + public Result Initialize(Memory heapBuffer, int blockSize, int orderMax, Memory 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 heapBuffer, int blockSize) + { + return Initialize(heapBuffer, blockSize, QueryOrderMax((nuint)heapBuffer.Length, (nuint)blockSize)); + } + + public Result Initialize(Memory 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 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); + } } } diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs new file mode 100644 index 00000000..6ee26f73 --- /dev/null +++ b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs @@ -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 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(); + + int entrySize = Unsafe.SizeOf() * maxCacheCount; + int attrListSize = Unsafe.SizeOf() * 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(); + 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(); + 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 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(); + + // Copy the entries back by one. + Span source = entryBuffer.Slice(index + 1, EntryCount - (index + 1)); + Span dest = entryBuffer.Slice(index); + source.CopyTo(dest); + + // Decrement our entry count. + EntryCount--; + } + + private ref AttrInfo FindAttrInfo(BufferAttribute attr) + { + LinkedListNode curNode = AttrList.First; + + while (curNode != null) + { + if (curNode.ValueRef.GetLevel() == attr.Level) + { + return ref curNode.ValueRef; + } + + curNode = curNode.Next; + } + + return ref Unsafe.NullRef(); + } + } + + 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 heapBuffer, int blockSize, Memory 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; + } + } +} diff --git a/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs b/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs index 31ca7412..981cf386 100644 --- a/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs +++ b/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs @@ -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 where T : struct + internal class RomFsDictionary 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;