Update IStorage range check functions for 14.0.0

This commit is contained in:
Alex Barney 2022-04-14 14:18:24 -07:00
parent 7dd137da6a
commit fc3fb188c7
12 changed files with 236 additions and 124 deletions

View file

@ -11,7 +11,7 @@ namespace LibHac.Fs;
/// <summary>
/// Allows interacting with an <see cref="IFile"/> via an <see cref="IStorage"/> interface.
/// </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
{
private const long InvalidSize = -1;
@ -60,8 +60,8 @@ public class FileStorage : IStorage
Result rc = UpdateSize();
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, destination.Length, _fileSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, destination.Length, _fileSize);
if (rc.IsFailure()) return rc.Miss();
return _baseFile.Read(out _, offset, destination, ReadOption.None);
}
@ -74,8 +74,8 @@ public class FileStorage : IStorage
Result rc = UpdateSize();
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, source.Length, _fileSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, source.Length, _fileSize);
if (rc.IsFailure()) return rc.Miss();
return _baseFile.Write(offset, source, WriteOption.None);
}
@ -104,15 +104,16 @@ public class FileStorage : IStorage
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
if (operationId == OperationId.InvalidateCache)
switch (operationId)
{
case OperationId.InvalidateCache:
{
Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
if (operationId == OperationId.QueryRange)
case OperationId.QueryRange:
{
if (size == 0)
{
@ -126,14 +127,15 @@ public class FileStorage : IStorage
Result rc = UpdateSize();
if (rc.IsFailure()) return rc.Miss();
if (!CheckOffsetAndSize(offset, size))
return ResultFs.OutOfRange.Log();
rc = CheckOffsetAndSize(offset, size);
if (rc.IsFailure()) return rc.Miss();
return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer);
}
default:
return ResultFs.UnsupportedOperateRangeForFileStorage.Log();
}
}
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="FileStorageBasedFileSystem"/> is disposed.
/// </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
{
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.
/// The caller may choose whether or not the file will be closed when the <see cref="FileHandleStorage"/> is disposed.
/// </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
{
private const long InvalidSize = -1;
@ -230,7 +232,7 @@ public class FileHandleStorage : IStorage
_handle = handle;
_closeFile = closeFile;
_size = InvalidSize;
_mutex.Initialize();
_mutex = new SdkMutexType();
}
public override void Dispose()
@ -255,8 +257,8 @@ public class FileHandleStorage : IStorage
Result rc = UpdateSize();
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, destination.Length, _size))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, destination.Length, _size);
if (rc.IsFailure()) return rc.Miss();
return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None);
}
@ -271,8 +273,8 @@ public class FileHandleStorage : IStorage
Result rc = UpdateSize();
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, source.Length, _size))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, source.Length, _size);
if (rc.IsFailure()) return rc.Miss();
return _fsClient.WriteFile(_handle, offset, source, WriteOption.None);
}

View file

@ -1,5 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Util;
namespace LibHac.Fs;
@ -7,12 +8,7 @@ namespace LibHac.Fs;
/// <summary>
/// Provides an interface for reading and writing a sequence of bytes.
/// </summary>
/// <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>
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public abstract class IStorage : IDisposable
{
public virtual void Dispose() { }
@ -78,20 +74,70 @@ public abstract class IStorage : IDisposable
return OperateRange(Span<byte>.Empty, operationId, offset, size, ReadOnlySpan<byte>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CheckAccessRange(long offset, long size, long totalSize)
public static Result CheckAccessRange(long offset, long size, long totalSize)
{
return offset >= 0 &&
size >= 0 &&
size <= totalSize &&
offset <= totalSize - size;
if (offset < 0)
return ResultFs.InvalidOffset.Log();
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 bool CheckOffsetAndSize(long offset, long size)
public static Result CheckAccessRange(long offset, ulong size, long totalSize)
{
return offset >= 0 &&
size >= 0 &&
offset <= offset + size;
Result rc = CheckAccessRange(offset, unchecked((long)size), totalSize);
if (rc.IsFailure()) return rc.Miss();
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;
}
}

View file

@ -7,7 +7,7 @@ namespace LibHac.Fs;
/// <summary>
/// Allows interacting with a <see cref="byte"/> array via the <see cref="IStorage"/> interface.
/// </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
{
private byte[] _storageBuffer;
@ -22,8 +22,8 @@ public class MemoryStorage : IStorage
if (destination.Length == 0)
return Result.Success;
if (!CheckAccessRange(offset, destination.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, _storageBuffer.Length);
if (rc.IsFailure()) return rc.Miss();
_storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
@ -35,8 +35,8 @@ public class MemoryStorage : IStorage
if (source.Length == 0)
return Result.Success;
if (!CheckAccessRange(offset, source.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, _storageBuffer.Length);
if (rc.IsFailure()) return rc.Miss();
source.CopyTo(_storageBuffer.AsSpan((int)offset));

View file

@ -8,16 +8,17 @@ namespace LibHac.Fs;
/// Presents a subsection of a base IStorage as a new IStorage.
/// </summary>
/// <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
/// 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
/// 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>
public class SubStorage : IStorage
{
@ -158,10 +159,13 @@ public class SubStorage : IStorage
if (!IsValid()) return ResultFs.NotInitialized.Log();
if (destination.Length == 0) return Result.Success;
if (!CheckAccessRange(offset, destination.Length, _size))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, _size);
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)
@ -169,26 +173,34 @@ public class SubStorage : IStorage
if (!IsValid()) return ResultFs.NotInitialized.Log();
if (source.Length == 0) return Result.Success;
if (!CheckAccessRange(offset, source.Length, _size))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, _size);
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()
{
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)
{
if (!IsValid()) return ResultFs.NotInitialized.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 (currentSize != _offset + _size)
@ -222,7 +234,9 @@ public class SubStorage : IStorage
if (operationId != OperationId.InvalidateCache)
{
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);

View file

@ -112,10 +112,13 @@ public struct ValueSubStorage : IDisposable
if (!IsValid()) return ResultFs.NotInitialized.Log();
if (destination.Length == 0) return Result.Success;
if (!IStorage.CheckAccessRange(offset, destination.Length, _size))
return ResultFs.OutOfRange.Log();
Result rc = IStorage.CheckAccessRange(offset, destination.Length, _size);
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)
@ -123,26 +126,34 @@ public struct ValueSubStorage : IDisposable
if (!IsValid()) return ResultFs.NotInitialized.Log();
if (source.Length == 0) return Result.Success;
if (!IStorage.CheckAccessRange(offset, source.Length, _size))
return ResultFs.OutOfRange.Log();
Result rc = IStorage.CheckAccessRange(offset, source.Length, _size);
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()
{
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)
{
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);
Result rc = IStorage.CheckOffsetAndSize(_offset, size);
if (rc.IsFailure()) return rc;
rc = _baseStorage.GetSize(out long currentSize);
if (rc.IsFailure()) return rc;
if (currentSize != _offset + _size)
@ -181,7 +192,9 @@ public struct ValueSubStorage : IDisposable
if (operationId != OperationId.InvalidateCache)
{
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);

View file

@ -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
/// 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>
/// <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]
public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStorage
where TDataAlignment : struct, IAlignmentMatchingStorageSize
@ -80,8 +80,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
Result rc = GetSize(out long totalSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, destination.Length, totalSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, destination.Length, totalSize);
if (rc.IsFailure()) return rc.Miss();
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);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, source.Length, totalSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, source.Length, totalSize);
if (rc.IsFailure()) return rc.Miss();
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);
if (rc.IsFailure()) return rc.Miss();
if (!CheckOffsetAndSize(offset, size))
return ResultFs.OutOfRange.Log();
rc = CheckOffsetAndSize(offset, size);
if (rc.IsFailure()) return rc.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
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
/// <see cref="AlignmentMatchingStorage{TDataAlignment,TBufferAlignment}"/> should be used instead
/// 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
where TBufferAlignment : struct, IAlignmentMatchingStorageSize
{
@ -220,8 +220,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, destination.Length, baseStorageSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, destination.Length, baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
using var pooledBuffer = new PooledBuffer();
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
@ -238,8 +238,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, source.Length, baseStorageSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, source.Length, baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
using var pooledBuffer = new PooledBuffer();
pooledBuffer.AllocateParticularlyLarge((int)_dataAlignment, (int)_dataAlignment);
@ -292,8 +292,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckOffsetAndSize(offset, size))
return ResultFs.OutOfRange.Log();
rc = CheckOffsetAndSize(offset, size);
if (rc.IsFailure()) return rc.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
@ -362,8 +362,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, destination.Length, baseStorageSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, destination.Length, baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
// Calculate the aligned offsets of the requested region.
long offsetEnd = offset + destination.Length;
@ -453,8 +453,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckAccessRange(offset, source.Length, baseStorageSize))
return ResultFs.OutOfRange.Log();
rc = CheckAccessRange(offset, source.Length, baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
using var pooledBuffer = new PooledBuffer((int)_dataAlignment, (int)_dataAlignment);
@ -505,8 +505,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
Result rc = GetSize(out long baseStorageSize);
if (rc.IsFailure()) return rc.Miss();
if (!CheckOffsetAndSize(offset, size))
return ResultFs.OutOfRange.Log();
rc = CheckOffsetAndSize(offset, size);
if (rc.IsFailure()) return rc.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);

