using SmartCoalApplication.SystemLayer; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SmartCoalApplication.Core { [Serializable] public unsafe sealed class MemoryBlock : IDisposable, ICloneable { private const int serializationChunkSize = 1048576; private const long largeBlockThreshold = 65536; private long length; [NonSerialized] private void* voidStar; [NonSerialized] private bool valid; private MemoryBlock parentBlock = null; [NonSerialized] private IntPtr bitmapHandle = IntPtr.Zero; private int bitmapWidth; private int bitmapHeight; private bool disposed = false; public void* VoidStar { get { if (disposed) { throw new ObjectDisposedException("MemoryBlock"); } return voidStar; } } public bool MaySetAllowWrites { get { if (disposed) { throw new ObjectDisposedException("MemoryBlock"); } if (this.parentBlock != null) { return this.parentBlock.MaySetAllowWrites; } else { return (this.length >= largeBlockThreshold && this.bitmapHandle != IntPtr.Zero); } } } public static void CopyBlock(MemoryBlock dst, long dstOffset, MemoryBlock src, long srcOffset, long length) { if ((dstOffset + length > dst.length) || (srcOffset + length > src.length)) { throw new ArgumentOutOfRangeException("", "copy ranges were out of bounds"); } if (dstOffset < 0) { throw new ArgumentOutOfRangeException("dstOffset", dstOffset, "must be >= 0"); } if (srcOffset < 0) { throw new ArgumentOutOfRangeException("srcOffset", srcOffset, "must be >= 0"); } if (length < 0) { throw new ArgumentOutOfRangeException("length", length, "must be >= 0"); } void* dstPtr = (void*)((byte*)dst.VoidStar + dstOffset); void* srcPtr = (void*)((byte*)src.VoidStar + srcOffset); Memory.Copy(dstPtr, srcPtr, (ulong)length); } object ICloneable.Clone() { if (disposed) { throw new ObjectDisposedException("MemoryBlock"); } return (object)Clone(); } public MemoryBlock Clone() { if (disposed) { throw new ObjectDisposedException("MemoryBlock"); } MemoryBlock dupe = new MemoryBlock(this.length); CopyBlock(dupe, 0, this, 0, length); return dupe; } public MemoryBlock(long bytes) { if (bytes <= 0) { throw new ArgumentOutOfRangeException("bytes", bytes, "Bytes must be greater than zero"); } this.length = bytes; this.parentBlock = null; this.voidStar = Allocate(bytes).ToPointer(); this.valid = true; } public MemoryBlock(int width, int height) { if (width < 0 && height < 0) { throw new ArgumentOutOfRangeException("width/height", new Size(width, height), "width and height must be >= 0"); } else if (width < 0) { throw new ArgumentOutOfRangeException("width", width, "width must be >= 0"); } else if (height < 0) { throw new ArgumentOutOfRangeException("height", width, "height must be >= 0"); } this.length = width * height * ColorBgra.SizeOf; this.parentBlock = null; this.voidStar = Allocate(width, height, out this.bitmapHandle).ToPointer(); this.valid = true; this.bitmapWidth = width; this.bitmapHeight = height; } public unsafe MemoryBlock(MemoryBlock parentBlock, long offset, long length) { if (offset + length > parentBlock.length) { throw new ArgumentOutOfRangeException(); } this.parentBlock = parentBlock; byte* bytePointer = (byte*)parentBlock.VoidStar; bytePointer += offset; this.voidStar = (void*)bytePointer; this.valid = true; this.length = length; } ~MemoryBlock() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!disposed) { disposed = true; if (disposing) { } if (this.valid && parentBlock == null) { if (this.bitmapHandle != IntPtr.Zero) { Memory.FreeBitmap(this.bitmapHandle, this.bitmapWidth, this.bitmapHeight); } else if (this.length >= largeBlockThreshold) { Memory.FreeLarge(new IntPtr(voidStar), (ulong)this.length); } else { Memory.Free(new IntPtr(voidStar)); } } parentBlock = null; voidStar = null; this.valid = false; } } private static IntPtr Allocate(int width, int height, out IntPtr handle) { return Allocate(width, height, out handle, true); } private static IntPtr Allocate(int width, int height, out IntPtr handle, bool allowRetry) { IntPtr block; try { block = Memory.AllocateBitmap(width, height, out handle); } catch (OutOfMemoryException) { if (allowRetry) { Utility.GCFullCollect(); return Allocate(width, height, out handle, false); } else { throw; } } return block; } private static IntPtr Allocate(long bytes) { return Allocate(bytes, true); } private static IntPtr Allocate(long bytes, bool allowRetry) { IntPtr block; try { if (bytes >= largeBlockThreshold) { block = Memory.AllocateLarge((ulong)bytes); } else { block = Memory.Allocate((ulong)bytes); } } catch (OutOfMemoryException) { if (allowRetry) { Utility.GCFullCollect(); return Allocate(bytes, false); } else { throw; } } return block; } } }