#![allow(clippy::missing_safety_doc, reason = "these are C intrinsics")]
use core::ffi::c_void;
use core::sync::atomic::{AtomicUsize, Ordering};
static HINSTANCE: AtomicUsize = AtomicUsize::new(0);
pub fn set_hinstance(hinst: *mut c_void) {
HINSTANCE.store(hinst as usize, Ordering::Relaxed);
}
#[must_use]
pub fn hinstance() -> *mut c_void {
HINSTANCE.load(Ordering::Relaxed) as *mut c_void
}
#[cfg(target_os = "windows")]
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetCurrentProcess() -> *mut c_void;
fn TerminateProcess(process: *mut c_void, exit_code: u32) -> i32;
}
#[cfg(target_os = "windows")]
pub fn abort() -> ! {
unsafe { TerminateProcess(GetCurrentProcess(), 0xC000_0409) };
#[allow(clippy::empty_loop, reason = "process is already terminating")]
loop {}
}
#[cfg(all(not(target_os = "windows"), feature = "std"))]
pub fn abort() -> ! {
std::process::abort()
}
#[cfg(all(not(target_os = "windows"), not(feature = "std")))]
pub fn abort() -> ! {
#[allow(clippy::empty_loop, reason = "no_std host build has no abort")]
loop {}
}
#[doc(hidden)]
pub fn unwind_unreachable() -> ! {
abort()
}
#[cfg(target_os = "windows")]
mod allocator {
use core::alloc::{GlobalAlloc, Layout};
use core::ffi::c_void;
use core::sync::atomic::{AtomicUsize, Ordering};
const HEAP_ZERO_MEMORY: u32 = 0x0000_0008;
const NATIVE_ALIGN: usize = if cfg!(target_pointer_width = "64") {
16
} else {
8
};
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetProcessHeap() -> *mut c_void;
fn HeapAlloc(heap: *mut c_void, flags: u32, bytes: usize) -> *mut c_void;
fn HeapReAlloc(
heap: *mut c_void,
flags: u32,
mem: *mut c_void,
bytes: usize,
) -> *mut c_void;
fn HeapFree(heap: *mut c_void, flags: u32, mem: *mut c_void) -> i32;
}
static HEAP: AtomicUsize = AtomicUsize::new(0);
fn heap() -> *mut c_void {
let cached = HEAP.load(Ordering::Relaxed);
if cached != 0 {
return cached as *mut c_void;
}
let h = unsafe { GetProcessHeap() };
HEAP.store(h as usize, Ordering::Relaxed);
h
}
pub struct NsisAllocator;
const HEADER: usize = core::mem::size_of::<usize>();
impl NsisAllocator {
unsafe fn alloc_flagged(layout: Layout, flags: u32) -> *mut u8 {
if layout.align() <= NATIVE_ALIGN {
return unsafe { HeapAlloc(heap(), flags, layout.size()) }.cast();
}
let total = layout.size() + layout.align() + HEADER;
let base: *mut u8 = unsafe { HeapAlloc(heap(), flags, total) }.cast();
if base.is_null() {
return base;
}
let candidate = base as usize + HEADER;
let aligned = (candidate + layout.align() - 1) & !(layout.align() - 1);
let ptr = aligned as *mut u8;
unsafe { ptr.sub(HEADER).cast::<usize>().write(base as usize) };
ptr
}
unsafe fn base_of(ptr: *mut u8, layout: Layout) -> *mut u8 {
if layout.align() <= NATIVE_ALIGN {
ptr
} else {
unsafe { ptr.sub(HEADER).cast::<usize>().read() as *mut u8 }
}
}
}
unsafe impl GlobalAlloc for NsisAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { Self::alloc_flagged(layout, 0) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
unsafe { Self::alloc_flagged(layout, HEAP_ZERO_MEMORY) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let base = unsafe { Self::base_of(ptr, layout) };
unsafe { HeapFree(heap(), 0, base.cast()) };
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= NATIVE_ALIGN {
return unsafe { HeapReAlloc(heap(), 0, ptr.cast(), new_size) }.cast();
}
let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
return core::ptr::null_mut();
};
let new_ptr = unsafe { self.alloc(new_layout) };
if !new_ptr.is_null() {
unsafe {
core::ptr::copy_nonoverlapping(ptr, new_ptr, layout.size().min(new_size));
self.dealloc(ptr, layout);
}
}
new_ptr
}
}
}
#[cfg(target_os = "windows")]
pub use allocator::NsisAllocator;
#[cfg(all(target_os = "windows", feature = "mem-intrinsics"))]
#[allow(
clippy::undocumented_unsafe_blocks,
reason = "every block rests on the same C contract: the caller guarantees \
`n` valid bytes behind each pointer"
)]
#[allow(
suspicious_runtime_symbol_definitions,
reason = "`*mut u8` is ABI-identical to `*mut c_void` and keeps the byte \
loops free of casts"
)]
mod mem {
#[unsafe(no_mangle)]
unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
let mut i = 0;
while i < n {
unsafe { *dest.add(i) = *src.add(i) };
i += 1;
}
dest
}
#[unsafe(no_mangle)]
unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
if (dest as usize) < (src as usize) {
let mut i = 0;
while i < n {
unsafe { *dest.add(i) = *src.add(i) };
i += 1;
}
} else {
let mut i = n;
while i != 0 {
i -= 1;
unsafe { *dest.add(i) = *src.add(i) };
}
}
dest
}
#[unsafe(no_mangle)]
unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
let byte = c as u8;
let mut i = 0;
while i < n {
unsafe { *dest.add(i) = byte };
i += 1;
}
dest
}
#[unsafe(no_mangle)]
unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
let mut i = 0;
while i < n {
let (x, y) = unsafe { (*a.add(i), *b.add(i)) };
if x != y {
return i32::from(x) - i32::from(y);
}
i += 1;
}
0
}
#[unsafe(no_mangle)]
unsafe extern "C" fn bcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
unsafe { memcmp(a, b, n) }
}
}