fenestroj 0.0.11

Easier wrappers for Win32 API stuff, safe when possible
Documentation
//! Basic elements of windows (there's a few modules like this really).

use super::*;

use winapi::um::{
  minwinbase::{LHND, LMEM_FIXED, LMEM_INVALID_HANDLE, LMEM_MOVEABLE, LPTR},
  winbase::{
    LocalAlloc, LocalFlags, LocalFree, LocalHandle, LocalLock, LocalReAlloc,
    LocalSize, LocalUnlock,
  },
};

/// The flags that can be used with [`local_alloc`](local_alloc)
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum LocalAllocFlags {
  /// Allocates fixed memory.
  ///
  /// The return value is a pointer to the memory object.
  Fixed = LMEM_FIXED,
  /// As `Fixed` but the memory is zeroed.
  ZeroedFixed = LPTR,
  /// Allocates movable memory. Higher overhead, avoid if possible.
  ///
  /// Memory blocks are never moved in physical memory, but they can be moved
  /// within the default heap. The return value is a handle to the memory
  /// object. To translate the handle to a pointer, use the `LocalLock`
  /// function.
  Movable = LMEM_MOVEABLE,
  /// As `Movable` but the memory is zeroed.
  ZeroedMovable = LHND,
}
impl Default for LocalAllocFlags {
  fn default() -> Self {
    LocalAllocFlags::Fixed
  }
}

/// Allocates bytes from the heap. High overhead.
///
/// Avoid this function unless it is documented that you must use it in
/// connection with some other function. Use the [heap
/// functions](https://docs.microsoft.com/en-us/windows/win32/memory/heap-functions)
/// instead.
///
/// See
/// [`LocalAlloc`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc)
pub unsafe fn local_alloc(
  flags: LocalAllocFlags,
  bytes: usize,
) -> Result<HLOCAL, ErrorCode> {
  let handle = LocalAlloc(flags as u32, bytes);
  if !handle.is_null() {
    Ok(handle)
  } else {
    Err(get_last_error())
  }
}

/// Changes the size or the attributes of a specified local memory object.
///
/// I don't at all understand how this works in practice.
///
/// If this function fails the old handle and pointer are still valid.
///
/// See
/// [`LocalReAlloc`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localrealloc)
#[inline]
pub unsafe fn local_realloc(
  handle: HLOCAL,
  size: usize,
  flags: u32,
) -> Result<HLOCAL, ErrorCode> {
  let output = LocalReAlloc(handle, size, flags);
  if !output.is_null() {
    Ok(output)
  } else {
    Err(get_last_error())
  }
}

/// Frees/invalidates a Local memory handle.
///
/// If you try to free a null handle it's ignored and you get `Ok(())`.
///
/// This will free a handle even if it's locked.
///
/// See
/// [`LocalFree`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
#[inline]
pub unsafe fn local_free(handle: HLOCAL) -> Result<(), ErrorCode> {
  let output = LocalFree(handle);
  if output.is_null() {
    Ok(())
  } else {
    Err(get_last_error())
  }
}

/// Locks a local handle and gets the address to its actual memory.
///
/// * Fixed memory permanently has a lock count of zero, and doesn't need to be
///   locked to be used. The `HLOCAL` value can be used directly as the pointer
///   to the memory, or you can use this function.
/// * Movable memory must be locked to be used, and you **must not** use the
///   `HLOCAL` value directly as a pointer.
///
/// See
/// [`LocalLock`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-locallock)
#[inline]
pub unsafe fn local_lock(handle: HLOCAL) -> Result<*mut c_void, ErrorCode> {
  let output = LocalLock(handle) as *mut c_void;
  if !output.is_null() {
    Ok(output)
  } else {
    Err(get_last_error())
  }
}

