mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Update IStorage range check functions for 14.0.0
This commit is contained in:
parent
7dd137da6a
commit
fc3fb188c7
12 changed files with 236 additions and 124 deletions
|
@ -11,7 +11,7 @@ namespace LibHac.Fs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows interacting with an <see cref="IFile"/> via an <see cref="IStorage"/> interface.
|
/// Allows interacting with an <see cref="IFile"/> via an <see cref="IStorage"/> interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
|
||||||
public class FileStorage : IStorage
|
public class FileStorage : IStorage
|
||||||
{
|
{
|
||||||
private const long InvalidSize = -1;
|
private const long InvalidSize = -1;
|
||||||
|
@ -60,8 +60,8 @@ public class FileStorage : IStorage
|
||||||
Result rc = UpdateSize();
|
Result rc = UpdateSize();
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, _fileSize))
|
rc = CheckAccessRange(offset, destination.Length, _fileSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _baseFile.Read(out _, offset, destination, ReadOption.None);
|
return _baseFile.Read(out _, offset, destination, ReadOption.None);
|
||||||
}
|
}
|
||||||
|
@ -74,8 +74,8 @@ public class FileStorage : IStorage
|
||||||
Result rc = UpdateSize();
|
Result rc = UpdateSize();
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, _fileSize))
|
rc = CheckAccessRange(offset, source.Length, _fileSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _baseFile.Write(offset, source, WriteOption.None);
|
return _baseFile.Write(offset, source, WriteOption.None);
|
||||||
}
|
}
|
||||||
|
@ -104,35 +104,37 @@ public class FileStorage : IStorage
|
||||||
|
|
||||||
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||||
{
|
{
|
||||||
if (operationId == OperationId.InvalidateCache)
|
switch (operationId)
|
||||||
{
|
{
|
||||||
Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size);
|
case OperationId.InvalidateCache:
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operationId == OperationId.QueryRange)
|
|
||||||
{
|
|
||||||
if (size == 0)
|
|
||||||
{
|
{
|
||||||
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size);
|
||||||
return ResultFs.InvalidSize.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer).Clear();
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
case OperationId.QueryRange:
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
Result rc = UpdateSize();
|
SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer).Clear();
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CheckOffsetAndSize(offset, size))
|
Result rc = UpdateSize();
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer);
|
rc = CheckOffsetAndSize(offset, size);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ResultFs.UnsupportedOperateRangeForFileStorage.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultFs.UnsupportedOperateRangeForFileStorage.Log();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result UpdateSize()
|
private Result UpdateSize()
|
||||||
|
@ -153,7 +155,7 @@ public class FileStorage : IStorage
|
||||||
/// <see cref="IStorage"/> interface. The opened file will automatically be closed when the
|
/// <see cref="IStorage"/> interface. The opened file will automatically be closed when the
|
||||||
/// <see cref="FileStorageBasedFileSystem"/> is disposed.
|
/// <see cref="FileStorageBasedFileSystem"/> is disposed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
|
||||||
public class FileStorageBasedFileSystem : FileStorage
|
public class FileStorageBasedFileSystem : FileStorage
|
||||||
{
|
{
|
||||||
private SharedRef<IFileSystem> _baseFileSystem;
|
private SharedRef<IFileSystem> _baseFileSystem;
|
||||||
|
@ -196,7 +198,7 @@ public class FileStorageBasedFileSystem : FileStorage
|
||||||
/// Provides an <see cref="IStorage"/> interface for interacting with an opened file from a mounted file system.
|
/// Provides an <see cref="IStorage"/> interface for interacting with an opened file from a mounted file system.
|
||||||
/// The caller may choose whether or not the file will be closed when the <see cref="FileHandleStorage"/> is disposed.
|
/// The caller may choose whether or not the file will be closed when the <see cref="FileHandleStorage"/> is disposed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
|
||||||
public class FileHandleStorage : IStorage
|
public class FileHandleStorage : IStorage
|
||||||
{
|
{
|
||||||
private const long InvalidSize = -1;
|
private const long InvalidSize = -1;
|
||||||
|
@ -230,7 +232,7 @@ public class FileHandleStorage : IStorage
|
||||||
_handle = handle;
|
_handle = handle;
|
||||||
_closeFile = closeFile;
|
_closeFile = closeFile;
|
||||||
_size = InvalidSize;
|
_size = InvalidSize;
|
||||||
_mutex.Initialize();
|
_mutex = new SdkMutexType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
|
@ -255,8 +257,8 @@ public class FileHandleStorage : IStorage
|
||||||
Result rc = UpdateSize();
|
Result rc = UpdateSize();
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, _size))
|
rc = CheckAccessRange(offset, destination.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None);
|
return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None);
|
||||||
}
|
}
|
||||||
|
@ -271,8 +273,8 @@ public class FileHandleStorage : IStorage
|
||||||
Result rc = UpdateSize();
|
Result rc = UpdateSize();
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, _size))
|
rc = CheckAccessRange(offset, source.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _fsClient.WriteFile(_handle, offset, source, WriteOption.None);
|
return _fsClient.WriteFile(_handle, offset, source, WriteOption.None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
|
@ -7,12 +8,7 @@ namespace LibHac.Fs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an interface for reading and writing a sequence of bytes.
|
/// Provides an interface for reading and writing a sequence of bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
|
||||||
/// The official IStorage makes the <c>Read</c> etc. methods abstract and doesn't
|
|
||||||
/// have <c>DoRead</c> etc. methods. We're using them here so we can make sure
|
|
||||||
/// the object isn't disposed before calling the method implementation.
|
|
||||||
/// </remarks>
|
|
||||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
|
||||||
public abstract class IStorage : IDisposable
|
public abstract class IStorage : IDisposable
|
||||||
{
|
{
|
||||||
public virtual void Dispose() { }
|
public virtual void Dispose() { }
|
||||||
|
@ -78,20 +74,70 @@ public abstract class IStorage : IDisposable
|
||||||
return OperateRange(Span<byte>.Empty, operationId, offset, size, ReadOnlySpan<byte>.Empty);
|
return OperateRange(Span<byte>.Empty, operationId, offset, size, ReadOnlySpan<byte>.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static Result CheckAccessRange(long offset, long size, long totalSize)
|
||||||
public static bool CheckAccessRange(long offset, long size, long totalSize)
|
|
||||||
{
|
{
|
||||||
return offset >= 0 &&
|
if (offset < 0)
|
||||||
size >= 0 &&
|
return ResultFs.InvalidOffset.Log();
|
||||||
size <= totalSize &&
|
|
||||||
offset <= totalSize - size;
|
if (size < 0)
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
if (!IntUtil.CanAddWithoutOverflow(offset, size))
|
||||||
|
return ResultFs.OutOfRange.Log();
|
||||||
|
|
||||||
|
if (size + offset > totalSize)
|
||||||
|
return ResultFs.OutOfRange.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static Result CheckAccessRange(long offset, ulong size, long totalSize)
|
||||||
public static bool CheckOffsetAndSize(long offset, long size)
|
|
||||||
{
|
{
|
||||||
return offset >= 0 &&
|
Result rc = CheckAccessRange(offset, unchecked((long)size), totalSize);
|
||||||
size >= 0 &&
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
offset <= offset + size;
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result CheckOffsetAndSize(long offset, long size)
|
||||||
|
{
|
||||||
|
if (offset < 0)
|
||||||
|
return ResultFs.InvalidOffset.Log();
|
||||||
|
|
||||||
|
if (size < 0)
|
||||||
|
return ResultFs.InvalidSize.Log();
|
||||||
|
|
||||||
|
if (!IntUtil.CanAddWithoutOverflow(offset, size))
|
||||||
|
return ResultFs.OutOfRange.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result CheckOffsetAndSize(long offset, ulong size)
|
||||||
|
{
|
||||||
|
Result rc = CheckOffsetAndSize(offset, unchecked((long)size));
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result CheckOffsetAndSizeWithResult(long offset, long size, Result resultOnFailure)
|
||||||
|
{
|
||||||
|
Result rc = CheckOffsetAndSize(offset, size);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
return resultOnFailure.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result CheckOffsetAndSizeWithResult(long offset, ulong size, Result resultOnFailure)
|
||||||
|
{
|
||||||
|
Result rc = CheckOffsetAndSize(offset, size);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
return resultOnFailure.Log();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ namespace LibHac.Fs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows interacting with a <see cref="byte"/> array via the <see cref="IStorage"/> interface.
|
/// Allows interacting with a <see cref="byte"/> array via the <see cref="IStorage"/> interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
|
||||||
public class MemoryStorage : IStorage
|
public class MemoryStorage : IStorage
|
||||||
{
|
{
|
||||||
private byte[] _storageBuffer;
|
private byte[] _storageBuffer;
|
||||||
|
@ -22,8 +22,8 @@ public class MemoryStorage : IStorage
|
||||||
if (destination.Length == 0)
|
if (destination.Length == 0)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, _storageBuffer.Length))
|
Result rc = CheckAccessRange(offset, destination.Length, _storageBuffer.Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
_storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
|
_storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ public class MemoryStorage : IStorage
|
||||||
if (source.Length == 0)
|
if (source.Length == 0)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, _storageBuffer.Length))
|
Result rc = CheckAccessRange(offset, source.Length, _storageBuffer.Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
source.CopyTo(_storageBuffer.AsSpan((int)offset));
|
source.CopyTo(_storageBuffer.AsSpan((int)offset));
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,17 @@ namespace LibHac.Fs;
|
||||||
/// Presents a subsection of a base IStorage as a new IStorage.
|
/// Presents a subsection of a base IStorage as a new IStorage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// A SubStorage presents a sub-range of an IStorage as a separate IStorage.
|
/// <para>A SubStorage presents a sub-range of an IStorage as a separate IStorage.</para>
|
||||||
///
|
///
|
||||||
/// The SubStorage doesn't check if the offset and size provided are actually in the base storage.
|
/// <para>The SubStorage doesn't check if the offset and size provided are actually in the base storage.
|
||||||
/// GetSize will return the size given to the SubStorage at initialization and will not query
|
/// GetSize will return the size given to the SubStorage at initialization and will not query
|
||||||
/// the base storage's size.
|
/// the base storage's size.</para>
|
||||||
///
|
///
|
||||||
/// A SubStorage is non-resizable by default. <see cref="SetResizable"/> may be used to mark
|
/// <para>A SubStorage is non-resizable by default. <see cref="SetResizable"/> may be used to mark
|
||||||
/// the SubStorage as resizable. The SubStorage may only be resized if the end of the SubStorage
|
/// the SubStorage as resizable. The SubStorage may only be resized if the end of the SubStorage
|
||||||
/// is located at the end of the base storage. When resizing the SubStorage, the base storage
|
/// is located at the end of the base storage. When resizing the SubStorage, the base storage
|
||||||
/// will be resized to the appropriate length.
|
/// will be resized to the appropriate length.</para>
|
||||||
|
/// <para>Based on FS 14.1.0 (nnSdk 14.3.0)</para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class SubStorage : IStorage
|
public class SubStorage : IStorage
|
||||||
{
|
{
|
||||||
|
@ -158,10 +159,13 @@ public class SubStorage : IStorage
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (destination.Length == 0) return Result.Success;
|
if (destination.Length == 0) return Result.Success;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, _size))
|
Result rc = CheckAccessRange(offset, destination.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return BaseStorage.Read(_offset + offset, destination);
|
rc = BaseStorage.Read(_offset + offset, destination);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
@ -169,26 +173,34 @@ public class SubStorage : IStorage
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (source.Length == 0) return Result.Success;
|
if (source.Length == 0) return Result.Success;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, _size))
|
Result rc = CheckAccessRange(offset, source.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return BaseStorage.Write(_offset + offset, source);
|
rc = BaseStorage.Write(_offset + offset, source);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Result Flush()
|
public override Result Flush()
|
||||||
{
|
{
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
|
||||||
return BaseStorage.Flush();
|
Result rc = BaseStorage.Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Result SetSize(long size)
|
public override Result SetSize(long size)
|
||||||
{
|
{
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log();
|
if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log();
|
||||||
if (!CheckOffsetAndSize(_offset, size)) return ResultFs.InvalidSize.Log();
|
|
||||||
|
|
||||||
Result rc = BaseStorage.GetSize(out long currentSize);
|
Result rc = CheckOffsetAndSize(_offset, size);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
rc = BaseStorage.GetSize(out long currentSize);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (currentSize != _offset + _size)
|
if (currentSize != _offset + _size)
|
||||||
|
@ -222,7 +234,9 @@ public class SubStorage : IStorage
|
||||||
if (operationId != OperationId.InvalidateCache)
|
if (operationId != OperationId.InvalidateCache)
|
||||||
{
|
{
|
||||||
if (size == 0) return Result.Success;
|
if (size == 0) return Result.Success;
|
||||||
if (!CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log();
|
|
||||||
|
Result rc = CheckOffsetAndSize(_offset, size);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
}
|
}
|
||||||
|
|
||||||
return BaseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
return BaseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
||||||
|
|
|
@ -112,10 +112,13 @@ public struct ValueSubStorage : IDisposable
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (destination.Length == 0) return Result.Success;
|
if (destination.Length == 0) return Result.Success;
|
||||||
|
|
||||||
if (!IStorage.CheckAccessRange(offset, destination.Length, _size))
|
Result rc = IStorage.CheckAccessRange(offset, destination.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _baseStorage.Read(_offset + offset, destination);
|
rc = _baseStorage.Read(_offset + offset, destination);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Result Write(long offset, ReadOnlySpan<byte> source)
|
public readonly Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
@ -123,26 +126,34 @@ public struct ValueSubStorage : IDisposable
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (source.Length == 0) return Result.Success;
|
if (source.Length == 0) return Result.Success;
|
||||||
|
|
||||||
if (!IStorage.CheckAccessRange(offset, source.Length, _size))
|
Result rc = IStorage.CheckAccessRange(offset, source.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return _baseStorage.Write(_offset + offset, source);
|
rc = _baseStorage.Write(_offset + offset, source);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Result Flush()
|
public readonly Result Flush()
|
||||||
{
|
{
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
|
|
||||||
return _baseStorage.Flush();
|
Result rc = _baseStorage.Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetSize(long size)
|
public Result SetSize(long size)
|
||||||
{
|
{
|
||||||
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
if (!IsValid()) return ResultFs.NotInitialized.Log();
|
||||||
if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log();
|
if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log();
|
||||||
if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.InvalidSize.Log();
|
|
||||||
|
|
||||||
Result rc = _baseStorage.GetSize(out long currentSize);
|
Result rc = IStorage.CheckOffsetAndSize(_offset, size);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = _baseStorage.GetSize(out long currentSize);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (currentSize != _offset + _size)
|
if (currentSize != _offset + _size)
|
||||||
|
@ -181,7 +192,9 @@ public struct ValueSubStorage : IDisposable
|
||||||
if (operationId != OperationId.InvalidateCache)
|
if (operationId != OperationId.InvalidateCache)
|
||||||
{
|
{
|
||||||
if (size == 0) return Result.Success;
|
if (size == 0) return Result.Success;
|
||||||
if (!IStorage.CheckOffsetAndSize(_offset, size)) return ResultFs.OutOfRange.Log();
|
|
||||||
|
Result rc = IStorage.CheckOffsetAndSize(_offset, size);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _baseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
return _baseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer);
|
||||||
|
|
|
@ -23,7 +23,7 @@ public interface IAlignmentMatchingStorageSize { }
|
||||||
/// <remarks><para>This class uses a work buffer on the stack to avoid allocations. Because of this the data alignment
|
/// <remarks><para>This class uses a work buffer on the stack to avoid allocations. Because of this the data alignment
|
||||||
/// must be kept small; no larger than 0x200. The <see cref="AlignmentMatchingStoragePooledBuffer{TBufferAlignment}"/> class
|
/// must be kept small; no larger than 0x200. The <see cref="AlignmentMatchingStoragePooledBuffer{TBufferAlignment}"/> class
|
||||||
/// should be used for data alignment sizes larger than this.</para>
|
/// should be used for data alignment sizes larger than this.</para>
|
||||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
/// <para>Based on FS 14.1.0 (nnSdk 14.3.0)</para></remarks>
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStorage
|
public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStorage
|
||||||
where TDataAlignment : struct, IAlignmentMatchingStorageSize
|
where TDataAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
|
@ -80,8 +80,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
Result rc = GetSize(out long totalSize);
|
Result rc = GetSize(out long totalSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, totalSize))
|
rc = CheckAccessRange(offset, destination.Length, totalSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return AlignmentMatchingStorageImpl.Read(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, destination);
|
return AlignmentMatchingStorageImpl.Read(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, destination);
|
||||||
}
|
}
|
||||||
|
@ -96,8 +96,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
Result rc = GetSize(out long totalSize);
|
Result rc = GetSize(out long totalSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, totalSize))
|
rc = CheckAccessRange(offset, source.Length, totalSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return AlignmentMatchingStorageImpl.Write(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, source);
|
return AlignmentMatchingStorageImpl.Write(_baseStorage, workBuffer, DataAlign, BufferAlign, offset, source);
|
||||||
}
|
}
|
||||||
|
@ -146,8 +146,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckOffsetAndSize(offset, size))
|
rc = CheckOffsetAndSize(offset, size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
long validSize = Math.Min(size, baseStorageSize - offset);
|
long validSize = Math.Min(size, baseStorageSize - offset);
|
||||||
long alignedOffset = Alignment.AlignDownPow2(offset, DataAlign);
|
long alignedOffset = Alignment.AlignDownPow2(offset, DataAlign);
|
||||||
|
@ -166,7 +166,7 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
|
||||||
/// the beginning or end of the requested range. For data alignment sizes of 0x200 or smaller
|
/// the beginning or end of the requested range. For data alignment sizes of 0x200 or smaller
|
||||||
/// <see cref="AlignmentMatchingStorage{TDataAlignment,TBufferAlignment}"/> should be used instead
|
/// <see cref="AlignmentMatchingStorage{TDataAlignment,TBufferAlignment}"/> should be used instead
|
||||||
/// to avoid these allocations.</para>
|
/// to avoid these allocations.</para>
|
||||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
/// <para>Based on FS 14.1.0 (nnSdk 14.3.0)</para></remarks>
|
||||||
public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
|
||||||
{
|
{
|
||||||
|
@ -220,8 +220,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, baseStorageSize))
|
rc = CheckAccessRange(offset, destination.Length, baseStorageSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
using var pooledBuffer = new PooledBuffer();
|
using var pooledBuffer = new PooledBuffer();
|
||||||
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
|
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
|
||||||
|
@ -238,8 +238,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, baseStorageSize))
|
rc = CheckAccessRange(offset, source.Length, baseStorageSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
using var pooledBuffer = new PooledBuffer();
|
using var pooledBuffer = new PooledBuffer();
|
||||||
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
|
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
|
||||||
|
@ -292,8 +292,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckOffsetAndSize(offset, size))
|
rc = CheckOffsetAndSize(offset, size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
long validSize = Math.Min(size, baseStorageSize - offset);
|
long validSize = Math.Min(size, baseStorageSize - offset);
|
||||||
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
|
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
|
||||||
|
@ -362,8 +362,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, baseStorageSize))
|
rc = CheckAccessRange(offset, destination.Length, baseStorageSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
// Calculate the aligned offsets of the requested region.
|
// Calculate the aligned offsets of the requested region.
|
||||||
long offsetEnd = offset + destination.Length;
|
long offsetEnd = offset + destination.Length;
|
||||||
|
@ -453,8 +453,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, baseStorageSize))
|
rc = CheckAccessRange(offset, source.Length, baseStorageSize);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
using var pooledBuffer = new PooledBuffer((int)_dataAlignment, (int)_dataAlignment);
|
using var pooledBuffer = new PooledBuffer((int)_dataAlignment, (int)_dataAlignment);
|
||||||
|
|
||||||
|
@ -505,8 +505,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
|
||||||
Result rc = GetSize(out long baseStorageSize);
|
Result rc = GetSize(out long baseStorageSize);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
if (!CheckOffsetAndSize(offset, size))
|
rc = CheckOffsetAndSize(offset, size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
long validSize = Math.Min(size, baseStorageSize - offset);
|
long validSize = Math.Min(size, baseStorageSize - offset);
|
||||||
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
|
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
|
||||||
|
|
|
@ -28,10 +28,10 @@ public class AesCbcStorage : SectorStorage
|
||||||
|
|
||||||
public override Result Read(long offset, Span<byte> destination)
|
public override Result Read(long offset, Span<byte> destination)
|
||||||
{
|
{
|
||||||
if (!CheckAccessRange(offset, destination.Length, _size))
|
Result rc = CheckAccessRange(offset, destination.Length, _size);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
Result rc = base.Read(offset, destination);
|
rc = base.Read(offset, destination);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = GetDecryptor(out ICipher cipher, offset);
|
rc = GetDecryptor(out ICipher cipher, offset);
|
||||||
|
|
|
@ -39,8 +39,8 @@ public class CachedStorage : IStorage
|
||||||
long inOffset = offset;
|
long inOffset = offset;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, Length))
|
Result rc = CheckAccessRange(offset, destination.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
lock (Blocks)
|
lock (Blocks)
|
||||||
{
|
{
|
||||||
|
@ -69,8 +69,8 @@ public class CachedStorage : IStorage
|
||||||
long inOffset = offset;
|
long inOffset = offset;
|
||||||
int outOffset = 0;
|
int outOffset = 0;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, Length))
|
Result rc = CheckAccessRange(offset, source.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
lock (Blocks)
|
lock (Blocks)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,8 +34,8 @@ public class ConcatenationStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = destination.Length;
|
int remaining = destination.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, Length))
|
Result rc = CheckAccessRange(offset, destination.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
int sourceIndex = FindSource(inPos);
|
int sourceIndex = FindSource(inPos);
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class ConcatenationStorage : IStorage
|
||||||
|
|
||||||
int bytesToRead = (int)Math.Min(entryRemain, remaining);
|
int bytesToRead = (int)Math.Min(entryRemain, remaining);
|
||||||
|
|
||||||
Result rc = entry.Storage.Read(entryPos, destination.Slice(outPos, bytesToRead));
|
rc = entry.Storage.Read(entryPos, destination.Slice(outPos, bytesToRead));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToRead;
|
outPos += bytesToRead;
|
||||||
|
@ -65,8 +65,8 @@ public class ConcatenationStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = source.Length;
|
int remaining = source.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, Length))
|
Result rc = CheckAccessRange(offset, source.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
int sourceIndex = FindSource(inPos);
|
int sourceIndex = FindSource(inPos);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ public class ConcatenationStorage : IStorage
|
||||||
|
|
||||||
int bytesToWrite = (int)Math.Min(entryRemain, remaining);
|
int bytesToWrite = (int)Math.Min(entryRemain, remaining);
|
||||||
|
|
||||||
Result rc = entry.Storage.Write(entryPos, source.Slice(outPos, bytesToWrite));
|
rc = entry.Storage.Write(entryPos, source.Slice(outPos, bytesToWrite));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToWrite;
|
outPos += bytesToWrite;
|
||||||
|
|
|
@ -33,8 +33,8 @@ public class DuplexStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = destination.Length;
|
int remaining = destination.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, Length))
|
Result rc = CheckAccessRange(offset, destination.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@ public class DuplexStorage : IStorage
|
||||||
|
|
||||||
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
|
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
|
||||||
|
|
||||||
Result rc = data.Read(inPos, destination.Slice(outPos, bytesToRead));
|
rc = data.Read(inPos, destination.Slice(outPos, bytesToRead));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToRead;
|
outPos += bytesToRead;
|
||||||
|
@ -62,8 +62,8 @@ public class DuplexStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = source.Length;
|
int remaining = source.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, Length))
|
Result rc = CheckAccessRange(offset, source.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ public class DuplexStorage : IStorage
|
||||||
|
|
||||||
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
|
IStorage data = Bitmap.Bitmap[blockNum] ? DataB : DataA;
|
||||||
|
|
||||||
Result rc = data.Write(inPos, source.Slice(outPos, bytesToWrite));
|
rc = data.Write(inPos, source.Slice(outPos, bytesToWrite));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToWrite;
|
outPos += bytesToWrite;
|
||||||
|
|
|
@ -40,8 +40,8 @@ public class JournalStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = destination.Length;
|
int remaining = destination.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, destination.Length, Length))
|
Result rc = CheckAccessRange(offset, destination.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,7 @@ public class JournalStorage : IStorage
|
||||||
|
|
||||||
int bytesToRead = Math.Min(remaining, BlockSize - blockPos);
|
int bytesToRead = Math.Min(remaining, BlockSize - blockPos);
|
||||||
|
|
||||||
Result rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead));
|
rc = BaseStorage.Read(physicalOffset, destination.Slice(outPos, bytesToRead));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToRead;
|
outPos += bytesToRead;
|
||||||
|
@ -69,8 +69,8 @@ public class JournalStorage : IStorage
|
||||||
int outPos = 0;
|
int outPos = 0;
|
||||||
int remaining = source.Length;
|
int remaining = source.Length;
|
||||||
|
|
||||||
if (!CheckAccessRange(offset, source.Length, Length))
|
Result rc = CheckAccessRange(offset, source.Length, Length);
|
||||||
return ResultFs.OutOfRange.Log();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
|
@ -81,7 +81,7 @@ public class JournalStorage : IStorage
|
||||||
|
|
||||||
int bytesToWrite = Math.Min(remaining, BlockSize - blockPos);
|
int bytesToWrite = Math.Min(remaining, BlockSize - blockPos);
|
||||||
|
|
||||||
Result rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite));
|
rc = BaseStorage.Write(physicalOffset, source.Slice(outPos, bytesToWrite));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outPos += bytesToWrite;
|
outPos += bytesToWrite;
|
||||||
|
|
37
src/LibHac/Util/IntUtil.cs
Normal file
37
src/LibHac/Util/IntUtil.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
namespace LibHac.Util;
|
||||||
|
|
||||||
|
public static class IntUtil
|
||||||
|
{
|
||||||
|
// Todo: Use generic math once C# 11 is out
|
||||||
|
public static bool IsIntValueRepresentableAsLong(ulong value)
|
||||||
|
{
|
||||||
|
return value <= long.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIntValueRepresentableAsULong(long value)
|
||||||
|
{
|
||||||
|
return value >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIntValueRepresentableAsUInt(long value)
|
||||||
|
{
|
||||||
|
return value >= uint.MinValue && value <= uint.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanAddWithoutOverflow(long x, long y)
|
||||||
|
{
|
||||||
|
if (y >= 0)
|
||||||
|
{
|
||||||
|
return x <= long.MaxValue - y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return x >= unchecked(long.MinValue - y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanAddWithoutOverflow(ulong x, ulong y)
|
||||||
|
{
|
||||||
|
return x <= ulong.MaxValue - y;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue