From eaff8059ba1badbb06ed0b7ab021d7fb85c243f7 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 10 Mar 2022 18:39:59 -0700 Subject: [PATCH] Skeleton BlockCacheBufferedStorage --- src/LibHac/Fs/FsEnums.cs | 7 + src/LibHac/FsSystem/BitmapUtils.cs | 14 + .../FsSystem/BlockCacheBufferedStorage.cs | 249 ++++++++++++++++++ src/LibHac/FsSystem/CompressedStorage.cs | 4 +- src/LibHac/FsSystem/Impl/BlockCacheManager.cs | 7 + src/LibHac/Util/BitUtil.cs | 8 +- 6 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 src/LibHac/FsSystem/BitmapUtils.cs create mode 100644 src/LibHac/FsSystem/BlockCacheBufferedStorage.cs diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index f5c3ceb4..dc10a21e 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -287,4 +287,11 @@ public enum SdmmcPort Mmc = 0, SdCard = 1, GcAsic = 2 +} + +public enum StorageType +{ + SaveData = 0, + RomFs = 1, + Authoring = 2 } \ No newline at end of file diff --git a/src/LibHac/FsSystem/BitmapUtils.cs b/src/LibHac/FsSystem/BitmapUtils.cs new file mode 100644 index 00000000..c425ec86 --- /dev/null +++ b/src/LibHac/FsSystem/BitmapUtils.cs @@ -0,0 +1,14 @@ +using LibHac.Diag; + +namespace LibHac.FsSystem; + +public static class BitmapUtils +{ + public static uint ILog2(uint value) + { + Assert.SdkRequiresGreater(value, 0u); + + const uint intBitCount = 32; + return intBitCount - 1 - (uint)Util.BitUtil.CountLeadingZeros(value); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/BlockCacheBufferedStorage.cs b/src/LibHac/FsSystem/BlockCacheBufferedStorage.cs new file mode 100644 index 00000000..1c339870 --- /dev/null +++ b/src/LibHac/FsSystem/BlockCacheBufferedStorage.cs @@ -0,0 +1,249 @@ +using System; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.FsSystem.Impl; +using LibHac.Os; +using Buffer = LibHac.Mem.Buffer; + +// Todo: Remove warning suppressions after implementing +// ReSharper disable All +#pragma warning disable CS0414 + +namespace LibHac.FsSystem; + +public class BlockCacheBufferedStorage : IStorage +{ + public struct CacheEntry : IBlockCacheManagerEntry + { + public AccessRange Range { get; } + public bool IsValid { get; set; } + public bool IsWriteBack { get; set; } + public bool IsCached { get; set; } + public bool IsFlushing { get; set; } + public short Age { get; set; } + public ulong Handle { get; set; } + public Buffer Buffer { get; set; } + + public void Invalidate() + { + throw new NotImplementedException(); + } + + public readonly bool IsAllocated() + { + throw new NotImplementedException(); + } + } + + public struct AccessRange : IBlockCacheManagerRange + { + public long Offset { get; set; } + public long Size { get; set; } + + public readonly long GetEndOffset() + { + throw new NotImplementedException(); + } + + public readonly long IsIncluded(long offset) + { + throw new NotImplementedException(); + } + } + + private SdkRecursiveMutex _mutex; + private IStorage _storageData; + private Result _lastResult; + private long _sizeBytesData; + private int _sizeBytesVerificationBlock; + private int _shiftBytesVerificationBlock; + private int _flags; + private int _bufferLevel; + private StorageType _storageType; + private BlockCacheManager _cacheManager; + + public BlockCacheBufferedStorage() + { + _bufferLevel = -1; + _cacheManager = new BlockCacheManager(); + } + + public override void Dispose() + { + FinalizeObject(); + _cacheManager.Dispose(); + + base.Dispose(); + } + + public bool IsEnabledKeepBurstMode() + { + throw new NotImplementedException(); + } + + public void SetKeepBurstMode(bool isEnabled) + { + throw new NotImplementedException(); + } + + public void SetRealDataCache(bool isEnabled) + { + throw new NotImplementedException(); + } + + public Result Initialize(IBufferManager bufferManager, SdkRecursiveMutex mutex, IStorage data, long dataSize, + int sizeBytesVerificationBlock, int maxCacheEntries, bool useRealDataCache, sbyte bufferLevel, + bool useKeepBurstMode, StorageType storageType) + { + Assert.SdkNotNull(data); + Assert.SdkNotNull(mutex); + Assert.SdkNull(_mutex); + Assert.SdkNull(_storageData); + Assert.SdkGreater(maxCacheEntries, 0); + + Result rc = _cacheManager.Initialize(bufferManager, maxCacheEntries); + if (rc.IsFailure()) return rc.Miss(); + + _mutex = mutex; + _storageData = data; + _sizeBytesData = dataSize; + _sizeBytesVerificationBlock = sizeBytesVerificationBlock; + _lastResult = Result.Success; + _flags = 0; + _bufferLevel = bufferLevel; + _storageType = storageType; + _shiftBytesVerificationBlock = (int)BitmapUtils.ILog2((uint)sizeBytesVerificationBlock); + + Assert.SdkEqual(1 << _shiftBytesVerificationBlock, _sizeBytesVerificationBlock); + + SetKeepBurstMode(useKeepBurstMode); + SetRealDataCache(useRealDataCache); + + return Result.Success; + } + + public void FinalizeObject() + { + throw new NotImplementedException(); + } + + public override Result GetSize(out long size) + { + throw new NotImplementedException(); + } + + public override Result SetSize(long size) + { + throw new NotImplementedException(); + } + + public override Result Read(long offset, Span destination) + { + throw new NotImplementedException(); + } + + public override Result Write(long offset, ReadOnlySpan source) + { + throw new NotImplementedException(); + } + + public override Result Flush() + { + throw new NotImplementedException(); + } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + + public Result Commit() + { + throw new NotImplementedException(); + } + + public Result OnRollback() + { + throw new NotImplementedException(); + } + + private Result FillZeroImpl(long offset, long size) + { + throw new NotImplementedException(); + } + + private Result DestroySignatureImpl(long offset, long size) + { + throw new NotImplementedException(); + } + + private Result InvalidateImpl() + { + throw new NotImplementedException(); + } + + private Result QueryRangeImpl(Span outBuffer, long offset, long size) + { + throw new NotImplementedException(); + } + + private Result GetAssociateBuffer(out Buffer outRange, out CacheEntry outEntry, long offset, int idealSize, + bool isAllocateForWrite) + { + throw new NotImplementedException(); + } + + private Result StoreOrDestroyBuffer(in Buffer memoryRange, ref CacheEntry entry) + { + throw new NotImplementedException(); + } + + private Result StoreOrDestroyBuffer(out int outCacheIndex, in Buffer memoryRange, ref CacheEntry entry) + { + throw new NotImplementedException(); + } + + private Result FlushCacheEntry(int index, bool isWithInvalidate) + { + throw new NotImplementedException(); + } + + private Result FlushRangeCacheEntries(long offset, long size, bool isWithInvalidate) + { + throw new NotImplementedException(); + } + + private Result FlushAllCacheEntries() + { + throw new NotImplementedException(); + } + + private Result ControlDirtiness() + { + throw new NotImplementedException(); + } + + private Result UpdateLastResult(Result result) + { + throw new NotImplementedException(); + } + + private Result ReadHeadCache(out Buffer outMemoryRange, out CacheEntry outEntry, out bool outIsCacheNeeded, + ref long offset, ref long offsetAligned, long offsetAlignedEnd, ref Span buffer) + { + throw new NotImplementedException(); + } + + private Result ReadTailCache(out Buffer outMemoryRange, out CacheEntry outEntry, out bool outIsCacheNeeded, + long offset, long offsetAligned, long offsetAlignedEnd, Span buffer, ref long size) + { + throw new NotImplementedException(); + } + + private Result BulkRead(long offset, Span buffer, ref Buffer memoryRangeHead, ref Buffer memoryRangeTail, + ref CacheEntry entryHead, ref CacheEntry entryTail, bool isHeadCacheNeeded, bool isTailCacheNeeded) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/CompressedStorage.cs b/src/LibHac/FsSystem/CompressedStorage.cs index e8e16bd8..7ea54a39 100644 --- a/src/LibHac/FsSystem/CompressedStorage.cs +++ b/src/LibHac/FsSystem/CompressedStorage.cs @@ -162,8 +162,8 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter public long Offset { get; set; } public uint Size { get; set; } - public long GetEndOffset() => Offset + Size; - public bool IsIncluded(long offset) => Offset <= offset && offset < GetEndOffset(); + public readonly long GetEndOffset() => Offset + Size; + public readonly bool IsIncluded(long offset) => Offset <= offset && offset < GetEndOffset(); } public struct AccessRange diff --git a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs index 51a24273..be673cbc 100644 --- a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs +++ b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs @@ -28,6 +28,13 @@ public interface IBlockCacheManagerRange long GetEndOffset(); } +/// +/// Handles caching buffers with the given . +/// +/// The type of the entries to be stores in the cache manager. +/// The type that uses +/// to represent the range cached by an entry. +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class BlockCacheManager : IDisposable where TEntry : struct, IBlockCacheManagerEntry where TRange : struct, IBlockCacheManagerRange diff --git a/src/LibHac/Util/BitUtil.cs b/src/LibHac/Util/BitUtil.cs index 002af32d..b883167b 100644 --- a/src/LibHac/Util/BitUtil.cs +++ b/src/LibHac/Util/BitUtil.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; namespace LibHac.Util; @@ -36,6 +37,11 @@ public static class BitUtil return value & (value - 1); } + public static int CountLeadingZeros(uint value) + { + return BitOperations.LeadingZeroCount(value); + } + // DivideUp comes from a C++ template that always casts to unsigned types public static uint DivideUp(uint value, uint divisor) => (value + divisor - 1) / divisor; public static ulong DivideUp(ulong value, ulong divisor) => (value + divisor - 1) / divisor;