extern crate alloc;
#[cfg(windows)]
use crate::os::windows::alloc::{
nstd_os_windows_alloc_allocate, nstd_os_windows_alloc_allocate_zeroed,
nstd_os_windows_alloc_deallocate,
};
use crate::{
core::{
alloc::{
nstd_core_alloc_layout_align, nstd_core_alloc_layout_new,
nstd_core_alloc_layout_new_unchecked, nstd_core_alloc_layout_size, NSTDAllocError,
NSTDAllocLayout, NSTDAllocator,
},
mem::{nstd_core_mem_copy, nstd_core_mem_dangling_mut},
optional::NSTDOptional,
},
NSTDAny, NSTDAnyMut, NSTD_NULL,
};
use cfg_if::cfg_if;
use core::{
alloc::Layout,
marker::PhantomData,
ops::{Deref, DerefMut},
ptr::addr_of,
};
use nstdapi::nstdapi;
#[repr(transparent)]
#[allow(dead_code)]
pub(crate) struct CBox<T>(NSTDAnyMut, PhantomData<T>);
#[allow(dead_code)]
impl<T> CBox<T> {
pub(crate) fn new(value: T) -> Option<Self> {
match core::mem::size_of::<T>() {
#[allow(unused_unsafe)]
0 => unsafe { Some(Self(nstd_core_mem_dangling_mut(), PhantomData)) },
size => {
#[allow(unused_unsafe)]
match unsafe { nstd_core_alloc_layout_new(size, core::mem::align_of::<T>()) } {
NSTDOptional::Some(layout) => match unsafe { nstd_alloc_allocate(layout) } {
NSTD_NULL => None,
mem => {
unsafe { nstd_core_mem_copy(mem.cast(), addr_of!(value).cast(), size) };
core::mem::forget(value);
Some(Self(mem, PhantomData))
}
},
NSTDOptional::None => None,
}
}
}
}
pub(crate) fn into_inner(self) -> T {
let value = unsafe { (self.0 as *const T).read() };
let size = core::mem::size_of::<T>();
if size > 0 {
let align = core::mem::align_of::<T>();
let layout = unsafe { nstd_core_alloc_layout_new_unchecked(size, align) };
unsafe { nstd_alloc_deallocate(self.0, layout) };
}
core::mem::forget(self);
value
}
}
impl<T> Deref for CBox<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self.0 as *const _) }
}
}
impl<T> DerefMut for CBox<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0.cast() }
}
}
impl<T> Drop for CBox<T> {
fn drop(&mut self) {
unsafe {
drop(self.0.cast::<T>().read());
let size = core::mem::size_of::<T>();
if size > 0 {
let align = core::mem::align_of::<T>();
let layout = nstd_core_alloc_layout_new_unchecked(size, align);
nstd_alloc_deallocate(self.0, layout);
}
}
}
}
#[inline]
unsafe extern "C" fn allocate(_: NSTDAny, layout: NSTDAllocLayout) -> NSTDAnyMut {
nstd_alloc_allocate(layout)
}
#[inline]
unsafe extern "C" fn allocate_zeroed(_: NSTDAny, layout: NSTDAllocLayout) -> NSTDAnyMut {
nstd_alloc_allocate_zeroed(layout)
}
#[inline]
unsafe extern "C" fn reallocate(
_: NSTDAny,
ptr: &mut NSTDAnyMut,
old_layout: NSTDAllocLayout,
new_layout: NSTDAllocLayout,
) -> NSTDAllocError {
nstd_alloc_reallocate(ptr, old_layout, new_layout)
}
#[inline]
unsafe extern "C" fn deallocate(
_: NSTDAny,
ptr: NSTDAnyMut,
layout: NSTDAllocLayout,
) -> NSTDAllocError {
nstd_alloc_deallocate(ptr, layout)
}
#[nstdapi]
pub static NSTD_ALLOCATOR: NSTDAllocator = NSTDAllocator {
state: NSTD_NULL,
allocate,
allocate_zeroed,
reallocate,
deallocate,
};
#[inline]
unsafe extern "C" fn rust_allocate(_: NSTDAny, layout: NSTDAllocLayout) -> NSTDAnyMut {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
return alloc::alloc::alloc(layout).cast();
}
NSTD_NULL
}
#[inline]
unsafe extern "C" fn rust_allocate_zeroed(_: NSTDAny, layout: NSTDAllocLayout) -> NSTDAnyMut {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
return alloc::alloc::alloc_zeroed(layout).cast();
}
NSTD_NULL
}
unsafe extern "C" fn rust_reallocate(
this: NSTDAny,
ptr: &mut NSTDAnyMut,
old_layout: NSTDAllocLayout,
new_layout: NSTDAllocLayout,
) -> NSTDAllocError {
if old_layout != new_layout {
let new_mem = rust_allocate(this, new_layout);
if new_mem.is_null() {
return NSTDAllocError::NSTD_ALLOC_ERROR_OUT_OF_MEMORY;
}
let old_size = nstd_core_alloc_layout_size(old_layout);
let new_size = nstd_core_alloc_layout_size(new_layout);
nstd_core_mem_copy(new_mem.cast(), (*ptr).cast(), old_size.min(new_size));
rust_deallocate(this, *ptr, old_layout);
*ptr = new_mem;
}
NSTDAllocError::NSTD_ALLOC_ERROR_NONE
}
unsafe extern "C" fn rust_deallocate(
_: NSTDAny,
ptr: NSTDAnyMut,
layout: NSTDAllocLayout,
) -> NSTDAllocError {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
alloc::alloc::dealloc(ptr.cast(), layout);
return NSTDAllocError::NSTD_ALLOC_ERROR_NONE;
}
NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT
}
#[allow(dead_code)]
pub(crate) static GLOBAL_ALLOCATOR: NSTDAllocator = NSTDAllocator {
state: NSTD_NULL,
allocate: rust_allocate,
allocate_zeroed: rust_allocate_zeroed,
reallocate: rust_reallocate,
deallocate: rust_deallocate,
};
#[inline]
#[nstdapi]
pub unsafe fn nstd_alloc_allocate(layout: NSTDAllocLayout) -> NSTDAnyMut {
cfg_if! {
if #[cfg(any(
all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "csky",
target_arch = "loongarch64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "sparc64",
target_arch = "x86",
target_arch = "x86_64"
)
),
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos"),
any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "riscv32",
target_arch = "x86",
target_arch = "x86_64"
)
),
all(
target_os = "android",
any(
target_arch = "aarch64",
target_arch = "riscv64",
target_arch = "x86",
target_arch = "x86_64"
)
),
all(
any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos"
),
any(target_pointer_width = "32", target_arch = "aarch64", target_arch = "x86_64")
),
all(target_os = "freebsd", target_arch = "x86_64"),
target_env = "wasi",
target_os = "wasi",
target_os = "emscripten"
))] {
let align = nstd_core_alloc_layout_align(layout);
if align <= core::mem::align_of::<libc::max_align_t>() {
libc::malloc(nstd_core_alloc_layout_size(layout))
} else {
let size = nstd_core_alloc_layout_size(layout);
let min_align = core::mem::size_of::<NSTDAnyMut>();
let mut ptr = NSTD_NULL;
libc::posix_memalign(&mut ptr, align.max(min_align), size);
ptr
}
} else if #[cfg(any(unix, target_os = "teeos"))] {
let size = nstd_core_alloc_layout_size(layout);
let min_align = core::mem::size_of::<NSTDAnyMut>();
let align = nstd_core_alloc_layout_align(layout).max(min_align);
let mut ptr = NSTD_NULL;
libc::posix_memalign(&mut ptr, align, size);
ptr
} else if #[cfg(target_os = "solid_asp3")] {
use crate::NSTD_INT_MAX;
let mut size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
#[allow(clippy::arithmetic_side_effects)]
let off = size % align;
#[allow(clippy::arithmetic_side_effects)]
if off != 0 {
size = match size.checked_add(align - off) {
Some(size) if size <= NSTD_INT_MAX => size,
_ => return NSTD_NULL,
};
}
libc::aligned_alloc(align, size)
} else if #[cfg(windows)] {
nstd_os_windows_alloc_allocate(layout)
} else {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
return alloc::alloc::alloc(layout).cast();
}
NSTD_NULL
}
}
}
#[inline]
#[nstdapi]
pub unsafe fn nstd_alloc_allocate_zeroed(layout: NSTDAllocLayout) -> NSTDAnyMut {
cfg_if! {
if #[cfg(any(
unix,
any(target_env = "wasi", target_os = "wasi"),
target_os = "solid_asp3",
target_os = "teeos"
))] {
use crate::core::mem::nstd_core_mem_zero;
let ptr = nstd_alloc_allocate(layout);
if !ptr.is_null() {
nstd_core_mem_zero(ptr.cast(), nstd_core_alloc_layout_size(layout));
}
ptr
} else if #[cfg(windows)] {
nstd_os_windows_alloc_allocate_zeroed(layout)
} else {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
return alloc::alloc::alloc_zeroed(layout).cast();
}
NSTD_NULL
}
}
}
#[inline]
#[nstdapi]
pub unsafe fn nstd_alloc_reallocate(
ptr: &mut NSTDAnyMut,
old_layout: NSTDAllocLayout,
new_layout: NSTDAllocLayout,
) -> NSTDAllocError {
if old_layout != new_layout {
let new_mem = nstd_alloc_allocate(new_layout);
if new_mem.is_null() {
return NSTDAllocError::NSTD_ALLOC_ERROR_OUT_OF_MEMORY;
}
let old_size = nstd_core_alloc_layout_size(old_layout);
let new_size = nstd_core_alloc_layout_size(new_layout);
nstd_core_mem_copy(new_mem.cast(), (*ptr).cast(), old_size.min(new_size));
nstd_alloc_deallocate(*ptr, old_layout);
*ptr = new_mem;
}
NSTDAllocError::NSTD_ALLOC_ERROR_NONE
}
#[inline]
#[nstdapi]
#[allow(unused_variables)]
pub unsafe fn nstd_alloc_deallocate(ptr: NSTDAnyMut, layout: NSTDAllocLayout) -> NSTDAllocError {
cfg_if! {
if #[cfg(any(
unix,
any(target_env = "wasi", target_os = "wasi"),
target_os = "solid_asp3",
target_os = "teeos"
))] {
libc::free(ptr);
NSTDAllocError::NSTD_ALLOC_ERROR_NONE
} else if #[cfg(windows)] {
nstd_os_windows_alloc_deallocate(ptr);
NSTDAllocError::NSTD_ALLOC_ERROR_NONE
} else {
let size = nstd_core_alloc_layout_size(layout);
let align = nstd_core_alloc_layout_align(layout);
if let Ok(layout) = Layout::from_size_align(size, align) {
alloc::alloc::dealloc(ptr.cast(), layout);
return NSTDAllocError::NSTD_ALLOC_ERROR_NONE;
}
NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT
}
}
}