use super::sys::mem::arena as sys_arena;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::{align_of_val, size_of_val, MaybeUninit};
use core::ptr;
use core::slice;
pub type RawArena = sys_arena::RawArena;
#[derive(Debug)]
pub struct Arena {
raw: RawArena,
_not_sync: PhantomData<UnsafeCell<()>>,
}
unsafe impl Send for Arena {}
impl Arena {
#[inline]
pub fn new() -> Self {
#[inline(never)]
#[cold]
fn arena_new_failed() -> ! {
panic!("Could not create a new UPB arena");
}
unsafe {
let Some(raw) = sys_arena::upb_Arena_New() else { arena_new_failed() };
Self { raw, _not_sync: PhantomData }
}
}
pub unsafe fn from_raw(raw_arena: RawArena) -> Self {
Arena { raw: raw_arena, _not_sync: PhantomData }
}
#[inline]
pub fn raw(&self) -> RawArena {
self.raw
}
#[allow(clippy::mut_from_ref)]
#[inline]
pub unsafe fn alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]> {
debug_assert!(align <= sys_arena::UPB_MALLOC_ALIGN);
let ptr = unsafe { sys_arena::upb_Arena_Malloc(self.raw, size) };
if ptr.is_null() {
None
} else {
Some(unsafe { slice::from_raw_parts_mut(ptr.cast(), size) })
}
}
#[allow(clippy::mut_from_ref)]
#[inline]
pub fn checked_alloc(&self, size: usize, align: usize) -> Option<&mut [MaybeUninit<u8>]> {
assert!(align <= sys_arena::UPB_MALLOC_ALIGN);
unsafe { self.alloc(size, align) }
}
pub fn copy_in<'a, T: Copy>(&'a self, data: &T) -> Option<&'a T> {
let size = size_of_val(data);
let align = align_of_val(data);
self.checked_alloc(size, align).map(|alloc| {
unsafe {
let alloc = alloc.as_mut_ptr().cast::<MaybeUninit<T>>();
&*(*alloc).write(*data)
}
})
}
pub fn copy_str_in<'a>(&'a self, s: &str) -> Option<&'a str> {
self.copy_slice_in(s.as_bytes()).map(|copied_bytes| {
unsafe { core::str::from_utf8_unchecked(copied_bytes) }
})
}
pub fn copy_slice_in<'a, T: Copy>(&'a self, data: &[T]) -> Option<&'a [T]> {
let size = size_of_val(data);
let align = align_of_val(data);
self.checked_alloc(size, align).map(|alloc| {
let alloc: *mut T = alloc.as_mut_ptr().cast();
unsafe {
ptr::copy_nonoverlapping(data.as_ptr(), alloc, data.len());
slice::from_raw_parts(alloc, data.len())
}
})
}
pub fn fuse(&self, other: &Arena) {
let success = unsafe { sys_arena::upb_Arena_Fuse(self.raw(), other.raw()) };
if !success {
panic!("Could not fuse two UPB arenas.");
}
}
}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
impl Drop for Arena {
#[inline]
fn drop(&mut self) {
unsafe {
sys_arena::upb_Arena_Free(self.raw);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use googletest::gtest;
#[gtest]
fn test_arena_new_and_free() {
let arena = Arena::new();
drop(arena);
}
}