1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! 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) }
  }
}