From df77de365c3ab4effbabbe2b09bdda763b223333 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 13 Dec 2022 19:10:10 -0700 Subject: [PATCH] Use generic math in Util.Alignment --- src/LibHac/Diag/Impl/AssertImpl.cs | 4 +- .../Fs/ApplicationSaveDataManagement.cs | 6 +- src/LibHac/Fs/Common/Path.cs | 8 +- src/LibHac/Fs/Impl/FileRegion.cs | 8 +- src/LibHac/Fs/Shim/SaveData.cs | 4 +- src/LibHac/FsSrv/Impl/AccessControl.cs | 2 +- src/LibHac/FsSrv/Impl/DeviceOperator.cs | 2 +- .../FsSystem/AesCtrCounterExtendedStorage.cs | 12 +-- src/LibHac/FsSystem/AesCtrStorage.cs | 12 +-- src/LibHac/FsSystem/AesXtsStorageExternal.cs | 16 +-- .../FsSystem/AlignmentMatchingStorage.cs | 26 ++--- .../FsSystem/AlignmentMatchingStorageImpl.cs | 24 ++--- src/LibHac/FsSystem/AsynchronousAccess.cs | 4 +- src/LibHac/FsSystem/BufferedStorage.cs | 32 +++--- .../FsSystem/Buffers/FileSystemBuddyHeap.cs | 12 +-- .../Buffers/FileSystemBufferManager.cs | 2 +- ...ierarchicalIntegrityVerificationStorage.cs | 4 +- .../FsSystem/IntegrityVerificationStorage.cs | 6 +- src/LibHac/FsSystem/UnionStorage.cs | 6 +- .../FsSystem/PartitionFileSystemBuilder.cs | 2 +- src/LibHac/Util/Alignment.cs | 70 ++++++------ tests/LibHac.Tests/LibHac.Tests.csproj | 1 + tests/LibHac.Tests/Util/AlignmentTests.cs | 102 ++++++++++++++++++ 23 files changed, 232 insertions(+), 133 deletions(-) create mode 100644 tests/LibHac.Tests/Util/AlignmentTests.cs diff --git a/src/LibHac/Diag/Impl/AssertImpl.cs b/src/LibHac/Diag/Impl/AssertImpl.cs index 8529a9f6..4bc0317f 100644 --- a/src/LibHac/Diag/Impl/AssertImpl.cs +++ b/src/LibHac/Diag/Impl/AssertImpl.cs @@ -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); } } \ No newline at end of file diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index ba9f74c3..79cbca43 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -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); diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs index 1abbdba1..f213d389 100644 --- a/src/LibHac/Fs/Common/Path.cs +++ b/src/LibHac/Fs/Common/Path.cs @@ -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.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.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 diff --git a/src/LibHac/Fs/Impl/FileRegion.cs b/src/LibHac/Fs/Impl/FileRegion.cs index 2d9d3bd0..304bb9f7 100644 --- a/src/LibHac/Fs/Impl/FileRegion.cs +++ b/src/LibHac/Fs/Impl/FileRegion.cs @@ -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); diff --git a/src/LibHac/Fs/Shim/SaveData.cs b/src/LibHac/Fs/Shim/SaveData.cs index 92522424..384c65da 100644 --- a/src/LibHac/Fs/Shim/SaveData.cs +++ b/src/LibHac/Fs/Shim/SaveData.cs @@ -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) diff --git a/src/LibHac/FsSrv/Impl/AccessControl.cs b/src/LibHac/FsSrv/Impl/AccessControl.cs index dd0b2f22..df8e4835 100644 --- a/src/LibHac/FsSrv/Impl/AccessControl.cs +++ b/src/LibHac/FsSrv/Impl/AccessControl.cs @@ -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 ids = MemoryMarshal.Cast( accessControlData.Slice(idsOffset, infoCount * sizeof(ulong))); diff --git a/src/LibHac/FsSrv/Impl/DeviceOperator.cs b/src/LibHac/FsSrv/Impl/DeviceOperator.cs index ba4466a6..d74a22c5 100644 --- a/src/LibHac/FsSrv/Impl/DeviceOperator.cs +++ b/src/LibHac/FsSrv/Impl/DeviceOperator.cs @@ -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(); diff --git a/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs b/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs index cbc1b89e..06cf980e 100644 --- a/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs +++ b/src/LibHac/FsSystem/AesCtrCounterExtendedStorage.cs @@ -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().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. diff --git a/src/LibHac/FsSystem/AesCtrStorage.cs b/src/LibHac/FsSystem/AesCtrStorage.cs index 500a937b..2c8afd56 100644 --- a/src/LibHac/FsSystem/AesCtrStorage.cs +++ b/src/LibHac/FsSystem/AesCtrStorage.cs @@ -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(); } diff --git a/src/LibHac/FsSystem/AesXtsStorageExternal.cs b/src/LibHac/FsSystem/AesXtsStorageExternal.cs index 23270f36..e6b43108 100644 --- a/src/LibHac/FsSystem/AesXtsStorageExternal.cs +++ b/src/LibHac/FsSystem/AesXtsStorageExternal.cs @@ -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(); } diff --git a/src/LibHac/FsSystem/AlignmentMatchingStorage.cs b/src/LibHac/FsSystem/AlignmentMatchingStorage.cs index cd089fd9..fdb62fb5 100644 --- a/src/LibHac/FsSystem/AlignmentMatchingStorage.cs +++ b/src/LibHac/FsSystem/AlignmentMatchingStorage.cs @@ -109,7 +109,7 @@ public class AlignmentMatchingStorage : 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 : 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 : 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 : 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 : 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 : 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 : 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 : 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); diff --git a/src/LibHac/FsSystem/AlignmentMatchingStorageImpl.cs b/src/LibHac/FsSystem/AlignmentMatchingStorageImpl.cs index adcbecfe..5c9968b7 100644 --- a/src/LibHac/FsSystem/AlignmentMatchingStorageImpl.cs +++ b/src/LibHac/FsSystem/AlignmentMatchingStorageImpl.cs @@ -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 storage, Span 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, diff --git a/src/LibHac/FsSystem/AsynchronousAccess.cs b/src/LibHac/FsSystem/AsynchronousAccess.cs index c903efa6..45527b88 100644 --- a/src/LibHac/FsSystem/AsynchronousAccess.cs +++ b/src/LibHac/FsSystem/AsynchronousAccess.cs @@ -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; } diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index da8be731..e1d20a47 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -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 currentDestination = destination.Slice((int)bufferOffset, currentSize); @@ -1421,15 +1421,15 @@ public class BufferedStorage : IStorage /// Otherwise, . private bool ReadHeadCache(ref long offset, Span 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 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 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; diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs b/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs index e9c9699c..03b8bc7a 100644 --- a/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs +++ b/src/LibHac/FsSystem/Buffers/FileSystemBuddyHeap.cs @@ -175,7 +175,7 @@ public unsafe class FileSystemBuddyHeap : IDisposable uint pageListAlignment = (uint)Unsafe.SizeOf(); 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(); - 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 with the class instead diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs index 9fd1c844..28b94515 100644 --- a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs +++ b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs @@ -98,7 +98,7 @@ public class FileSystemBufferManager : IBufferManager int entrySize = Unsafe.SizeOf() * maxCacheCount; int attrListSize = Unsafe.SizeOf() * 0x100; - return (int)Alignment.AlignUpPow2( + return (int)Alignment.AlignUp( (ulong)(entrySize + attrListSize + entryAlignment + attrInfoAlignment), 8); } diff --git a/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs b/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs index 31da2bac..06228f98 100644 --- a/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs +++ b/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs @@ -137,14 +137,14 @@ public class HierarchicalIntegrityVerificationStorageControlArea : IDisposable Span 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]); } diff --git a/src/LibHac/FsSystem/IntegrityVerificationStorage.cs b/src/LibHac/FsSystem/IntegrityVerificationStorage.cs index f7d52bfa..39895d8d 100644 --- a/src/LibHac/FsSystem/IntegrityVerificationStorage.cs +++ b/src/LibHac/FsSystem/IntegrityVerificationStorage.cs @@ -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; diff --git a/src/LibHac/FsSystem/UnionStorage.cs b/src/LibHac/FsSystem/UnionStorage.cs index 87323751..4e5f8c42 100644 --- a/src/LibHac/FsSystem/UnionStorage.cs +++ b/src/LibHac/FsSystem/UnionStorage.cs @@ -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 outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { - for (long currentOffset = Alignment.AlignDownPow2(offset, _blockSize); + for (long currentOffset = Alignment.AlignDown(offset, _blockSize); currentOffset < offset + size; currentOffset += _blockSize) { diff --git a/src/LibHac/Tools/FsSystem/PartitionFileSystemBuilder.cs b/src/LibHac/Tools/FsSystem/PartitionFileSystemBuilder.cs index e20fa49e..e7f25c37 100644 --- a/src/LibHac/Tools/FsSystem/PartitionFileSystemBuilder.cs +++ b/src/LibHac/Tools/FsSystem/PartitionFileSystemBuilder.cs @@ -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; } diff --git a/src/LibHac/Util/Alignment.cs b/src/LibHac/Util/Alignment.cs index 16eb451b..e9362135 100644 --- a/src/LibHac/Util/Alignment.cs +++ b/src/LibHac/Util/Alignment.cs @@ -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 value, ulong alignment) where T : IBinaryNumber { - 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 value, ulong alignment) where T : IBinaryNumber { - 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 value, long alignment) where T : IBinaryNumber => AlignDown(value, (ulong)alignment); - ulong invMask = alignment - 1; - return (value & invMask) == 0; + public static bool IsAligned(T value, ulong alignment) where T : IBinaryNumber + { + Assert.SdkRequires(BitUtil.IsPowerOfTwo(alignment) || alignment == 0); + + unchecked + { + ulong invMask = alignment - 1; + return (ulong.CreateTruncating(value) & invMask) == 0; + } } - public static bool IsAlignedPow2(ReadOnlySpan buffer, uint alignment) + public static bool IsAligned(ReadOnlySpan buffer, ulong alignment) { - return IsAlignedPow2(ref MemoryMarshal.GetReference(buffer), alignment); + return IsAligned(ref MemoryMarshal.GetReference(buffer), alignment); } - public static unsafe bool IsAlignedPow2(ref T pointer, uint alignment) + public static unsafe bool IsAligned(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 value) where T : IUnsignedNumber, IBinaryInteger + { + return unchecked(value & -value); + } } \ No newline at end of file diff --git a/tests/LibHac.Tests/LibHac.Tests.csproj b/tests/LibHac.Tests/LibHac.Tests.csproj index c8f2a861..194507c6 100644 --- a/tests/LibHac.Tests/LibHac.Tests.csproj +++ b/tests/LibHac.Tests/LibHac.Tests.csproj @@ -3,6 +3,7 @@ net7.0 true + true false diff --git a/tests/LibHac.Tests/Util/AlignmentTests.cs b/tests/LibHac.Tests/Util/AlignmentTests.cs new file mode 100644 index 00000000..936b5dfc --- /dev/null +++ b/tests/LibHac.Tests/Util/AlignmentTests.cs @@ -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); + } + } +}