#[cfg(target_os = "macos")]
pub unsafe fn reallocarray(
optr: *mut core::ffi::c_void,
nmemb: usize,
size: usize,
) -> *mut core::ffi::c_void {
const MUL_NO_OVERFLOW: usize = 1usize << (size_of::<usize>() * 4);
unsafe {
if (nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW)
&& nmemb > 0
&& usize::MAX / nmemb < size
{
crate::errno!() = libc::ENOMEM;
return core::ptr::null_mut();
}
libc::realloc(optr, size * nmemb)
}
}
#[cfg(target_os = "linux")]
pub(crate) use libc::reallocarray;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reallocarray_alloc_usable() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 4, 4).cast::<u8>();
assert!(!p.is_null());
for i in 0..16usize {
*p.add(i) = i as u8;
}
for i in 0..16usize {
assert_eq!(*p.add(i), i as u8);
}
libc::free(p.cast());
}
}
#[test]
fn test_reallocarray_overflow_returns_null() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), usize::MAX, 2);
assert!(p.is_null());
}
}
#[test]
fn test_reallocarray_grow_preserves_prefix() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 4, 1).cast::<u8>();
assert!(!p.is_null());
for i in 0..4usize {
*p.add(i) = i as u8;
}
let q = reallocarray(p.cast(), 8, 1).cast::<u8>();
assert!(!q.is_null());
for i in 0..4usize {
assert_eq!(*q.add(i), i as u8, "grown byte {i} not preserved");
}
for i in 0..8usize {
*q.add(i) = (i * 2) as u8;
}
libc::free(q.cast());
}
}
#[test]
fn test_reallocarray_shrink_keeps_prefix() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 16, 1).cast::<u8>();
assert!(!p.is_null());
for i in 0..16usize {
*p.add(i) = (0x40 + i) as u8;
}
let q = reallocarray(p.cast(), 4, 1).cast::<u8>();
assert!(!q.is_null());
for i in 0..4usize {
assert_eq!(*q.add(i), (0x40 + i) as u8, "retained byte {i} wrong");
}
libc::free(q.cast());
}
}
#[test]
fn test_reallocarray_size_is_nmemb_times_size() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 3, 4).cast::<u8>();
assert!(!p.is_null());
for i in 0..12usize {
*p.add(i) = i as u8;
}
for i in 0..12usize {
assert_eq!(*p.add(i), i as u8);
}
libc::free(p.cast());
}
}
#[test]
fn test_reallocarray_below_threshold_allocates() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 1000, 1000).cast::<u8>();
assert!(!p.is_null());
*p = 0xAB;
*p.add(999_999) = 0xCD;
assert_eq!(*p, 0xAB);
assert_eq!(*p.add(999_999), 0xCD);
libc::free(p.cast());
}
}
#[test]
fn test_reallocarray_size_operand_overflow() {
unsafe {
let p = reallocarray(core::ptr::null_mut(), 2, usize::MAX);
assert!(p.is_null());
}
}
}