mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add ValueSubStorage
This commit is contained in:
parent
280fe14b5f
commit
771f2cdb26
4 changed files with 291 additions and 3 deletions
|
@ -2,6 +2,7 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Diag.Impl;
|
using LibHac.Diag.Impl;
|
||||||
using LibHac.Os;
|
using LibHac.Os;
|
||||||
|
|
||||||
|
@ -395,6 +396,98 @@ public static class Assert
|
||||||
NotNullImpl(AssertionType.SdkRequires, value, valueText, functionName, fileName, lineNumber);
|
NotNullImpl(AssertionType.SdkRequires, value, valueText, functionName, fileName, lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Not null UniqueRef<T>
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static void NotNullImpl<T>(AssertionType assertionType, in UniqueRef<T> value,
|
||||||
|
string valueText, string functionName, string fileName, int lineNumber) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
if (AssertImpl.NotNull(in value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssertImpl.InvokeAssertionNotNull(assertionType, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
public static void NotNull<T>(in UniqueRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.UserAssert, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
internal static void SdkNotNull<T>(in UniqueRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.SdkAssert, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
internal static void SdkRequiresNotNull<T>(in UniqueRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.SdkRequires, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Not null SharedRef<T>
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static void NotNullImpl<T>(AssertionType assertionType, in SharedRef<T> value,
|
||||||
|
string valueText, string functionName, string fileName, int lineNumber) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
if (AssertImpl.NotNull(in value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssertImpl.InvokeAssertionNotNull(assertionType, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
public static void NotNull<T>(in SharedRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.UserAssert, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
internal static void SdkNotNull<T>(in SharedRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.SdkAssert, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional(AssertCondition)]
|
||||||
|
internal static void SdkRequiresNotNull<T>(in SharedRef<T> value,
|
||||||
|
[CallerArgumentExpression("value")] string valueText = "",
|
||||||
|
[CallerMemberName] string functionName = "",
|
||||||
|
[CallerFilePath] string fileName = "",
|
||||||
|
[CallerLineNumber] int lineNumber = 0)
|
||||||
|
where T : class, IDisposable
|
||||||
|
{
|
||||||
|
NotNullImpl(AssertionType.SdkRequires, in value, valueText, functionName, fileName, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Null
|
// Null
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Diag.Impl;
|
namespace LibHac.Diag.Impl;
|
||||||
|
@ -145,6 +146,16 @@ internal static class AssertImpl
|
||||||
return !Unsafe.IsNullRef(ref MemoryMarshal.GetReference(span)) || span.Length == 0;
|
return !Unsafe.IsNullRef(ref MemoryMarshal.GetReference(span)) || span.Length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool NotNull<T>(in UniqueRef<T> item) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
return item.HasValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NotNull<T>(in SharedRef<T> item) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
return item.HasValue;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool WithinRange(int value, int lowerInclusive, int upperExclusive)
|
public static bool WithinRange(int value, int lowerInclusive, int upperExclusive)
|
||||||
{
|
{
|
||||||
return lowerInclusive <= value && value < upperExclusive;
|
return lowerInclusive <= value && value < upperExclusive;
|
||||||
|
|
|
@ -201,6 +201,7 @@ public class SubStorage : IStorage
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_size = size;
|
_size = size;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,9 +218,13 @@ public class SubStorage : IStorage
|
||||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||||
{
|
{
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (size == 0) return Result.Success;
|
|
||||||
if (!CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log();
|
|
||||||
|
|
||||||
return base.DoOperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
if (operationId != OperationId.InvalidateCache)
|
||||||
|
{
|
||||||
|
if (size == 0) return Result.Success;
|
||||||
|
if (!CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
179
src/LibHac/Fs/ValueSubStorage.cs
Normal file
179
src/LibHac/Fs/ValueSubStorage.cs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
|
[NonCopyableDisposable]
|
||||||
|
public struct ValueSubStorage : IDisposable
|
||||||
|
{
|
||||||
|
private IStorage _baseStorage;
|
||||||
|
private long _offset;
|
||||||
|
private long _size;
|
||||||
|
private bool _isResizable;
|
||||||
|
private SharedRef<IStorage> _sharedBaseStorage;
|
||||||
|
|
||||||
|
public ValueSubStorage()
|
||||||
|
{
|
||||||
|
_baseStorage = null;
|
||||||
|
_offset = 0;
|
||||||
|
_size = 0;
|
||||||
|
_isResizable = false;
|
||||||
|
_sharedBaseStorage = new SharedRef<IStorage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueSubStorage(in ValueSubStorage other)
|
||||||
|
{
|
||||||
|
_baseStorage = other._baseStorage;
|
||||||
|
_offset = other._offset;
|
||||||
|
_size = other._size;
|
||||||
|
_isResizable = other._isResizable;
|
||||||
|
_sharedBaseStorage = SharedRef<IStorage>.CreateCopy(in other._sharedBaseStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueSubStorage(IStorage baseStorage, long offset, long size)
|
||||||
|
{
|
||||||
|
_baseStorage = baseStorage;
|
||||||
|
_offset = offset;
|
||||||
|
_size = size;
|
||||||
|
_isResizable = false;
|
||||||
|
_sharedBaseStorage = new SharedRef<IStorage>();
|
||||||
|
|
||||||
|
Assert.SdkRequiresNotNull(baseStorage);
|
||||||
|
Assert.SdkRequiresLessEqual(0, offset);
|
||||||
|
Assert.SdkRequiresLessEqual(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueSubStorage(in ValueSubStorage subStorage, long offset, long size)
|
||||||
|
{
|
||||||
|
_baseStorage = subStorage._baseStorage;
|
||||||
|
_offset = subStorage._offset + offset;
|
||||||
|
_size = size;
|
||||||
|
_isResizable = false;
|
||||||
|
_sharedBaseStorage = SharedRef<IStorage>.CreateCopy(in subStorage._sharedBaseStorage);
|
||||||
|
|
||||||
|
Assert.SdkRequiresLessEqual(0, offset);
|
||||||
|
Assert.SdkRequiresLessEqual(0, size);
|
||||||
|
Assert.SdkRequires(subStorage.IsValid());
|
||||||
|
Assert.SdkRequiresGreaterEqual(subStorage._size, offset + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueSubStorage(in SharedRef<IStorage> baseStorage, long offset, long size)
|
||||||
|
{
|
||||||
|
_baseStorage = baseStorage.Get;
|
||||||
|
_offset = offset;
|
||||||
|
_size = size;
|
||||||
|
_isResizable = false;
|
||||||
|
_sharedBaseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage);
|
||||||
|
|
||||||
|
Assert.SdkRequiresNotNull(in baseStorage);
|
||||||
|
Assert.SdkRequiresLessEqual(0, _offset);
|
||||||
|
Assert.SdkRequiresLessEqual(0, _size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_baseStorage = null;
|
||||||
|
_sharedBaseStorage.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(in ValueSubStorage other)
|
||||||
|
{
|
||||||
|
if (!Unsafe.AreSame(ref Unsafe.AsRef(in this), ref Unsafe.AsRef(in other)))
|
||||||
|
{
|
||||||
|
_baseStorage = other._baseStorage;
|
||||||
|
_offset = other._offset;
|
||||||
|
_size = other._size;
|
||||||
|
_isResizable = other._isResizable;
|
||||||
|
_sharedBaseStorage.SetByCopy(in other._sharedBaseStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly bool IsValid() => _baseStorage is not null;
|
||||||
|
|
||||||
|
public void SetResizable(bool isResizable)
|
||||||
|
{
|
||||||
|
_isResizable = isResizable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result Read(long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
if (destination.Length == 0) return Result.Success;
|
||||||
|
|
||||||
|
if (!IStorage.CheckAccessRange(offset, destination.Length, _size))
|
||||||
|
return ResultFs.OutOfRange.Log();
|
||||||
|
|
||||||
|
return _baseStorage.Read(_offset + offset, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
{
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
if (source.Length == 0) return Result.Success;
|
||||||
|
|
||||||
|
if (!IStorage.CheckAccessRange(offset, source.Length, _size))
|
||||||
|
return ResultFs.OutOfRange.Log();
|
||||||
|
|
||||||
|
return _baseStorage.Write(_offset + offset, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result Flush()
|
||||||
|
{
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
|
||||||
|
return _baseStorage.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result SetSize(long size)
|
||||||
|
{
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log();
|
||||||
|
if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
Result rc = _baseStorage.GetSize(out long currentSize);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (currentSize != _offset + _size)
|
||||||
|
{
|
||||||
|
// SubStorage cannot be resized unless it is located at the end of the base storage.
|
||||||
|
return ResultFs.UnsupportedSetSizeForResizableSubStorage.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = _baseStorage.SetSize(_offset + size);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
_size = size;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result GetSize(out long size)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out size);
|
||||||
|
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
|
||||||
|
size = _size;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result OperateRange(OperationId operationId, long offset, long size)
|
||||||
|
{
|
||||||
|
return OperateRange(Span<byte>.Empty, operationId, offset, size, ReadOnlySpan<byte>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||||
|
{
|
||||||
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
|
||||||
|
if (operationId != OperationId.InvalidateCache)
|
||||||
|
{
|
||||||
|
if (size == 0) return Result.Success;
|
||||||
|
if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _baseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue