Use generic math in Util.Alignment

This commit is contained in:
Alex Barney 2022-12-13 19:10:10 -07:00
parent 7fce26e899
commit df77de365c
23 changed files with 232 additions and 133 deletions

View file

@ -242,11 +242,11 @@ internal static class AssertImpl
public static bool IsAligned(long value, int alignment)
{
return Alignment.IsAlignedPow2(value, (uint)alignment);
return Alignment.IsAligned(value, (uint)alignment);
}
public static bool IsAligned(ulong value, int alignment)
{
return Alignment.IsAlignedPow2(value, (uint)alignment);
return Alignment.IsAligned(value, (uint)alignment);
}
}

View file

@ -20,7 +20,7 @@ public static class ApplicationSaveDataManagement
private static long RoundUpOccupationSize(long size)
{
return Alignment.AlignUpPow2(size, SaveDataBlockSize);
return Alignment.AlignUp(size, SaveDataBlockSize);
}
private static long CalculateSaveDataExtensionContextFileSize(long saveDataSize, long saveDataJournalSize)
@ -48,10 +48,10 @@ public static class ApplicationSaveDataManagement
if (availableSize < saveDataSize || journalSize < saveDataJournalSize)
{
// Make sure the new sizes are valid
if (availableSize < saveDataSize && !Alignment.IsAlignedPow2(saveDataSize, SaveDataExtensionSizeAlignment))
if (availableSize < saveDataSize && !Alignment.IsAligned(saveDataSize, SaveDataExtensionSizeAlignment))
return ResultFs.ExtensionSizeInvalid.Log();
if (journalSize < saveDataJournalSize && !Alignment.IsAlignedPow2(saveDataJournalSize, SaveDataExtensionSizeAlignment))
if (journalSize < saveDataJournalSize && !Alignment.IsAligned(saveDataJournalSize, SaveDataExtensionSizeAlignment))
return ResultFs.ExtensionSizeInvalid.Log();
long newSaveDataSize = Math.Max(saveDataSize, availableSize);

View file

@ -171,7 +171,7 @@ public ref struct Path
if (_buffer is not null && _buffer.Length > length)
return Result.Success;
int alignedLength = Alignment.AlignUpPow2(length, WriteBufferAlignmentLength);
int alignedLength = Alignment.AlignUp(length, WriteBufferAlignmentLength);
byte[] buffer = ArrayPool<byte>.Shared.Rent(alignedLength);
byte[] oldBuffer = _buffer;
@ -321,7 +321,7 @@ public ref struct Path
{
Assert.SdkRequiresNotNull(buffer);
Assert.SdkRequires(length > 0);
Assert.SdkRequires(Alignment.IsAlignedPow2(length, WriteBufferAlignmentLength));
Assert.SdkRequires(Alignment.IsAligned(length, WriteBufferAlignmentLength));
byte[] oldBuffer = _writeBuffer;
_writeBuffer = buffer;
@ -359,7 +359,7 @@ public ref struct Path
if (_writeBufferLength > length)
return Result.Success;
int alignedLength = Alignment.AlignUpPow2(length, WriteBufferAlignmentLength);
int alignedLength = Alignment.AlignUp(length, WriteBufferAlignmentLength);
byte[] buffer = ArrayPool<byte>.Shared.Rent(alignedLength);
SetModifiableBuffer(buffer, alignedLength);
@ -1134,7 +1134,7 @@ public ref struct Path
if (flags.IsWindowsPathAllowed() && WindowsPath.IsWindowsPath(_string, true))
bufferLength += 1;
int alignedBufferLength = Alignment.AlignUpPow2(bufferLength, WriteBufferAlignmentLength);
int alignedBufferLength = Alignment.AlignUp(bufferLength, WriteBufferAlignmentLength);
byte[] rentedArray = null;
try

View file

@ -39,8 +39,8 @@ public readonly struct FileRegion
public FileRegion ExpandAndAlign(uint alignment)
{
long alignedStartOffset = Alignment.AlignDownPow2(Offset, alignment);
long alignedEndOffset = Alignment.AlignUpPow2(GetEndOffset(), alignment);
long alignedStartOffset = Alignment.AlignDown(Offset, alignment);
long alignedEndOffset = Alignment.AlignUp(GetEndOffset(), alignment);
long alignedSize = alignedEndOffset - alignedStartOffset;
return new FileRegion(alignedStartOffset, alignedSize);
@ -48,8 +48,8 @@ public readonly struct FileRegion
public FileRegion ShrinkAndAlign(uint alignment)
{
long alignedStartOffset = Alignment.AlignUpPow2(Offset, alignment);
long alignedEndOffset = Alignment.AlignDownPow2(GetEndOffset(), alignment);
long alignedStartOffset = Alignment.AlignUp(Offset, alignment);
long alignedEndOffset = Alignment.AlignDown(GetEndOffset(), alignment);
long alignedSize = alignedEndOffset - alignedStartOffset;
return new FileRegion(alignedStartOffset, alignedSize);

View file

@ -120,10 +120,10 @@ public static class SaveData
public static Result EnsureSaveDataImpl(this FileSystemClientImpl fs, UserId userId, long saveDataSize,
long saveDataJournalSize, bool extendIfNeeded)
{
if (!Alignment.IsAlignedPow2(saveDataSize, SaveDataBlockSize))
if (!Alignment.IsAligned(saveDataSize, SaveDataBlockSize))
return ResultFs.InvalidSize.Log();
if (!Alignment.IsAlignedPow2(saveDataJournalSize, SaveDataBlockSize))
if (!Alignment.IsAligned(saveDataJournalSize, SaveDataBlockSize))
return ResultFs.InvalidSize.Log();
if (saveDataSize + saveDataJournalSize > SaveDataTotalSizeMax)

View file

@ -146,7 +146,7 @@ public class AccessControl
accessControlData.Slice(data.SaveDataOwnerInfoOffset + sizeof(int), infoCount));
// The ID list must be 4-byte aligned
int idsOffset = Alignment.AlignUpPow2(data.SaveDataOwnerInfoOffset + sizeof(int) + infoCount, 4);
int idsOffset = Alignment.AlignUp(data.SaveDataOwnerInfoOffset + sizeof(int) + infoCount, 4);
ReadOnlySpan<ulong> ids = MemoryMarshal.Cast<byte, ulong>(
accessControlData.Slice(idsOffset, infoCount * sizeof(ulong)));

View file

@ -358,7 +358,7 @@ public class DeviceOperator : IDeviceOperator
// Changed: Removed the alignment check for the buffer address
if (!Alignment.IsAlignedPow2(bufferSize, 0x1000))
if (!Alignment.IsAligned(bufferSize, 0x1000))
return ResultFs.InvalidAlignment.Log();
return _fsServer.Storage.WriteToGameCardDirectly(offset, GetSpan(buffer, bufferSize)).Ret();

View file

@ -217,10 +217,10 @@ public class AesCtrCounterExtendedStorage : IStorage
return Result.Success;
// Reads cannot contain any partial blocks.
if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize))
if (!Alignment.IsAligned(offset, (uint)BlockSize))
return ResultFs.InvalidOffset.Log();
if (!Alignment.IsAlignedPow2(destination.Length, (uint)BlockSize))
if (!Alignment.IsAligned(destination.Length, (uint)BlockSize))
return ResultFs.InvalidSize.Log();
// Ensure the the requested range is within the bounds of the table.
@ -244,7 +244,7 @@ public class AesCtrCounterExtendedStorage : IStorage
// Verify that the entry's offset is aligned to an AES block and within the bounds of the table.
long entryOffset = visitor.Get<Entry>().GetOffset();
if (!Alignment.IsAlignedPow2(entryOffset, (uint)BlockSize) || entryOffset < 0 ||
if (!Alignment.IsAligned(entryOffset, (uint)BlockSize) || entryOffset < 0 ||
!offsets.IsInclude(entryOffset))
{
return ResultFs.InvalidAesCtrCounterExtendedEntryOffset.Log();
@ -283,7 +283,7 @@ public class AesCtrCounterExtendedStorage : IStorage
entryEndOffset = offsets.EndOffset;
}
if (!Alignment.IsAlignedPow2((ulong)entryEndOffset, (uint)BlockSize) || currentOffset >= entryEndOffset)
if (!Alignment.IsAligned((ulong)entryEndOffset, (uint)BlockSize) || currentOffset >= entryEndOffset)
return ResultFs.InvalidAesCtrCounterExtendedEntryOffset.Log();
// Get the part of the entry that contains the data we read.
@ -383,10 +383,10 @@ public class AesCtrCounterExtendedStorage : IStorage
return Result.Success;
}
if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize))
if (!Alignment.IsAligned(offset, (uint)BlockSize))
return ResultFs.InvalidOffset.Log();
if (!Alignment.IsAlignedPow2(size, (uint)BlockSize))
if (!Alignment.IsAligned(size, (uint)BlockSize))
return ResultFs.InvalidSize.Log();
// Ensure the storage contains the provided offset and size.

View file

@ -76,10 +76,10 @@ public class AesCtrStorage : IStorage
return Result.Success;
// Reads cannot contain any partial blocks.
if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize))
if (!Alignment.IsAligned(offset, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(destination.Length, (uint)BlockSize))
if (!Alignment.IsAligned(destination.Length, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
Result res = _baseStorage.Read(offset, destination);
@ -103,10 +103,10 @@ public class AesCtrStorage : IStorage
return Result.Success;
// We can only write full, aligned blocks.
if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize))
if (!Alignment.IsAligned(offset, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(source.Length, (uint)BlockSize))
if (!Alignment.IsAligned(source.Length, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
// Get a pooled buffer.
@ -192,10 +192,10 @@ public class AesCtrStorage : IStorage
return Result.Success;
}
if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize))
if (!Alignment.IsAligned(offset, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(size, (uint)BlockSize))
if (!Alignment.IsAligned(size, (uint)BlockSize))
return ResultFs.InvalidArgument.Log();
}

View file

@ -78,10 +78,10 @@ public class AesXtsStorageExternal : IStorage
return ResultFs.NullptrArgument.Log();
// We can only read at block aligned offsets.
if (!Alignment.IsAlignedPow2(offset, AesBlockSize))
if (!Alignment.IsAligned(offset, AesBlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(destination.Length, AesBlockSize))
if (!Alignment.IsAligned(destination.Length, AesBlockSize))
return ResultFs.InvalidArgument.Log();
// Read the encrypted data.
@ -102,7 +102,7 @@ public class AesXtsStorageExternal : IStorage
if (offset % _blockSize != 0)
{
// Determine the size of the pre-data read.
int skipSize = (int)(offset - Alignment.AlignDownPow2(offset, _blockSize));
int skipSize = (int)(offset - Alignment.AlignDown(offset, _blockSize));
int dataSize = (int)Math.Min(destination.Length, _blockSize - skipSize);
// Decrypt into a pooled buffer.
@ -159,10 +159,10 @@ public class AesXtsStorageExternal : IStorage
return ResultFs.NullptrArgument.Log();
// We can only write at block aligned offsets.
if (!Alignment.IsAlignedPow2(offset, AesBlockSize))
if (!Alignment.IsAligned(offset, AesBlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(source.Length, AesBlockSize))
if (!Alignment.IsAligned(source.Length, AesBlockSize))
return ResultFs.InvalidArgument.Log();
// Get a pooled buffer.
@ -186,7 +186,7 @@ public class AesXtsStorageExternal : IStorage
if (offset % _blockSize != 0)
{
// Determine the size of the pre-data write.
int skipSize = (int)(offset - Alignment.AlignDownPow2(offset, _blockSize));
int skipSize = (int)(offset - Alignment.AlignDown(offset, _blockSize));
int dataSize = (int)Math.Min(source.Length, _blockSize - skipSize);
// Encrypt into a pooled buffer.
@ -289,10 +289,10 @@ public class AesXtsStorageExternal : IStorage
return Result.Success;
// Ensure alignment.
if (!Alignment.IsAlignedPow2(offset, AesBlockSize))
if (!Alignment.IsAligned(offset, AesBlockSize))
return ResultFs.InvalidArgument.Log();
if (!Alignment.IsAlignedPow2(size, AesBlockSize))
if (!Alignment.IsAligned(size, AesBlockSize))
return ResultFs.InvalidArgument.Log();
}

View file

@ -109,7 +109,7 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
public override Result SetSize(long size)
{
Result res = _baseStorage.SetSize(Alignment.AlignUpPow2(size, DataAlign));
Result res = _baseStorage.SetSize(Alignment.AlignUp(size, DataAlign));
_isBaseStorageSizeDirty = true;
return res;
@ -150,8 +150,8 @@ public class AlignmentMatchingStorage<TDataAlignment, TBufferAlignment> : IStora
if (res.IsFailure()) return res.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
long alignedOffset = Alignment.AlignDownPow2(offset, DataAlign);
long alignedOffsetEnd = Alignment.AlignUpPow2(offset + validSize, DataAlign);
long alignedOffset = Alignment.AlignDown(offset, DataAlign);
long alignedOffsetEnd = Alignment.AlignUp(offset + validSize, DataAlign);
long alignedSize = alignedOffsetEnd - alignedOffset;
return _baseStorage.OperateRange(outBuffer, operationId, alignedOffset, alignedSize, inBuffer);
@ -255,7 +255,7 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
public override Result SetSize(long size)
{
Result res = _baseStorage.SetSize(Alignment.AlignUpPow2(size, _dataAlignment));
Result res = _baseStorage.SetSize(Alignment.AlignUp(size, _dataAlignment));
_isBaseStorageSizeDirty = true;
return res;
@ -296,8 +296,8 @@ public class AlignmentMatchingStoragePooledBuffer<TBufferAlignment> : IStorage
if (res.IsFailure()) return res.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUpPow2(offset + validSize, _dataAlignment);
long alignedOffset = Alignment.AlignDown(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUp(offset + validSize, _dataAlignment);
long alignedSize = alignedOffsetEnd - alignedOffset;
return _baseStorage.OperateRange(outBuffer, operationId, alignedOffset, alignedSize, inBuffer);
@ -367,8 +367,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
// Calculate the aligned offsets of the requested region.
long offsetEnd = offset + destination.Length;
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUpPow2(offsetEnd, _dataAlignment);
long alignedOffset = Alignment.AlignDown(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUp(offsetEnd, _dataAlignment);
long alignedSize = alignedOffsetEnd - alignedOffset;
using var pooledBuffer = new PooledBuffer();
@ -406,8 +406,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
}
// Determine read extents for the aligned portion.
long coreOffset = Alignment.AlignUpPow2(offset, _dataAlignment);
long coreOffsetEnd = Alignment.AlignDownPow2(offsetEnd, _dataAlignment);
long coreOffset = Alignment.AlignUp(offset, _dataAlignment);
long coreOffsetEnd = Alignment.AlignDown(offsetEnd, _dataAlignment);
// Handle any data before the aligned portion.
if (offset < coreOffset)
@ -469,7 +469,7 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
public override Result SetSize(long size)
{
Result res = _baseStorage.SetSize(Alignment.AlignUpPow2(size, _dataAlignment));
Result res = _baseStorage.SetSize(Alignment.AlignUp(size, _dataAlignment));
_baseStorageSize = -1;
return res;
@ -509,8 +509,8 @@ public class AlignmentMatchingStorageInBulkRead<TBufferAlignment> : IStorage
if (res.IsFailure()) return res.Miss();
long validSize = Math.Min(size, baseStorageSize - offset);
long alignedOffset = Alignment.AlignDownPow2(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUpPow2(offset + validSize, _dataAlignment);
long alignedOffset = Alignment.AlignDown(offset, _dataAlignment);
long alignedOffsetEnd = Alignment.AlignUp(offset + validSize, _dataAlignment);
long alignedSize = alignedOffsetEnd - alignedOffset;
return _baseStorage.OperateRange(outBuffer, operationId, alignedOffset, alignedSize, inBuffer);

View file

@ -15,22 +15,22 @@ public static class AlignmentMatchingStorageImpl
{
public static uint GetRoundDownDifference(int value, uint alignment)
{
return (uint)(value - Alignment.AlignDownPow2(value, alignment));
return (uint)(value - Alignment.AlignDown(value, alignment));
}
public static uint GetRoundDownDifference(long value, uint alignment)
{
return (uint)(value - Alignment.AlignDownPow2(value, alignment));
return (uint)(value - Alignment.AlignDown(value, alignment));
}
public static uint GetRoundUpDifference(int value, uint alignment)
{
return (uint)(Alignment.AlignUpPow2(value, alignment) - value);
return (uint)(Alignment.AlignUp(value, alignment) - value);
}
private static uint GetRoundUpDifference(long value, uint alignment)
{
return (uint)(Alignment.AlignUpPow2(value, alignment) - value);
return (uint)(Alignment.AlignUp(value, alignment) - value);
}
public static Result Read(in SharedRef<IStorage> storage, Span<byte> workBuffer, uint dataAlignment,
@ -60,10 +60,10 @@ public static class AlignmentMatchingStorageImpl
// Calculate the range that contains only full data blocks.
uint offsetRoundUpDifference = GetRoundUpDifference(offset, dataAlignment);
long coreOffset = Alignment.AlignUpPow2(offset, dataAlignment);
long coreOffset = Alignment.AlignUp(offset, dataAlignment);
long coreSize = destination.Length < offsetRoundUpDifference
? 0
: Alignment.AlignDownPow2(destination.Length - offsetRoundUpDifference, dataAlignment);
: Alignment.AlignDown(destination.Length - offsetRoundUpDifference, dataAlignment);
long coveredOffset = coreSize > 0 ? coreOffset : offset;
@ -77,7 +77,7 @@ public static class AlignmentMatchingStorageImpl
// Read any partial block at the head of the requested range
if (offset < coveredOffset)
{
long headOffset = Alignment.AlignDownPow2(offset, dataAlignment);
long headOffset = Alignment.AlignDown(offset, dataAlignment);
int headSize = (int)(coveredOffset - offset);
Assert.SdkAssert(GetRoundDownDifference(offset, dataAlignment) + headSize <= workBuffer.Length);
@ -94,7 +94,7 @@ public static class AlignmentMatchingStorageImpl
// Read any partial block at the tail of the requested range
while (remainingTailSize > 0)
{
long alignedTailOffset = Alignment.AlignDownPow2(tailOffset, dataAlignment);
long alignedTailOffset = Alignment.AlignDown(tailOffset, dataAlignment);
long copySize = Math.Min(alignedTailOffset + dataAlignment - tailOffset, remainingTailSize);
Result res = storage.Read(alignedTailOffset, workBuffer.Slice(0, (int)dataAlignment));
@ -127,10 +127,10 @@ public static class AlignmentMatchingStorageImpl
// Calculate the range that contains only full data blocks.
uint offsetRoundUpDifference = GetRoundUpDifference(offset, dataAlignment);
long coreOffset = Alignment.AlignUpPow2(offset, dataAlignment);
long coreOffset = Alignment.AlignUp(offset, dataAlignment);
long coreSize = source.Length < offsetRoundUpDifference
? 0
: Alignment.AlignDownPow2(source.Length - offsetRoundUpDifference, dataAlignment);
: Alignment.AlignDown(source.Length - offsetRoundUpDifference, dataAlignment);
long coveredOffset = coreSize > 0 ? coreOffset : offset;
@ -144,7 +144,7 @@ public static class AlignmentMatchingStorageImpl
// Write any partial block at the head of the specified range
if (offset < coveredOffset)
{
long headOffset = Alignment.AlignDownPow2(offset, dataAlignment);
long headOffset = Alignment.AlignDown(offset, dataAlignment);
int headSize = (int)(coveredOffset - offset);
Assert.SdkAssert((offset - headOffset) + headSize <= workBuffer.Length);
@ -168,7 +168,7 @@ public static class AlignmentMatchingStorageImpl
{
Assert.SdkAssert(tailOffset - offset < source.Length);
long alignedTailOffset = Alignment.AlignDownPow2(tailOffset, dataAlignment);
long alignedTailOffset = Alignment.AlignDown(tailOffset, dataAlignment);
long copySize = Math.Min(alignedTailOffset + dataAlignment - tailOffset, remainingTailSize);
// Read the existing block, copy the partial block to the appropriate portion,

View file

@ -77,13 +77,13 @@ public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
public Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize)
{
offsetAppropriate = Alignment.AlignDownPow2(startOffset + accessSize, alignmentSize);
offsetAppropriate = Alignment.AlignDown(startOffset + accessSize, alignmentSize);
return Result.Success;
}
public Result QueryInvocationCount(out long count, long startOffset, long endOffset, long accessSize, long alignmentSize)
{
long alignedStartOffset = Alignment.AlignDownPow2(startOffset, alignmentSize);
long alignedStartOffset = Alignment.AlignDown(startOffset, alignmentSize);
count = BitUtil.DivideUp(endOffset - alignedStartOffset, accessSize);
return Result.Success;
}

View file

@ -554,7 +554,7 @@ public class BufferedStorage : IStorage
private readonly void CalcFetchParameter(out FetchParameter fetchParam, long offset)
{
long blockSize = _bufferedStorage._blockSize;
long storageOffset = Alignment.AlignDownPow2(offset, (uint)_bufferedStorage._blockSize);
long storageOffset = Alignment.AlignDown(offset, (uint)_bufferedStorage._blockSize);
long baseSize = _bufferedStorage._baseStorageSize;
long remainingSize = baseSize - storageOffset;
long cacheSize = Math.Min(blockSize, remainingSize);
@ -1107,7 +1107,7 @@ public class BufferedStorage : IStorage
if (prevSize < size)
{
// Prepare to expand.
if (!Alignment.IsAlignedPow2(prevSize, (uint)_blockSize))
if (!Alignment.IsAligned(prevSize, (uint)_blockSize))
{
using var cache = new SharedCache(this);
long invalidateOffset = prevSize;
@ -1130,7 +1130,7 @@ public class BufferedStorage : IStorage
using var cache = new SharedCache(this);
long invalidateOffset = prevSize;
long invalidateSize = size - prevSize;
bool isFragment = Alignment.IsAlignedPow2(size, (uint)_blockSize);
bool isFragment = Alignment.IsAligned(size, (uint)_blockSize);
while (cache.AcquireNextOverlappedCache(invalidateOffset, invalidateSize))
{
@ -1317,7 +1317,7 @@ public class BufferedStorage : IStorage
int currentSize;
// If the offset is in the middle of a block, read the remaining part of that block.
if (!Alignment.IsAlignedPow2(currentOffset, (uint)_blockSize))
if (!Alignment.IsAligned(currentOffset, (uint)_blockSize))
{
long alignedSize = _blockSize - (currentOffset & (_blockSize - 1));
currentSize = (int)Math.Min(alignedSize, remainingSize);
@ -1330,7 +1330,7 @@ public class BufferedStorage : IStorage
// We have at least one full block to read. Read all the remaining full blocks at once.
else
{
currentSize = (int)Alignment.AlignDownPow2(remainingSize, (uint)_blockSize);
currentSize = (int)Alignment.AlignDown(remainingSize, (uint)_blockSize);
}
Span<byte> currentDestination = destination.Slice((int)bufferOffset, currentSize);
@ -1421,15 +1421,15 @@ public class BufferedStorage : IStorage
/// Otherwise, <see langword="false"/>.</returns>
private bool ReadHeadCache(ref long offset, Span<byte> buffer, ref long size, ref long bufferOffset)
{
bool isCacheNeeded = !Alignment.IsAlignedPow2(offset, (uint)_blockSize);
bool isCacheNeeded = !Alignment.IsAligned(offset, (uint)_blockSize);
while (size > 0)
{
long currentSize;
if (!Alignment.IsAlignedPow2(offset, (uint)_blockSize))
if (!Alignment.IsAligned(offset, (uint)_blockSize))
{
long alignedSize = Alignment.AlignUpPow2(offset, (uint)_blockSize) - offset;
long alignedSize = Alignment.AlignUp(offset, (uint)_blockSize) - offset;
currentSize = Math.Min(alignedSize, size);
}
else if (size < _blockSize)
@ -1458,16 +1458,16 @@ public class BufferedStorage : IStorage
private bool ReadTailCache(long offset, Span<byte> buffer, ref long size, long bufferOffset)
{
bool isCacheNeeded = !Alignment.IsAlignedPow2(offset + size, (uint)_blockSize);
bool isCacheNeeded = !Alignment.IsAligned(offset + size, (uint)_blockSize);
while (size > 0)
{
long currentOffsetEnd = offset + size;
long currentSize;
if (!Alignment.IsAlignedPow2(currentOffsetEnd, (uint)_blockSize))
if (!Alignment.IsAligned(currentOffsetEnd, (uint)_blockSize))
{
long alignedSize = currentOffsetEnd - Alignment.AlignDownPow2(currentOffsetEnd, (uint)_blockSize);
long alignedSize = currentOffsetEnd - Alignment.AlignDown(currentOffsetEnd, (uint)_blockSize);
currentSize = Math.Min(alignedSize, size);
}
else if (size < _blockSize)
@ -1511,8 +1511,8 @@ public class BufferedStorage : IStorage
Result res;
// Determine aligned extents.
long alignedOffset = Alignment.AlignDownPow2(offset, (uint)_blockSize);
long alignedOffsetEnd = Math.Min(Alignment.AlignUpPow2(offset + buffer.Length, (uint)_blockSize),
long alignedOffset = Alignment.AlignDown(offset, (uint)_blockSize);
long alignedOffsetEnd = Math.Min(Alignment.AlignUp(offset + buffer.Length, (uint)_blockSize),
_baseStorageSize);
long alignedSize = alignedOffsetEnd - alignedOffset;
@ -1606,7 +1606,7 @@ public class BufferedStorage : IStorage
if (upgradeResult.wasUpgradeSuccessful)
{
long tailCacheOffset = Alignment.AlignDownPow2(offset + buffer.Length, (uint)_blockSize);
long tailCacheOffset = Alignment.AlignDown(offset + buffer.Length, (uint)_blockSize);
long tailCacheSize = alignedSize - tailCacheOffset + alignedOffset;
res = fetchCache.FetchFromBuffer(tailCacheOffset,
@ -1649,7 +1649,7 @@ public class BufferedStorage : IStorage
ReadOnlySpan<byte> currentSource = source.Slice(bufferOffset);
int currentSize;
if (!Alignment.IsAlignedPow2(currentOffset, (uint)_blockSize))
if (!Alignment.IsAligned(currentOffset, (uint)_blockSize))
{
int alignedSize = (int)(_blockSize - (currentOffset & (_blockSize - 1)));
currentSize = Math.Min(alignedSize, remainingSize);
@ -1660,7 +1660,7 @@ public class BufferedStorage : IStorage
}
else
{
currentSize = Alignment.AlignDownPow2(remainingSize, (uint)_blockSize);
currentSize = Alignment.AlignDown(remainingSize, (uint)_blockSize);
}
Result res;

View file

@ -175,7 +175,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
uint pageListAlignment = (uint)Unsafe.SizeOf<nint>();
const uint ulongAlignment = 8;
return (nuint)Alignment.AlignUpPow2(pageListSize * (orderMax + 1) + pageListAlignment, ulongAlignment);
return (nuint)Alignment.AlignUp(pageListSize * (orderMax + 1) + pageListAlignment, ulongAlignment);
}
public static int QueryOrderMax(nuint size, nuint blockSize)
@ -184,7 +184,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
Assert.SdkRequiresGreaterEqual(blockSize, BlockSizeMin);
Assert.SdkRequires(BitUtil.IsPowerOfTwo(blockSize));
int blockCount = (int)(Alignment.AlignUpPow2(size, (uint)blockSize) / blockSize);
int blockCount = (int)(Alignment.AlignUp(size, (uint)blockSize) / blockSize);
for (int order = 1; ; order++)
{
if (blockCount <= GetBlockCountFromOrder(order))
@ -203,7 +203,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
Assert.SdkRequiresGreaterEqual(workBufferSize, QueryWorkBufferSize(orderMax));
uint pageListAlignment = (uint)Unsafe.SizeOf<nint>();
var alignedWork = (void*)Alignment.AlignUpPow2((ulong)workBuffer, pageListAlignment);
var alignedWork = (void*)Alignment.AlignUp((ulong)workBuffer, pageListAlignment);
ExternalFreeLists = (PageList*)alignedWork;
// Note: The original code does not have a buffer size assert after adjusting for alignment.
@ -531,7 +531,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
private int GetBlockCountFromSize(nuint size)
{
nuint blockSize = GetBlockSize();
return (int)(Alignment.AlignUpPow2(size, (uint)blockSize) / blockSize);
return (int)(Alignment.AlignUp(size, (uint)blockSize) / blockSize);
}
private UIntPtr GetAddressFromPageEntry(PageEntry* pageEntry)
@ -551,7 +551,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
Assert.SdkRequiresLess((nuint)address, HeapStart + HeapSize);
ulong blockStart = (ulong)HeapStart +
Alignment.AlignDownPow2((nuint)address - HeapStart, (uint)GetBlockSize());
Alignment.AlignDown((nuint)address - HeapStart, (uint)GetBlockSize());
return (PageEntry*)blockStart;
}
@ -568,7 +568,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable
private bool IsAlignedToOrder(PageEntry* pageEntry, int order)
{
return Alignment.IsAlignedPow2(GetIndexFromPageEntry(pageEntry), (uint)GetBlockCountFromOrder(order));
return Alignment.IsAligned(GetIndexFromPageEntry(pageEntry), (uint)GetBlockCountFromOrder(order));
}
// Addition: The below fields and methods allow using Memory<byte> with the class instead

View file

@ -98,7 +98,7 @@ public class FileSystemBufferManager : IBufferManager
int entrySize = Unsafe.SizeOf<Entry>() * maxCacheCount;
int attrListSize = Unsafe.SizeOf<AttrInfo>() * 0x100;
return (int)Alignment.AlignUpPow2(
return (int)Alignment.AlignUp(
(ulong)(entrySize + attrListSize + entryAlignment + attrInfoAlignment), 8);
}

View file

@ -137,14 +137,14 @@ public class HierarchicalIntegrityVerificationStorageControlArea : IDisposable
Span<long> levelSize = stackalloc long[IntegrityMaxLayerCount];
int level = layerCount - 1;
levelSize[level] = Alignment.AlignUpPow2(dataSize, (uint)inputParam.LevelBlockSizes[level - 1]);
levelSize[level] = Alignment.AlignUp(dataSize, (uint)inputParam.LevelBlockSizes[level - 1]);
level--;
for (; level > 0; level--)
{
// Calculate how much space is needed to store the hashes of the above level, rounding up to the next block size.
levelSize[level] =
Alignment.AlignUpPow2(levelSize[level + 1] / inputParam.LevelBlockSizes[level] * HashSize,
Alignment.AlignUp(levelSize[level + 1] / inputParam.LevelBlockSizes[level] * HashSize,
(uint)inputParam.LevelBlockSizes[level - 1]);
}

View file

@ -170,7 +170,7 @@ public class IntegrityVerificationStorage : IStorage
if (dataSize < offset)
return ResultFs.InvalidOffset.Log();
long alignedDataSize = Alignment.AlignUpPow2(dataSize, (uint)_verificationBlockSize);
long alignedDataSize = Alignment.AlignUp(dataSize, (uint)_verificationBlockSize);
res = CheckAccessRange(offset, destination.Length, alignedDataSize);
if (res.IsFailure()) return res.Miss();
@ -268,7 +268,7 @@ public class IntegrityVerificationStorage : IStorage
if (offset >= dataSize)
return ResultFs.InvalidOffset.Log();
res = CheckAccessRange(offset, source.Length, Alignment.AlignUpPow2(dataSize, (uint)_verificationBlockSize));
res = CheckAccessRange(offset, source.Length, Alignment.AlignUp(dataSize, (uint)_verificationBlockSize));
if (res.IsFailure()) return res.Miss();
Assert.SdkRequiresAligned(offset, _verificationBlockSize);
@ -292,7 +292,7 @@ public class IntegrityVerificationStorage : IStorage
return Result.Success;
}
int alignedWriteSize = Alignment.AlignUpPow2(writeSize, (uint)_verificationBlockSize);
int alignedWriteSize = Alignment.AlignUp(writeSize, (uint)_verificationBlockSize);
Result updateResult = Result.Success;
int updatedSignatureCount = 0;

View file

@ -168,7 +168,7 @@ public class UnionStorage : IStorage
// Get the start offset of the block containing the requested offset
long offsetBuffer = 0;
long offsetOriginal = Alignment.AlignDownPow2(offset, _blockSize);
long offsetOriginal = Alignment.AlignDown(offset, _blockSize);
long sizeSkipBlock = offset - offsetOriginal;
while (offsetBuffer < destination.Length)
@ -214,7 +214,7 @@ public class UnionStorage : IStorage
// Get the start offset of the block containing the requested offset
long offsetBuffer = 0;
long offsetOriginal = Alignment.AlignDownPow2(offset, _blockSize);
long offsetOriginal = Alignment.AlignDown(offset, _blockSize);
long sizeSkipBlock = offset - offsetOriginal;
while (offsetBuffer < source.Length)
@ -302,7 +302,7 @@ public class UnionStorage : IStorage
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
for (long currentOffset = Alignment.AlignDownPow2(offset, _blockSize);
for (long currentOffset = Alignment.AlignDown(offset, _blockSize);
currentOffset < offset + size;
currentOffset += _blockSize)
{

View file

@ -126,7 +126,7 @@ public class PartitionFileSystemBuilder
size += entry.NameLength + 1;
}
int endOffset = Alignment.AlignUpPow2(startOffset + size, GetMetaDataAlignment(type));
int endOffset = Alignment.AlignUp(startOffset + size, GetMetaDataAlignment(type));
return endOffset - startOffset;
}

View file

@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Diag;
@ -7,58 +8,53 @@ namespace LibHac.Util;
public static class Alignment
{
// The alignment functions in this class come from C++ templates that always cast to unsigned types
public static ulong AlignUpPow2(ulong value, uint alignment)
public static T AlignUp<T>(T value, ulong alignment) where T : IBinaryNumber<T>
{
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment));
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment) || alignment == 0);
ulong invMask = alignment - 1;
return ((value + invMask) & ~invMask);
unchecked
{
ulong invMask = alignment - 1;
return T.CreateTruncating((ulong.CreateTruncating(value) + invMask) & ~invMask);
}
}
public static ulong AlignDownPow2(ulong value, uint alignment)
public static T AlignDown<T>(T value, ulong alignment) where T : IBinaryNumber<T>
{
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment));
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment) || alignment == 0);
ulong invMask = alignment - 1;
return (value & ~invMask);
unchecked
{
ulong invMask = alignment - 1;
return T.CreateTruncating(ulong.CreateTruncating(value) & ~invMask);
}
}
public static bool IsAlignedPow2(ulong value, uint alignment)
{
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment));
public static T AlignDown<T>(T value, long alignment) where T : IBinaryNumber<T> => AlignDown(value, (ulong)alignment);
ulong invMask = alignment - 1;
return (value & invMask) == 0;
public static bool IsAligned<T>(T value, ulong alignment) where T : IBinaryNumber<T>
{
Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment) || alignment == 0);
unchecked
{
ulong invMask = alignment - 1;
return (ulong.CreateTruncating(value) & invMask) == 0;
}
}
public static bool IsAlignedPow2<T>(ReadOnlySpan<T> buffer, uint alignment)
public static bool IsAligned<T>(ReadOnlySpan<T> buffer, ulong alignment)
{
return IsAlignedPow2(ref MemoryMarshal.GetReference(buffer), alignment);
return IsAligned(ref MemoryMarshal.GetReference(buffer), alignment);
}
public static unsafe bool IsAlignedPow2<T>(ref T pointer, uint alignment)
public static unsafe bool IsAligned<T>(ref T pointer, ulong alignment)
{
return IsAlignedPow2((ulong)Unsafe.AsPointer(ref pointer), alignment);
return IsAligned((ulong)Unsafe.AsPointer(ref pointer), alignment);
}
public static int AlignUpPow2(int value, uint alignment) => (int)AlignUpPow2((ulong)value, alignment);
public static long AlignUpPow2(long value, uint alignment) => (long)AlignUpPow2((ulong)value, alignment);
public static int AlignDownPow2(int value, uint alignment) => (int)AlignDownPow2((ulong)value, alignment);
public static long AlignDownPow2(long value, uint alignment) => (long)AlignDownPow2((ulong)value, alignment);
public static long AlignDownPow2(long value, long alignment) => (long)AlignDownPow2((ulong)value, (uint)alignment);
public static bool IsAlignedPow2(int value, uint alignment) => IsAlignedPow2((ulong)value, alignment);
public static bool IsAlignedPow2(long value, uint alignment) => IsAlignedPow2((ulong)value, alignment);
public static ulong AlignUp(ulong value, uint alignment) => AlignDown(value + alignment - 1, alignment);
public static ulong AlignDown(ulong value, uint alignment) => value - value % alignment;
public static bool IsAligned(ulong value, uint alignment) => value % alignment == 0;
public static int AlignUp(int value, uint alignment) => (int)AlignUp((ulong)value, alignment);
public static long AlignUp(long value, uint alignment) => (long)AlignUp((ulong)value, alignment);
public static int AlignDown(int value, uint alignment) => (int)AlignDown((ulong)value, alignment);
public static long AlignDown(long value, uint alignment) => (long)AlignDown((ulong)value, alignment);
public static bool IsAligned(int value, uint alignment) => IsAligned((ulong)value, alignment);
public static bool IsAligned(long value, uint alignment) => IsAligned((ulong)value, alignment);
public static T GetAlignment<T>(T value) where T : IUnsignedNumber<T>, IBinaryInteger<T>
{
return unchecked(value & -value);
}
}

View file

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<IsPackable>false</IsPackable>
</PropertyGroup>

View file

@ -0,0 +1,102 @@
using LibHac.Util;
using Xunit;
namespace LibHac.Tests.Util
{
public class AlignmentTests
{
[Theory]
[InlineData(0, 0x40, 0)]
[InlineData(0x3F, 0x40, 0x40)]
[InlineData(0x40, 0x40, 0x40)]
[InlineData(0x41, 0x40, 0x80)]
public void AlignUp_Byte(byte value, uint alignment, byte expectedValue)
{
var actualValue = Alignment.AlignUp(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, 0)]
[InlineData(-0x3F, 0x40, 0)]
[InlineData(-0x40, 0x40, -0x40)]
[InlineData(-0x41, 0x40, -0x40)]
[InlineData(-0x41, 0, 0)]
[InlineData(int.MaxValue, 0x40, int.MinValue)]
public void AlignUp_Int(int value, uint alignment, int expectedValue)
{
var actualValue = Alignment.AlignUp(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, 0)]
[InlineData(0x3F, 0x40, 0x40)]
[InlineData(0x40, 0x40, 0x40)]
[InlineData(0x41, 0x40, 0x80)]
[InlineData(0xFFF_FFFF_8000, 0x10000, 0x1000_0000_0000)]
public void AlignUp_Ulong(ulong value, uint alignment, ulong expectedValue)
{
var actualValue = Alignment.AlignUp(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, 0)]
[InlineData(0x3F, 0x40, 0)]
[InlineData(0x40, 0x40, 0x40)]
[InlineData(0x41, 0x40, 0x40)]
public void AlignDown_Byte(byte value, uint alignment, byte expectedValue)
{
var actualValue = Alignment.AlignDown(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, 0)]
[InlineData(0x3F, 0x40, 0)]
[InlineData(0x40, 0x40, 0x40)]
[InlineData(0x41, 0x40, 0x40)]
public void AlignDown_Long(long value, uint alignment, long expectedValue)
{
var actualValue = Alignment.AlignDown(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, true)]
[InlineData(0x3F, 0x40, false)]
[InlineData(0x40, 0x40, true)]
[InlineData(0x41, 0x40, false)]
public void IsAligned_Byte(byte value, uint alignment, bool expectedValue)
{
var actualValue = Alignment.IsAligned(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0x40, true)]
[InlineData(0x3F, 0x40, false)]
[InlineData(0x40, 0x40, true)]
[InlineData(0x41, 0x40, false)]
[InlineData(0xFFF_FFFF_8000, 0x400, true)]
public void IsAligned_Long(long value, uint alignment, bool expectedValue)
{
var actualValue = Alignment.IsAligned(value, alignment);
Assert.Equal(expectedValue, actualValue);
}
[Theory]
[InlineData(0, 0)]
[InlineData(0x3F, 1)]
[InlineData(0x40, 0x40)]
[InlineData(0x41, 1)]
[InlineData(0x42, 2)]
[InlineData(0xFF900000, 0x100000)]
public void GetAlignment_Uint(uint value, long expectedValue)
{
var actualValue = Alignment.GetAlignment(value);
Assert.Equal(expectedValue, actualValue);
}
}
}