View file

@ -28,10 +28,10 @@ public class AesCbcStorage : SectorStorage
public override Result Read(long offset, Span<byte> destination)
{
if (!CheckAccessRange(offset, destination.Length, _size))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, _size);
if (rc.IsFailure()) return rc.Miss();
Result rc = base.Read(offset, destination);
rc = base.Read(offset, destination);
if (rc.IsFailure()) return rc;
rc = GetDecryptor(out ICipher cipher, offset);

View file

@ -39,8 +39,8 @@ public class CachedStorage : IStorage
long inOffset = offset;
int outOffset = 0;
if (!CheckAccessRange(offset, destination.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, Length);
if (rc.IsFailure()) return rc.Miss();
lock (Blocks)
{
@ -69,8 +69,8 @@ public class CachedStorage : IStorage
long inOffset = offset;
int outOffset = 0;
if (!CheckAccessRange(offset, source.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, Length);
if (rc.IsFailure()) return rc.Miss();
lock (Blocks)
{

View file

@ -34,8 +34,8 @@ public class ConcatenationStorage : IStorage
int outPos = 0;
int remaining = destination.Length;
if (!CheckAccessRange(offset, destination.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, Length);
if (rc.IsFailure()) return rc.Miss();
int sourceIndex = FindSource(inPos);
@ -47,7 +47,7 @@ public class ConcatenationStorage : IStorage
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;
outPos += bytesToRead;
@ -65,8 +65,8 @@ public class ConcatenationStorage : IStorage
int outPos = 0;
int remaining = source.Length;
if (!CheckAccessRange(offset, source.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, Length);
if (rc.IsFailure()) return rc.Miss();
int sourceIndex = FindSource(inPos);
@ -78,7 +78,7 @@ public class ConcatenationStorage : IStorage
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;
outPos += bytesToWrite;

View file

@ -33,8 +33,8 @@ public class DuplexStorage : IStorage
int outPos = 0;
int remaining = destination.Length;
if (!CheckAccessRange(offset, destination.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, Length);
if (rc.IsFailure()) return rc.Miss();
while (remaining > 0)
{
@ -45,7 +45,7 @@ public class DuplexStorage : IStorage
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;
outPos += bytesToRead;
@ -62,8 +62,8 @@ public class DuplexStorage : IStorage
int outPos = 0;
int remaining = source.Length;
if (!CheckAccessRange(offset, source.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, Length);
if (rc.IsFailure()) return rc.Miss();
while (remaining > 0)
{
@ -74,7 +74,7 @@ public class DuplexStorage : IStorage
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;
outPos += bytesToWrite;

View file

@ -40,8 +40,8 @@ public class JournalStorage : IStorage
int outPos = 0;
int remaining = destination.Length;
if (!CheckAccessRange(offset, destination.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, destination.Length, Length);
if (rc.IsFailure()) return rc.Miss();
while (remaining > 0)
{
@ -52,7 +52,7 @@ public class JournalStorage : IStorage
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;
outPos += bytesToRead;
@ -69,8 +69,8 @@ public class JournalStorage : IStorage
int outPos = 0;
int remaining = source.Length;
if (!CheckAccessRange(offset, source.Length, Length))
return ResultFs.OutOfRange.Log();
Result rc = CheckAccessRange(offset, source.Length, Length);
if (rc.IsFailure()) return rc.Miss();
while (remaining > 0)
{
@ -81,7 +81,7 @@ public class JournalStorage : IStorage
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;
outPos += bytesToWrite;

View 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;
}
}