using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.PInvoke; namespace Vanara.InteropServices { /// A memory block aligned on a specific byte boundary. /// The type of the memory. /// public class AlignedMemory : SafeAllocatedMemoryHandle where TMem : IMemoryMethods, new() { /// The implementation instance. protected TMem mm = new TMem(); /// The number of bytes currently allocated. protected SizeT sz; private int alignment; private IntPtr rawMemPtr; /// Initializes a new instance of the class. /// The number of aligned bytes to allocate. /// The memory offset to which the memory is aligned. /// /// sizeInBytes - The value of this argument must be non-negative or alignmentBoundary - Alignment must be a power of 2. /// public AlignedMemory(int sizeInBytes, int alignmentBoundary) : base(IntPtr.Zero, true) { if (sizeInBytes < 0) throw new ArgumentOutOfRangeException(nameof(sizeInBytes), "The value of this argument must be non-negative"); if (!IsPowerOfTwo(alignmentBoundary)) throw new ArgumentOutOfRangeException(nameof(alignmentBoundary), "Alignment must be a power of 2."); if (sizeInBytes == 0) return; RuntimeHelpers.PrepareConstrainedRegions(); alignment = alignmentBoundary; // TODO: Look at optimizing allocation by checking to see if first one is already aligned. rawMemPtr = mm.AllocMem(sz = GetBufSz(sizeInBytes)); SetHandle(rawMemPtr.Offset(GetOffset())); Zero(); } /// Initializes a new instance of the class. /// The number of aligned bytes to allocate. /// The type to which to align the memory. Memory will be aligned to the byte size of this type. public AlignedMemory(int sizeInBytes, Type alignemntType) : this(sizeInBytes, Marshal.SizeOf(alignemntType)) { } /// Gets a value indicating whether the handle value is invalid. public override bool IsInvalid => handle == IntPtr.Zero; /// Gets or sets the size in bytes of the allocated memory block. /// The size in bytes of the allocated memory block. public override SizeT Size { get => IsInvalid ? 0 : sz + 1 - alignment; set { if (value == 0) { ReleaseHandle(); } else { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); var newsz = GetBufSz(value); rawMemPtr = IsInvalid ? mm.AllocMem(newsz) : mm.ReAllocMem(rawMemPtr, newsz); SetHandle(rawMemPtr.Offset(GetOffset())); if (value > Size) handle.Offset(Size).FillMemory(0, value - Size); sz = newsz; } } } /// When overridden in a derived class, executes the code required to free the handle. /// /// if the handle is released successfully; otherwise, in the event of a catastrophic failure, /// . In this case, it generates a releaseHandleFailed MDA Managed Debugging Assistant. /// protected override bool ReleaseHandle() { mm.FreeMem(rawMemPtr); sz = 0; handle = IntPtr.Zero; return true; } private bool IsPowerOfTwo(int x) => x != 0 && (x & (x - 1)) == 0; private int GetBufSz(int value) => value + alignment - 1; private long GetOffset() => alignment - rawMemPtr.ToInt64() % alignment; } }