using System; using System.Runtime.InteropServices; namespace SvdCli.Storage.Implements { public class UnmanagedGigabyteRamDisk : ISvdStorage { private readonly IntPtr[] _blocks; private bool _isInit; public string ProductId { get; } = "Svd RamDisk"; public string ProductVersion { get; } = "1.0"; public uint BlockSize { get; } = 512; public ulong BlockCount { get; } public uint MaxTransferLength { get; } = 64 * CapacityUnits.KiloByte; public bool SupportTrim { get; } = false; public bool WriteProtect { get; set; } public Guid Guid { get; } = Guid.NewGuid(); public event EventHandler Mounted; public UnmanagedGigabyteRamDisk(int sizeInGigabyte) { _blocks = new IntPtr[sizeInGigabyte]; var size = (ulong)sizeInGigabyte * CapacityUnits.Gigabyte; BlockCount = size / BlockSize; } public void Init() { if (_isInit) return; try { for (var i = 0; i < _blocks.Length; i++) { _blocks[i] = Marshal.AllocHGlobal(CapacityUnits.Gigabyte); } _isInit = true; } catch (OutOfMemoryException) { Exit(); throw; } } public void Exit() { foreach (var block in _blocks) { if (block != IntPtr.Zero) Marshal.FreeHGlobal(block); } GC.Collect(); GC.WaitForPendingFinalizers(); _isInit = false; } private void ReadInternal(byte[] buffer, int offset, int count, int gigabyteIndex, int gigabyteOffset) { Marshal.Copy(_blocks[gigabyteIndex] + gigabyteOffset, buffer, offset, count); } private void WriteInternal(byte[] buffer, int offset, int count, int gigabyteIndex, int gigabyteOffset) { Marshal.Copy(buffer, offset, _blocks[gigabyteIndex] + gigabyteOffset, count); } public void ReadBlocks(byte[] buffer, ulong blockIndex, uint blockCount) { var rawOffset = blockIndex * BlockSize; var rawSize = blockCount * BlockSize; for (var i = 0; i < rawSize;) { var gigabyteIndex = (int)(rawOffset / CapacityUnits.Gigabyte); var gigabyteOffset = (int)(rawOffset % CapacityUnits.Gigabyte); var bytesToRead = CapacityUnits.Gigabyte - gigabyteOffset; if (i + bytesToRead > rawSize) { bytesToRead = (int)(rawSize - i); } ReadInternal(buffer, i, bytesToRead, gigabyteIndex, gigabyteOffset); i += bytesToRead; rawOffset += (ulong)bytesToRead; } } public void WriteBlocks(byte[] buffer, ulong blockIndex, uint blockCount) { var rawOffset = blockIndex * BlockSize; var rawSize = (int)(blockCount * BlockSize); for (var i = 0; i < rawSize;) { var gigabyteIndex = (int)(rawOffset / CapacityUnits.Gigabyte); var gigabyteOffset = (int)(rawOffset % CapacityUnits.Gigabyte); var bytesToWrite = CapacityUnits.Gigabyte - gigabyteOffset; if (i + bytesToWrite > rawSize) { bytesToWrite = rawSize - i; } WriteInternal(buffer, i, bytesToWrite, gigabyteIndex, gigabyteOffset); i += bytesToWrite; rawOffset += (ulong)bytesToWrite; } } public int Read(byte[] buffer, long offset, int index, int count) { for (var i = 0; i < count;) { var gigabyteIndex = (int)(offset / CapacityUnits.Gigabyte); var gigabyteOffset = (int)(offset % CapacityUnits.Gigabyte); var bytesToRead = CapacityUnits.Gigabyte - gigabyteOffset; if (i + bytesToRead > count) { bytesToRead = (count - i); } ReadInternal(buffer, i, bytesToRead, gigabyteIndex, gigabyteOffset); i += bytesToRead; offset += bytesToRead; } return count; } public void Write(byte[] buffer, long offset, int index, int count) { for (var i = 0; i < count;) { var gigabyteIndex = (int)(offset / CapacityUnits.Gigabyte); var gigabyteOffset = (int)(offset % CapacityUnits.Gigabyte); var bytesToWrite = CapacityUnits.Gigabyte - gigabyteOffset; if (i + bytesToWrite > count) { bytesToWrite = count - i; } WriteInternal(buffer, i, bytesToWrite, gigabyteIndex, gigabyteOffset); i += bytesToWrite; offset += bytesToWrite; } } public void Flush() { } public void Trim(params TrimDescriptor[] descriptors) { } public void FireMountedEvent() { Mounted?.Invoke(this, EventArgs.Empty); } } }