/// Unlocks a local handle.
///
/// Returns `Ok(still_locked)` on success.
///
/// * Fixed memory permanently has a lock count of zero and will always report
///   `ERROR_NOT_LOCKED` if you try to unlock it.
/// * Movable memory will report `ERROR_NOT_LOCKED` if it's not currently
///   locked.
///
/// See
/// [`LocalUnlock`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localunlock)
#[inline]
pub unsafe fn local_unlock(handle: HLOCAL) -> Result<bool, ErrorCode> {
  let output = LocalUnlock(handle);
  if output != 0 {
    Ok(true)
  } else {
    let e = get_last_error();
    if e.0 == NO_ERROR {
      Ok(false)
    } else {
      Err(e)
    }
  }
}

/// Flag info about this local handle.
///
/// * The low-order byte of the low-order word of the return value contains the
///   lock count of the object.
/// * The high-order byte of the low-order word of the return value indicates
///   the allocation values of the memory object. It can be zero or
///   LMEM_DISCARDABLE.
///
/// See
/// [`LocalFlags`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localflags)
#[inline]
pub unsafe fn local_flags(handle: HLOCAL) -> Result<u32, ErrorCode> {
  let output = LocalFlags(handle);
  if output != LMEM_INVALID_HANDLE {
    Ok(output)
  } else {
    Err(get_last_error())
  }
}

/// Checks the size of the allocation for this local handle.
///
/// The allocation of a local handle can be larger than was requested.
///
/// To verify that the allocation hasn't been discarded use
/// [`local_flags`](local_flags) **before** calling this function.
///
/// See
/// [`LocalSize`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localsize)
#[inline]
pub unsafe fn local_size(handle: HLOCAL) -> Result<usize, ErrorCode> {
  let output = LocalSize(handle);
  if output != 0 {
    Ok(output)
  } else {
    Err(get_last_error())
  }
}

/// Gets the local handle associated with the specified pointer to a local
/// memory object.
///
/// This is primarily for use with Movable memory, because in that case the
/// handle value and the pointer value are not the same.
///
/// See
/// [`LocalHandle`](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localhandle)
#[inline]
pub unsafe fn local_handle(ptr: *mut c_void) -> Result<HLOCAL, ErrorCode> {
  let output = LocalHandle(ptr as *mut _);
  if !output.is_null() {
    Ok(output)
  } else {
    Err(get_last_error())
  }
}

/// An owning local memory handle wrapper that will free the memory when dropped.
#[derive(Debug)]
pub(crate) struct OwnedLocalHandle(HLOCAL);
#[allow(dead_code)]
impl OwnedLocalHandle {
  /// Wraps the handle given to be an owned value.
  ///
  /// ## Safety
  ///
  /// Once you have wrapped the `HLOCAL`, you should not attempt to free it
  /// yourself. You also must not use it after you have dropped this struct. The
  /// `Drop` code will free the handle for you when this struct drops.
  pub const unsafe fn new(handle: HLOCAL) -> Self {
    Self(handle)
  }

  /// Copies out the `HLOCAL` value, but it's still owned.
  ///
  /// ## Safety
  ///
  /// There's no lifetime on this output value, so you must not use it after
  /// this struct drops.
  pub const unsafe fn as_hlocal(&self) -> HLOCAL {
    self.0
  }

  /// Unwraps the `HLOCAL` so that it's no longer owned.
  pub fn into_inner(self) -> HLOCAL {
    let out = self.0;
    core::mem::forget(self);
    out
  }
}
impl Drop for OwnedLocalHandle {
  fn drop(&mut self) {
    let free_result = unsafe { local_free(self.0) };
    if cfg!(debug_assertions) {
      free_result.unwrap();
    } else {
      free_result.ok();
    };
  }
}

pub(crate) struct FixedLocalSlice<T> {
  handle: OwnedLocalHandle,
  data_type: PhantomData<T>,
  count: usize,
}
impl<T> FixedLocalSlice<T> {
  /// ## Safety
  ///
  /// ONLY ALLOWED FOR FIXED MEMORY
  pub const unsafe fn new(handle: OwnedLocalHandle, count: usize) -> Self {
    Self {
      handle,
      count,
      data_type: PhantomData,
    }
  }
}
impl<T> Deref for FixedLocalSlice<T> {
  type Target = [T];
  fn deref(&self) -> &[T] {
    unsafe { core::slice::from_raw_parts(self.handle.0 as *const T, self.count) }
  }
}