diff --git a/csharp/Platform.Memory.Tests/VirtualResizableDirectMemoryTests.cs b/csharp/Platform.Memory.Tests/VirtualResizableDirectMemoryTests.cs
new file mode 100644
index 0000000..a3fa62d
--- /dev/null
+++ b/csharp/Platform.Memory.Tests/VirtualResizableDirectMemoryTests.cs
@@ -0,0 +1,103 @@
+using System;
+using Xunit;
+
+namespace Platform.Memory.Tests
+{
+ public unsafe class VirtualResizableDirectMemoryTests
+ {
+ [Fact]
+ public void CorrectMemoryAllocationTest()
+ {
+ using var virtualMemory = new VirtualResizableDirectMemory();
+ Assert.True(virtualMemory.Pointer != IntPtr.Zero);
+ Assert.True(virtualMemory.ReservedCapacity >= VirtualResizableDirectMemory.MinimumCapacity);
+ Assert.Equal(0, virtualMemory.UsedCapacity);
+ }
+
+ [Fact]
+ public void CorrectMemoryReallocationTest()
+ {
+ using var virtualMemory = new VirtualResizableDirectMemory();
+ var value1 = GetLastByte(virtualMemory);
+ virtualMemory.ReservedCapacity *= 2;
+ var value2 = GetLastByte(virtualMemory);
+ Assert.Equal(value1, value2);
+ Assert.Equal(0, value1);
+ }
+
+ [Fact]
+ public void MemoryZeroingTest()
+ {
+ using var virtualMemory = new VirtualResizableDirectMemory(8192);
+ var pointer = (byte*)virtualMemory.Pointer;
+
+ // Check that initial memory is zeroed
+ for (int i = 0; i < 8192; i++)
+ {
+ Assert.Equal(0, pointer[i]);
+ }
+ }
+
+ [Fact]
+ public void MemoryResizePreservesDataTest()
+ {
+ using var virtualMemory = new VirtualResizableDirectMemory(4096);
+ var pointer = (byte*)virtualMemory.Pointer;
+
+ // Write test pattern
+ for (int i = 0; i < 1024; i++)
+ {
+ pointer[i] = (byte)(i % 256);
+ }
+
+ // Resize to larger capacity
+ virtualMemory.ReservedCapacity = 8192;
+ pointer = (byte*)virtualMemory.Pointer;
+
+ // Verify data is preserved
+ for (int i = 0; i < 1024; i++)
+ {
+ Assert.Equal((byte)(i % 256), pointer[i]);
+ }
+
+ // Verify new memory is zeroed
+ for (int i = 4096; i < 8192; i++)
+ {
+ Assert.Equal(0, pointer[i]);
+ }
+ }
+
+ [Fact]
+ public void UsedCapacitySetTest()
+ {
+ using var virtualMemory = new VirtualResizableDirectMemory(4096);
+ Assert.Equal(0, virtualMemory.UsedCapacity);
+
+ virtualMemory.UsedCapacity = 2048;
+ Assert.Equal(2048, virtualMemory.UsedCapacity);
+
+ virtualMemory.UsedCapacity = 4096;
+ Assert.Equal(4096, virtualMemory.UsedCapacity);
+ }
+
+ [Fact]
+ public void InitialCapacityTest()
+ {
+ using var virtualMemory1 = new VirtualResizableDirectMemory();
+ Assert.Equal(VirtualResizableDirectMemory.MinimumCapacity, virtualMemory1.ReservedCapacity);
+
+ using var virtualMemory2 = new VirtualResizableDirectMemory(8192);
+ Assert.Equal(8192, virtualMemory2.ReservedCapacity);
+
+ // Test minimum capacity enforcement
+ using var virtualMemory3 = new VirtualResizableDirectMemory(100);
+ Assert.Equal(VirtualResizableDirectMemory.MinimumCapacity, virtualMemory3.ReservedCapacity);
+ }
+
+ private static byte GetLastByte(VirtualResizableDirectMemory virtualMemory)
+ {
+ var pointer = (void*)virtualMemory.Pointer;
+ return *((byte*)pointer + virtualMemory.ReservedCapacity - 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Memory/Platform.Memory.csproj b/csharp/Platform.Memory/Platform.Memory.csproj
index 6363b22..7ae5470 100644
--- a/csharp/Platform.Memory/Platform.Memory.csproj
+++ b/csharp/Platform.Memory/Platform.Memory.csproj
@@ -4,13 +4,13 @@
LinksPlatform's Platform.Memory Class Library
Konstantin Diachenko
Platform.Memory
- 0.4.1
+ 0.5.0
Konstantin Diachenko
net8
true
Platform.Memory
Platform.Memory
- LinksPlatform;Memory;ArrayMemory;DirectMemoryAsArrayMemoryAdapter;FileArrayMemory;FileMappedResizableDirectMemory;HeapResizableDirectMemory;IArrayMemory;IDirectMemory;IMemory;IResizableDirectMemory;ResizableDirectMemoryBase;TemporaryFileMappedResizableDirectMemory;MemoryMappedFiles
+ LinksPlatform;Memory;ArrayMemory;DirectMemoryAsArrayMemoryAdapter;FileArrayMemory;FileMappedResizableDirectMemory;HeapResizableDirectMemory;VirtualResizableDirectMemory;IArrayMemory;IDirectMemory;IMemory;IResizableDirectMemory;ResizableDirectMemoryBase;TemporaryFileMappedResizableDirectMemory;MemoryMappedFiles
https://raw.githubusercontent.com/linksplatform/Documentation/18469f4d033ee9a5b7b84caab9c585acab2ac519/doc/Avatar-rainbow-icon-64x64.png
https://linksplatform.github.io/Memory
Unlicense
@@ -24,7 +24,7 @@
true
snupkg
latest
- Update target framework from net7 to net8.
+ Add VirtualResizableDirectMemory class using virtual memory API for cross-platform memory allocation.
enable
diff --git a/csharp/Platform.Memory/VirtualResizableDirectMemory.cs b/csharp/Platform.Memory/VirtualResizableDirectMemory.cs
new file mode 100644
index 0000000..94ffb79
--- /dev/null
+++ b/csharp/Platform.Memory/VirtualResizableDirectMemory.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Platform.Unsafe;
+
+namespace Platform.Memory
+{
+ ///
+ /// Represents a memory block allocated using virtual memory API.
+ /// Представляет блок памяти, выделенный с использованием API виртуальной памяти.
+ ///
+ public unsafe class VirtualResizableDirectMemory : ResizableDirectMemoryBase
+ {
+ #region Native API Constants
+
+ private const uint MEM_COMMIT = 0x1000;
+ private const uint MEM_RESERVE = 0x2000;
+ private const uint MEM_RELEASE = 0x8000;
+ private const uint PAGE_READWRITE = 0x04;
+
+ private const int PROT_READ = 0x1;
+ private const int PROT_WRITE = 0x2;
+ private const int MAP_PRIVATE = 0x02;
+ private const int MAP_ANONYMOUS = 0x20;
+ private const int MAP_FAILED = -1;
+
+ #endregion
+
+ #region Native API Declarations
+
+ // Windows Virtual Memory API
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern IntPtr VirtualAlloc(IntPtr lpAddress, nuint dwSize, uint flAllocationType, uint flProtect);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool VirtualFree(IntPtr lpAddress, nuint dwSize, uint dwFreeType);
+
+ // Unix/Linux/macOS Memory Mapping API
+ [DllImport("libc", SetLastError = true)]
+ private static extern IntPtr mmap(IntPtr addr, nuint length, int prot, int flags, int fd, nint offset);
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern int munmap(IntPtr addr, nuint length);
+
+ // Linux-specific mremap (not available on macOS)
+ [DllImport("libc", SetLastError = true)]
+ private static extern IntPtr mremap(IntPtr old_address, nuint old_size, nuint new_size, int flags);
+
+ private const int MREMAP_MAYMOVE = 1;
+
+ #endregion
+
+ #region Fields
+
+ private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ private static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+
+ private long _allocatedSize;
+
+ #endregion
+
+ #region DisposableBase Properties
+
+ ///
+ protected override string ObjectName
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => $"Virtual memory block at {Pointer} address.";
+ }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Инициализирует новый экземпляр класса .
+ ///
+ /// Minimum memory size in bytes.Минимальный размер памяти в байтах.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public VirtualResizableDirectMemory(long minimumReservedCapacity)
+ {
+ if (minimumReservedCapacity < MinimumCapacity)
+ {
+ minimumReservedCapacity = MinimumCapacity;
+ }
+ ReservedCapacity = minimumReservedCapacity;
+ UsedCapacity = 0;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Инициализирует новый экземпляр класса .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public VirtualResizableDirectMemory() : this(MinimumCapacity) { }
+
+ #endregion
+
+ #region ResizableDirectMemoryBase Methods
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void DisposePointer(IntPtr pointer, long usedCapacity)
+ {
+ if (IsWindows)
+ {
+ if (!VirtualFree(pointer, 0, MEM_RELEASE))
+ {
+ throw new InvalidOperationException($"Failed to free virtual memory at {pointer}. Error: {Marshal.GetLastWin32Error()}");
+ }
+ }
+ else
+ {
+ if (munmap(pointer, (nuint)_allocatedSize) != 0)
+ {
+ throw new InvalidOperationException($"Failed to unmap virtual memory at {pointer}. Error: {Marshal.GetLastPInvokeError()}");
+ }
+ }
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void OnReservedCapacityChanged(long oldReservedCapacity, long newReservedCapacity)
+ {
+ if (Pointer == IntPtr.Zero)
+ {
+ // Initial allocation
+ Pointer = AllocateVirtualMemory(newReservedCapacity);
+ _allocatedSize = newReservedCapacity;
+ MemoryBlock.Zero((void*)Pointer, newReservedCapacity);
+ }
+ else
+ {
+ // Resize existing allocation
+ if (IsLinux && oldReservedCapacity < newReservedCapacity)
+ {
+ // Try to use mremap on Linux for efficient resizing
+ try
+ {
+ var remappedPointer = mremap(Pointer, (nuint)oldReservedCapacity, (nuint)newReservedCapacity, MREMAP_MAYMOVE);
+ if (remappedPointer != new IntPtr(MAP_FAILED))
+ {
+ Pointer = remappedPointer;
+ _allocatedSize = newReservedCapacity;
+ // Zero the new memory region
+ var pointer = (byte*)Pointer + oldReservedCapacity;
+ MemoryBlock.Zero(pointer, newReservedCapacity - oldReservedCapacity);
+ return;
+ }
+ }
+ catch
+ {
+ // Fall back to manual allocation and copy
+ }
+ }
+
+ // Manual resize: allocate new, copy old, free old
+ var newPointer = AllocateVirtualMemory(newReservedCapacity);
+ var copySize = Math.Min(oldReservedCapacity, newReservedCapacity);
+ Buffer.MemoryCopy((void*)Pointer, (void*)newPointer, newReservedCapacity, copySize);
+
+ // Zero the additional memory if growing
+ if (newReservedCapacity > oldReservedCapacity)
+ {
+ var pointer = (byte*)newPointer + oldReservedCapacity;
+ MemoryBlock.Zero(pointer, newReservedCapacity - oldReservedCapacity);
+ }
+
+ DisposePointer(Pointer, oldReservedCapacity);
+ Pointer = newPointer;
+ _allocatedSize = newReservedCapacity;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static IntPtr AllocateVirtualMemory(long size)
+ {
+ if (IsWindows)
+ {
+ var pointer = VirtualAlloc(IntPtr.Zero, (nuint)size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (pointer == IntPtr.Zero)
+ {
+ throw new OutOfMemoryException($"Failed to allocate virtual memory of size {size}. Error: {Marshal.GetLastWin32Error()}");
+ }
+ return pointer;
+ }
+ else
+ {
+ var pointer = mmap(IntPtr.Zero, (nuint)size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pointer == new IntPtr(MAP_FAILED))
+ {
+ throw new OutOfMemoryException($"Failed to allocate virtual memory of size {size}. Error: {Marshal.GetLastPInvokeError()}");
+ }
+ return pointer;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file