1#![allow(clippy::missing_safety_doc, reason = "these are C intrinsics")]
8
9use core::ffi::c_void;
10use core::sync::atomic::{AtomicUsize, Ordering};
11
12static HINSTANCE: AtomicUsize = AtomicUsize::new(0);
14
15pub fn set_hinstance(hinst: *mut c_void) {
17 HINSTANCE.store(hinst as usize, Ordering::Relaxed);
18}
19
20#[must_use]
25pub fn hinstance() -> *mut c_void {
26 HINSTANCE.load(Ordering::Relaxed) as *mut c_void
27}
28
29#[cfg(target_os = "windows")]
36#[link(name = "kernel32")]
37unsafe extern "system" {
38 fn GetCurrentProcess() -> *mut c_void;
39 fn TerminateProcess(process: *mut c_void, exit_code: u32) -> i32;
40}
41
42#[cfg(target_os = "windows")]
48pub fn abort() -> ! {
49 unsafe { TerminateProcess(GetCurrentProcess(), 0xC000_0409) };
52 #[allow(clippy::empty_loop, reason = "process is already terminating")]
54 loop {}
55}
56
57#[cfg(all(not(target_os = "windows"), feature = "std"))]
59pub fn abort() -> ! {
60 std::process::abort()
61}
62
63#[cfg(all(not(target_os = "windows"), not(feature = "std")))]
65pub fn abort() -> ! {
66 #[allow(clippy::empty_loop, reason = "no_std host build has no abort")]
67 loop {}
68}
69
70#[doc(hidden)]
95pub fn unwind_unreachable() -> ! {
96 abort()
97}
98
99#[cfg(target_os = "windows")]
102mod allocator {
103 use core::alloc::{GlobalAlloc, Layout};
104 use core::ffi::c_void;
105 use core::sync::atomic::{AtomicUsize, Ordering};
106
107 const HEAP_ZERO_MEMORY: u32 = 0x0000_0008;
108
109 const NATIVE_ALIGN: usize = if cfg!(target_pointer_width = "64") {
111 16
112 } else {
113 8
114 };
115
116 #[link(name = "kernel32")]
117 unsafe extern "system" {
118 fn GetProcessHeap() -> *mut c_void;
119 fn HeapAlloc(heap: *mut c_void, flags: u32, bytes: usize) -> *mut c_void;
120 fn HeapReAlloc(
121 heap: *mut c_void,
122 flags: u32,
123 mem: *mut c_void,
124 bytes: usize,
125 ) -> *mut c_void;
126 fn HeapFree(heap: *mut c_void, flags: u32, mem: *mut c_void) -> i32;
127 }
128
129 static HEAP: AtomicUsize = AtomicUsize::new(0);
130
131 fn heap() -> *mut c_void {
132 let cached = HEAP.load(Ordering::Relaxed);
133 if cached != 0 {
134 return cached as *mut c_void;
135 }
136 let h = unsafe { GetProcessHeap() };
138 HEAP.store(h as usize, Ordering::Relaxed);
139 h
140 }
141
142 pub struct NsisAllocator;
148
149 const HEADER: usize = core::mem::size_of::<usize>();
151
152 impl NsisAllocator {
153 unsafe fn alloc_flagged(layout: Layout, flags: u32) -> *mut u8 {
154 if layout.align() <= NATIVE_ALIGN {
155 return unsafe { HeapAlloc(heap(), flags, layout.size()) }.cast();
158 }
159 let total = layout.size() + layout.align() + HEADER;
162 let base: *mut u8 = unsafe { HeapAlloc(heap(), flags, total) }.cast();
164 if base.is_null() {
165 return base;
166 }
167 let candidate = base as usize + HEADER;
168 let aligned = (candidate + layout.align() - 1) & !(layout.align() - 1);
169 let ptr = aligned as *mut u8;
170 unsafe { ptr.sub(HEADER).cast::<usize>().write(base as usize) };
176 ptr
177 }
178
179 unsafe fn base_of(ptr: *mut u8, layout: Layout) -> *mut u8 {
180 if layout.align() <= NATIVE_ALIGN {
181 ptr
182 } else {
183 unsafe { ptr.sub(HEADER).cast::<usize>().read() as *mut u8 }
186 }
187 }
188 }
189
190 unsafe impl GlobalAlloc for NsisAllocator {
195 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
196 unsafe { Self::alloc_flagged(layout, 0) }
198 }
199
200 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
201 unsafe { Self::alloc_flagged(layout, HEAP_ZERO_MEMORY) }
203 }
204
205 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
206 let base = unsafe { Self::base_of(ptr, layout) };
209 unsafe { HeapFree(heap(), 0, base.cast()) };
211 }
212
213 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
214 if layout.align() <= NATIVE_ALIGN {
215 return unsafe { HeapReAlloc(heap(), 0, ptr.cast(), new_size) }.cast();
218 }
219 let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
221 return core::ptr::null_mut();
222 };
223 let new_ptr = unsafe { self.alloc(new_layout) };
225 if !new_ptr.is_null() {
226 unsafe {
230 core::ptr::copy_nonoverlapping(ptr, new_ptr, layout.size().min(new_size));
231 self.dealloc(ptr, layout);
232 }
233 }
234 new_ptr
235 }
236 }
237}
238
239#[cfg(target_os = "windows")]
240pub use allocator::NsisAllocator;
241
242#[cfg(all(target_os = "windows", feature = "mem-intrinsics"))]
250#[allow(
251 clippy::undocumented_unsafe_blocks,
252 reason = "every block rests on the same C contract: the caller guarantees \
253 `n` valid bytes behind each pointer"
254)]
255#[allow(
256 suspicious_runtime_symbol_definitions,
257 reason = "`*mut u8` is ABI-identical to `*mut c_void` and keeps the byte \
258 loops free of casts"
259)]
260mod mem {
261 #[unsafe(no_mangle)]
262 unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
263 let mut i = 0;
264 while i < n {
265 unsafe { *dest.add(i) = *src.add(i) };
266 i += 1;
267 }
268 dest
269 }
270
271 #[unsafe(no_mangle)]
272 unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
273 if (dest as usize) < (src as usize) {
274 let mut i = 0;
275 while i < n {
276 unsafe { *dest.add(i) = *src.add(i) };
277 i += 1;
278 }
279 } else {
280 let mut i = n;
281 while i != 0 {
282 i -= 1;
283 unsafe { *dest.add(i) = *src.add(i) };
284 }
285 }
286 dest
287 }
288
289 #[unsafe(no_mangle)]
290 unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
291 let byte = c as u8;
292 let mut i = 0;
293 while i < n {
294 unsafe { *dest.add(i) = byte };
295 i += 1;
296 }
297 dest
298 }
299
300 #[unsafe(no_mangle)]
301 unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
302 let mut i = 0;
303 while i < n {
304 let (x, y) = unsafe { (*a.add(i), *b.add(i)) };
305 if x != y {
306 return i32::from(x) - i32::from(y);
307 }
308 i += 1;
309 }
310 0
311 }
312
313 #[unsafe(no_mangle)]
314 unsafe extern "C" fn bcmp(a: *const u8, b: *const u8, n: usize) -> i32 {
315 unsafe { memcmp(a, b, n) }
316 }
317}