context: /////////////////////////////////////////////////////////////////////////////////
  // Paint.NET                                                                   //
  // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors.     //
  // Portions Copyright (C) Microsoft Corporation. All Rights Reserved.          //
  // See src/Resources/Files/License.txt for full licensing and attribution      //
  // details.                                                                    //
  // .                                                                           //
  /////////////////////////////////////////////////////////////////////////////////
  
  //#define REPORTLEAKS
  
  using System;
  using System.Runtime.InteropServices;
  using System.Windows.Forms;
  
  namespace PaintDotNet.SystemLayer
  {
      /// <summary>
      /// Contains methods for allocating, freeing, and performing operations on memory
      /// that is fixed (pinned) in memory.
      /// </summary>
      public unsafe static class Memory
      {
          private static IntPtr hHeap;
  
          static Memory()
          {
              hHeap = SafeNativeMethods.HeapCreate(0, IntPtr.Zero, IntPtr.Zero);
  
              uint info = 2;
  
              try
              {
                  // Enable the low-fragmentation heap (LFH)
                  SafeNativeMethods.HeapSetInformation(hHeap, NativeConstants.HeapCompatibilityInformation, (void*)&info, sizeof(uint));
              }
              catch (Exception)
              {
                  // If that method isn't available, like on Win2K, don't worry about it.
              }
  
              Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
          }
  
          /// <summary>
          /// Gets the total amount of physical memory (RAM) in the system.
          /// </summary>
          public static ulong TotalPhysicalBytes
          {
              get
              {
                  NativeStructs.MEMORYSTATUSEX mse = new NativeStructs.MEMORYSTATUSEX();
                  mse.dwLength = (uint)sizeof(NativeStructs.MEMORYSTATUSEX);
  
                  bool result = NativeMethods.GlobalMemoryStatusEx(ref mse);
  
                  if (!result)
                      NativeMethods.ThrowOnWin32Error("GlobalMemoryStatusEx");
  
                  return mse.ullTotalPhys;
              }
          }
  
          private static void DestroyHeap()
          {
              IntPtr hHeap2 = hHeap;
              hHeap = IntPtr.Zero;
              SafeNativeMethods.HeapDestroy(hHeap2);
          }
  
          private static void Application_ApplicationExit(object sender, EventArgs e)
          {
              DestroyHeap();
          }
  
          /// <summary>
          /// Allocates a block of memory at least as large as the amount requested.
          /// </summary>
          /// <param name="bytes">The number of bytes you want to allocate.</param>
          /// <returns>A pointer to a block of memory at least as large as <b>bytes</b>.</returns>
          /// <exception cref="OutOfMemoryException">Thrown if the memory manager could not fulfill the request for a memory block at least as large as <b>bytes</b>.</exception>
          public static IntPtr Allocate(ulong bytes)
          {
              if (hHeap == IntPtr.Zero)
              {
                  throw new InvalidOperationException("heap has already been destroyed");
              }
              else
              {
                  IntPtr block = SafeNativeMethods.HeapAlloc(hHeap, 0, new UIntPtr(bytes));
  
                  if (block == IntPtr.Zero)
                      throw new OutOfMemoryException("HeapAlloc returned a null pointer");
  
                  if (bytes > 0)
                      GC.AddMemoryPressure((long)bytes);
  
                  return block;
              }
          }
  
          /// <summary>
          /// Allocates a block of memory at least as large as the amount requested.
          /// </summary>
          /// <param name="bytes">The number of bytes you want to allocate.</param>
          /// <returns>A pointer to a block of memory at least as large as bytes</returns>
          /// <remarks>
          /// This method uses an alternate method for allocating memory (VirtualAlloc in Windows). The allocation
          /// granularity is the page size of the system (usually 4K). Blocks allocated with this method may also
          /// be protected using the ProtectBlock method.
          /// </remarks>
          public static IntPtr AllocateLarge(ulong bytes)
          {
              IntPtr block = SafeNativeMethods.VirtualAlloc(IntPtr.Zero, new UIntPtr(bytes),
                  NativeConstants.MEM_COMMIT, NativeConstants.PAGE_READWRITE);
  
              if (block == IntPtr.Zero)
                  throw new OutOfMemoryException("VirtualAlloc returned a null pointer");
  
              if (bytes > 0)
                  GC.AddMemoryPressure((long)bytes);
  
              return block;
          }
  
          /// <summary>
          /// Allocates a bitmap of the given height and width. Pixel data may be read/written directly,
          /// and it may be drawn to the screen using PdnGraphics.DrawBitmap().
          /// </summary>
          /// <param name="width">The width of the bitmap to allocate.</param>
          /// <param name="height">The height of the bitmap to allocate.</param>
          /// <param name="handle">Receives a handle to the bitmap.</param>
          /// <returns>A pointer to the bitmap's pixel data.</returns>
          /// <remarks>
          /// The following invariants may be useful for implementors:
          /// * The bitmap is always 32-bits per pixel, BGRA.
          /// * Stride for the bitmap is always width * 4.
          /// * The upper-left pixel of the bitmap (0,0) is located at the first memory location pointed to by the returned pointer.
          /// * The bitmap is top-down ("memory correct" ordering).
          /// * The 'handle' may be any type of data you want, but must be unique for the lifetime of the bitmap, and must not be IntPtr.Zero.
          /// * The handle's value must be understanded by PdnGraphics.DrawBitmap().
          /// * The bitmap is always modified by directly reading and writing to the memory pointed to by the return value.
          /// * PdnGraphics.DrawBitmap() must always render from this memory location (i.e. it must treat the memory as 'volatile')
          /// </remarks>
          public static IntPtr AllocateBitmap(int width, int height, out IntPtr handle)
          {
              NativeStructs.BITMAPINFO bmi = new NativeStructs.BITMAPINFO();
              bmi.bmiHeader.biSize = (uint)sizeof(NativeStructs.BITMAPINFOHEADER);
              bmi.bmiHeader.biWidth = width;
              bmi.bmiHeader.biHeight = -height;
              bmi.bmiHeader.biPlanes = 1;
              bmi.bmiHeader.biBitCount = 32;
              bmi.bmiHeader.biCompression = NativeConstants.BI_RGB;
              bmi.bmiHeader.biSizeImage = 0;
              bmi.bmiHeader.biXPelsPerMeter = 96;
              bmi.bmiHeader.biYPelsPerMeter = 96;
              bmi.bmiHeader.biClrUsed = 0;
              bmi.bmiHeader.biClrImportant = 0;
  
              IntPtr pvBits;
              IntPtr hBitmap = SafeNativeMethods.CreateDIBSection(
                  IntPtr.Zero,
                  ref bmi,
                  NativeConstants.DIB_RGB_COLORS,
                  out pvBits,
                  IntPtr.Zero,
                  0);
  
              if (hBitmap == IntPtr.Zero)
                  throw new OutOfMemoryException("CreateDIBSection returned NULL (" + Marshal.GetLastWin32Error().ToString() + ") while attempting to allocate " + width + "x" + height + " bitmap");
  
              handle = hBitmap;
              long bytes = width * (long)height * 4;
  
              if (bytes > 0)
                  GC.AddMemoryPressure(bytes);
  
              return pvBits;
          }
  
          /// <summary>
          /// Frees a bitmap previously allocated with AllocateBitmap.
          /// </summary>
          /// <param name="handle">The handle that was returned from a previous call to AllocateBitmap.</param>
          /// <param name="width">The width of the bitmap, as specified in the original call to AllocateBitmap.</param>
          /// <param name="height">The height of the bitmap, as specified in the original call to AllocateBitmap.</param>
          public static void FreeBitmap(IntPtr handle, int width, int height)
          {
              long bytes = width * (long)height * 4;
  
              bool bResult = SafeNativeMethods.DeleteObject(handle);
  
              if (!bResult)
                  NativeMethods.ThrowOnWin32Error("DeleteObject returned false");
  
              if (bytes > 0)
                  GC.RemoveMemoryPressure(bytes);
          }
  
          /// <summary>
          /// Frees a block of memory previously allocated with Allocate().
          /// </summary>
          /// <param name="block">The block to free.</param>
          /// <exception cref="InvalidOperationException">There was an error freeing the block.</exception>
          public static void Free(IntPtr block)
          {
              if (hHeap != IntPtr.Zero)
              {
                  long bytes = (long)SafeNativeMethods.HeapSize(hHeap, 0, block);
  
                  bool result = SafeNativeMethods.HeapFree(hHeap, 0, block);
  
                  if (!result)
                  {
                      int error = Marshal.GetLastWin32Error();
                      throw new InvalidOperationException("HeapFree returned an error: " + error.ToString());
                  }
  
                  if (bytes > 0)
                      GC.RemoveMemoryPressure(bytes);
              }
              else
              {
  #if REPORTLEAKS
                  throw new InvalidOperationException("memory leak! check the debug output for more info, and http://blogs.msdn.com/ricom/archive/2004/12/10/279612.aspx to track it down");
  #endif
              }
          }
  
          /// <summary>
          /// Frees a block of memory previous allocated with AllocateLarge().
          /// </summary>
          /// <param name="block">The block to free.</param>
          /// <param name="bytes">The size of the block.</param>
          public static void FreeLarge(IntPtr block, ulong bytes)
          {
              bool result = SafeNativeMethods.VirtualFree(block, UIntPtr.Zero, NativeConstants.MEM_RELEASE);
  
              if (!result)
              {
                  int error = Marshal.GetLastWin32Error();
                  throw new InvalidOperationException("VirtualFree returned an error: " + error.ToString());
              }
  
              if (bytes > 0)
                  GC.RemoveMemoryPressure((long)bytes);
          }
  
          /// <summary>
          /// Sets protection on a block previously allocated with AllocateLarge.
          /// </summary>
          /// <param name="block">The starting memory address to set protection for.</param>
          /// <param name="size">The size of the block.</param>
          /// <param name="readAccess">Whether to allow read access.</param>
          /// <param name="writeAccess">Whether to allow write access.</param>
          /// <remarks>
          /// You may not specify false for read access without also specifying false for write access.
          /// Note to implementors: This method is not guaranteed to actually set read/write-ability
          /// on a block of memory, and may instead be implemented as a no-op after parameter validation.
          /// </remarks>
          public static void ProtectBlockLarge(IntPtr block, ulong size, bool readAccess, bool writeAccess)
          {
              uint flOldProtect;
              uint flNewProtect;
  
              if (readAccess && writeAccess)
                  flNewProtect = NativeConstants.PAGE_READWRITE;
              else if (readAccess && !writeAccess)
                  flNewProtect = NativeConstants.PAGE_READONLY;
              else if (!readAccess && !writeAccess)
                  flNewProtect = NativeConstants.PAGE_NOACCESS;
              else
                  throw new InvalidOperationException("May not specify a page to be write-only");
  
  #if DEBUGSPEW
              Tracing.Ping("ProtectBlockLarge: block #" + block.ToString() + ", read: " + readAccess + ", write: " + writeAccess);
  #endif
  
              SafeNativeMethods.VirtualProtect(block, new UIntPtr(size), flNewProtect, out flOldProtect);
          }
  
          /// <summary>
          /// Copies bytes from one area of memory to another. Since this function only
          /// takes pointers, it can not do any bounds checking.
          /// </summary>
          /// <param name="dst">The starting address of where to copy bytes to.</param>
          /// <param name="src">The starting address of where to copy bytes from.</param>
          /// <param name="length">The number of bytes to copy</param>
          public static void Copy(IntPtr dst, IntPtr src, ulong length)
          {
  
gt:             Copy(dst.ToPointer(), src.ToPointer(), length);