use crate::collections::cache::CacheTable;
use crate::ArenaString;
use crate::storage::{ArenaStorage, ArrayLayout, Capacity, LayoutSpec};
use core::alloc::Layout;
use core::cmp::Ordering;
use core::fmt::{self, Debug, Display, Formatter, Pointer, Write};
use core::hash::{BuildHasher, Hash, Hasher};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut, Range};
use core::ptr::{null_mut, slice_from_raw_parts_mut, NonNull};
use core::slice::from_raw_parts_mut;
pub struct Box<'src, T: ?Sized> {
ptr: NonNull<T>,
val: PhantomData<T>, src: PhantomData<&'src ()>, }
impl<'src, T: ?Sized> Box<'src, T> {
pub(crate) unsafe fn new_unchecked(ptr: *mut T) -> Self {
Box { ptr: NonNull::new_unchecked(ptr), val: PhantomData, src: PhantomData }
}
}
impl<'src, T: Sized> Box<'src, MaybeUninit<T>> {
#[inline]
pub unsafe fn assume_init(mut self) -> Box<'src, T> {
let ptr = self.as_mut_ptr();
core::mem::forget(self);
Box::new_unchecked(ptr)
}
#[inline]
pub fn init(mut self, x: T) -> Box<'src, T> {
let ptr = self.as_mut_ptr();
unsafe {
ptr.write(x);
self.assume_init()
}
}
}
impl<'src, T: Sized> Box<'src, [MaybeUninit<T>]> {
#[inline]
pub unsafe fn assume_init(mut self) -> Box<'src, [T]> {
let ptr = self.as_mut_ptr().cast::<T>();
let len = self.len();
core::mem::forget(self);
Box::new_unchecked(slice_from_raw_parts_mut(ptr, len))
}
#[inline]
pub fn init_with<F: Fn(usize) -> T>(mut self, f: F) -> Box<'src, [T]> {
unsafe {
for i in 0..self.len() {
self[i].as_mut_ptr().write(f(i));
}
self.assume_init()
}
}
}
impl<T: ?Sized> Deref for Box<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> DerefMut for Box<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized> AsRef<T> for Box<'_, T> {
fn as_ref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> AsMut<T> for Box<'_, T> {
fn as_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized> Drop for Box<'_, T> {
fn drop(&mut self) {
unsafe { self.ptr.as_ptr().drop_in_place(); }
}
}
impl<T: Debug + ?Sized> Debug for Box<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: Display + ?Sized> Display for Box<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T: ?Sized> Pointer for Box<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Pointer::fmt(&self.ptr, f)
}
}
impl<T: ?Sized + PartialEq> PartialEq for Box<'_, T> {
#[inline]
fn eq(&self, other: &Box<'_, T>) -> bool {
PartialEq::eq(&**self, &**other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for Box<'_, T> {
#[inline]
fn partial_cmp(&self, other: &Box<'_, T>) -> Option<Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
#[inline]
fn lt(&self, other: &Box<'_, T>) -> bool {
PartialOrd::lt(&**self, &**other)
}
#[inline]
fn le(&self, other: &Box<'_, T>) -> bool {
PartialOrd::le(&**self, &**other)
}
#[inline]
fn ge(&self, other: &Box<'_, T>) -> bool {
PartialOrd::ge(&**self, &**other)
}
#[inline]
fn gt(&self, other: &Box<'_, T>) -> bool {
PartialOrd::gt(&**self, &**other)
}
}
impl<T: ?Sized + Ord> Ord for Box<'_, T> {
#[inline]
fn cmp(&self, other: &Box<T>) -> Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: ?Sized + Eq> Eq for Box<'_, T> {}
impl<T: ?Sized + Hash> Hash for Box<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: ?Sized + Hasher> Hasher for Box<'_, T> {
fn finish(&self) -> u64 {
(**self).finish()
}
fn write(&mut self, bytes: &[u8]) {
(**self).write(bytes);
}
fn write_u8(&mut self, i: u8) {
(**self).write_u8(i);
}
fn write_u16(&mut self, i: u16) {
(**self).write_u16(i);
}
fn write_u32(&mut self, i: u32) {
(**self).write_u32(i);
}
fn write_u64(&mut self, i: u64) {
(**self).write_u64(i);
}
fn write_u128(&mut self, i: u128) {
(**self).write_u128(i);
}
fn write_usize(&mut self, i: usize) {
(**self).write_usize(i);
}
fn write_i8(&mut self, i: i8) {
(**self).write_i8(i);
}
fn write_i16(&mut self, i: i16) {
(**self).write_i16(i);
}
fn write_i32(&mut self, i: i32) {
(**self).write_i32(i);
}
fn write_i64(&mut self, i: i64) {
(**self).write_i64(i);
}
fn write_i128(&mut self, i: i128) {
(**self).write_i128(i);
}
fn write_isize(&mut self, i: isize) {
(**self).write_isize(i);
}
}
impl<I: Iterator + ?Sized> Iterator for Box<'_, I> {
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
(**self).next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(**self).size_hint()
}
fn nth(&mut self, n: usize) -> Option<I::Item> {
(**self).nth(n)
}
}
#[cfg(feature = "profile")]
#[derive(Copy, Clone)]
struct ProfileMetaData {
initial_cursor_pos: usize,
peak_cursor_pos: usize,
allocation_count: usize,
failed_allocations: usize,
}
#[cfg(feature = "profile")]
#[cfg_attr(docs_rs, doc(cfg(feature = "profile")))]
#[derive(Copy, Clone, Debug)]
pub struct UtilizationProfile {
pub peak_utilization: usize,
pub allocation_count: usize,
pub failed_allocations: usize,
}
pub struct Arena<'src> {
cursor: *mut MaybeUninit<u8>,
end: *mut MaybeUninit<u8>,
src: PhantomData<&'src mut ()>, }
fn align_offset(ptr: *mut MaybeUninit<u8>, layout: &Layout) -> usize {
let p = ptr as usize;
let align = layout.align();
let a = align.wrapping_sub(1);
let result = (p.wrapping_add(a) & 0_usize.wrapping_sub(align)).wrapping_sub(p);
debug_assert_eq!(result, ptr.align_offset(align));
result
}
impl<'src> From<&'src mut [MaybeUninit<u8>]> for Arena<'src> {
#[inline]
fn from(buf: &mut [MaybeUninit<u8>]) -> Self {
let Range { start, end } = buf.as_mut_ptr_range();
unsafe { Arena::from_raw_pointers(start, end) }
}
}
#[cfg(feature = "alloc")]
impl Arena<'static> {
#[inline]
pub fn try_static_with_capacity(capacity: usize) -> Option<Self> {
unsafe {
let ptr = alloc::alloc::alloc(Layout::from_size_align(capacity, 8).ok()?);
if ptr.is_null() { return None; }
Some(Self::from_raw_pointers(ptr.cast(), ptr.add(capacity).cast()))
}
}
#[inline]
#[track_caller]
pub fn static_with_capacity(capacity: usize) -> Self {
Self::try_static_with_capacity(capacity).expect("unexpected allocation failure in `Arena::static_with_capacity`")
}
}
impl Debug for Arena<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
core::write!(f, "Arena({:p}..{:p})", self.cursor, self.end)
}
}
impl<'src> Arena<'src> {
#[inline]
unsafe fn from_raw_pointers(start: *mut MaybeUninit<u8>, end: *mut MaybeUninit<u8>) -> Self {
#[cfg(feature = "profile")]
#[allow(clippy::cast_ptr_alignment)]
let end = {
use core::mem::size_of;
let layout = Layout::new::<ProfileMetaData>();
let offset = align_offset(end, &layout);
assert!(offset < size_of::<usize>());
let end_of_meta = end
.wrapping_add(offset)
.wrapping_sub(size_of::<usize>());
let new_end = end_of_meta.wrapping_sub(layout.size());
assert!(start <= new_end);
assert!(new_end < end_of_meta);
assert!(end_of_meta <= end);
debug_assert_eq!(align_offset(new_end, &layout), 0);
let meta = new_end.cast::<ProfileMetaData>();
meta.write(ProfileMetaData {
initial_cursor_pos: start as usize,
peak_cursor_pos: start as usize,
allocation_count: 0,
failed_allocations: 0,
});
new_end
};
Arena {
cursor: start,
end,
src: PhantomData,
}
}
#[inline]
pub fn bytes_remaining(&self) -> usize {
(self.end as usize) - (self.cursor as usize)
}
#[inline]
pub fn make_sub_arena(&mut self) -> Arena<'_> {
Arena {
cursor: self.cursor,
end: self.end,
src: PhantomData,
}
}
#[inline]
fn try_alloc_raw(&mut self, alloc_layout: &Layout) -> *mut MaybeUninit<u8> {
let align_offset = align_offset(self.cursor, alloc_layout);
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().allocation_count += 1;
}
if let Some(total_bytes) = align_offset.checked_add(alloc_layout.size()) {
if self.bytes_remaining() >= total_bytes {
let result = unsafe { self.cursor.add(align_offset) };
let new_cursor = unsafe { result.add(alloc_layout.size()) };
self.cursor = new_cursor;
#[cfg(feature = "profile")]
{
let meta = self.profile_meta_data_mut();
if meta.peak_cursor_pos < new_cursor as usize {
meta.peak_cursor_pos = new_cursor as usize;
}
}
return result;
}
}
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().failed_allocations += 1;
}
null_mut()
}
#[inline]
#[track_caller]
pub fn storage_with_capacity<R: LayoutSpec>(
&mut self,
capacity: usize,
) -> ArenaStorage<'src, R> {
self.try_storage_with_capacity(capacity)
.expect("unexpected allocation failure in `storage_with_capacity`")
}
pub fn try_storage_with_capacity<R: LayoutSpec>(
&mut self,
capacity: usize,
) -> Option<ArenaStorage<'src, R>> {
let layout = R::layout_with_capacity(capacity).ok()?;
let ptr = self.try_alloc_raw(&layout).cast::<u8>();
unsafe { ArenaStorage::from_raw_parts(ptr, capacity) }
}
#[inline]
#[track_caller]
pub fn alloc_default<T: Default + Sized>(&mut self) -> Box<'src, T> {
self.try_reserve()
.expect("unexpected allocation failure in `alloc_default`")
.init(T::default())
}
#[inline]
pub fn try_alloc_default<T: Default + Sized>(&mut self) -> Option<Box<'src, T>> {
self.try_reserve().map(|b| b.init(T::default()))
}
#[inline]
#[track_caller]
pub fn alloc<T: Sized>(&mut self, x: T) -> Box<'src, T> {
self.try_reserve()
.expect("unexpected allocation failure in `alloc`")
.init(x)
}
#[inline]
pub fn try_alloc<T: Sized>(&mut self, x: T) -> Option<Box<'src, T>> {
self.try_reserve().map(|b| b.init(x))
}
#[inline]
#[track_caller]
pub fn reserve<T: Sized>(&mut self) -> Box<'src, MaybeUninit<T>> {
self.try_reserve()
.expect("unexpected allocation failure in `reserve`")
}
#[inline]
pub fn try_reserve<T: Sized>(&mut self) -> Option<Box<'src, MaybeUninit<T>>> {
let ptr = self.try_alloc_raw(&Layout::new::<T>()).cast::<MaybeUninit<T>>();
if ptr.is_null() {
return None;
}
Some(unsafe { Box::new_unchecked(ptr) })
}
#[inline]
#[track_caller]
pub fn reserve_array<T: Sized>(&mut self, count: usize) -> Box<'src, [MaybeUninit<T>]> {
self.try_reserve_array(count)
.expect("unexpected allocation failure in `reserve_array`")
}
#[inline]
pub fn try_reserve_array<T: Sized>(
&mut self,
count: usize,
) -> Option<Box<'src, [MaybeUninit<T>]>> {
let ptr = self.try_array_raw(count)?;
Some(unsafe { Box::new_unchecked(ptr) })
}
#[inline]
#[track_caller]
pub fn array_default<T>(&mut self, count: usize) -> Box<'src, [T]>
where
T: Default + Sized,
{
self.try_reserve_array(count)
.expect("unexpected allocation failure in `array_default`")
.init_with(|_| T::default())
}
#[inline]
pub fn try_array_default<T>(&mut self, count: usize) -> Option<Box<'src, [T]>>
where
T: Default + Sized,
{
self.try_reserve_array(count)
.map(|b| b.init_with(|_| T::default()))
}
#[inline]
#[track_caller]
pub fn array<T>(&mut self, x: T, count: usize) -> Box<'src, [T]>
where
T: Copy + Sized,
{
self.try_reserve_array(count)
.expect("unexpected allocation failure in `array`")
.init_with(|_| x)
}
#[inline]
fn try_array_raw<T>(&mut self, count: usize) -> Option<*mut [MaybeUninit<T>]>
where
T: Sized,
{
let alloc_layout = match Layout::array::<T>(count) {
Ok(layout) => layout,
Err(_) => {
return None;
}
};
let ptr = self.try_alloc_raw(&alloc_layout).cast::<MaybeUninit<T>>();
if ptr.is_null() {
return None;
}
Some(slice_from_raw_parts_mut(ptr, count))
}
#[inline]
pub fn try_array<T>(&mut self, x: T, count: usize) -> Option<Box<'src, [T]>>
where
T: Copy + Sized,
{
self.try_reserve_array(count).map(|b| b.init_with(|_| x))
}
pub fn with_capacity<S, C>(&mut self, capacity: usize) -> C
where
C: From<ArenaStorage<'src, S>>,
S: LayoutSpec,
{
self.try_with_capacity(capacity).expect("unexpected allocation failure in `with_capacity`")
}
pub fn try_with_capacity<S, C>(&mut self, capacity: usize) -> Option<C>
where
C: From<ArenaStorage<'src, S>>,
S: LayoutSpec,
{
Some(C::from(self.try_storage_with_capacity(capacity)?))
}
#[track_caller]
pub fn string_from<I: Capacity, T: AsRef<str>>(&mut self, value: T) -> ArenaString<'src, I> {
self.try_string_from::<I, T>(value).expect("unexpected allocation failure in `string_from`")
}
pub fn try_string_from<I: Capacity, T: AsRef<str>>(&mut self, value: T) -> Option<ArenaString<'src, I>> {
let str = value.as_ref();
let mut result: ArenaString<'_, _> = self.try_with_capacity(str.len())?;
result.push_str(str);
Some(result)
}
#[track_caller]
pub fn string_with_capacity_from<I: Capacity, T: AsRef<str>>(&mut self, capacity: I, value: T) -> ArenaString<'src, I> {
self.try_string_with_capacity_from(capacity, value).expect("unexpected allocation failure in `string_with_capacity_from`")
}
pub fn try_string_with_capacity_from<I: Capacity, T: AsRef<str>>(&mut self, capacity: I, value: T) -> Option<ArenaString<'src, I>> {
if value.as_ref().len() > capacity.as_usize() {
return None;
}
let mut result: ArenaString<'_, _> = self.try_with_capacity(capacity.as_usize())?;
result.push_str(value.as_ref());
Some(result)
}
#[allow(clippy::type_complexity)]
pub fn try_cache_with_hasher<K: Eq + Hash, V, L: crate::collections::cache::CacheLine<K, V>, H: BuildHasher>(&mut self, capacity: usize, hash_builder: H) -> Option<CacheTable<K, V, ArenaStorage<'src, ArrayLayout<L>>, L, H>> {
let capacity = (capacity + L::CAPACITY - 1) / L::CAPACITY;
let storage = self.try_storage_with_capacity(capacity)?;
Some(CacheTable::from_storage_and_hasher(storage, hash_builder))
}
pub fn cache_with_hasher<K: Eq + Hash, V, L: crate::collections::cache::CacheLine<K, V>, H: BuildHasher>(&mut self, capacity: usize, hash_builder: H) -> CacheTable<K, V, ArenaStorage<'src, ArrayLayout<L>>, L, H> {
self.try_cache_with_hasher(capacity, hash_builder)
.expect("unexpected allocation failure in cache_with_hasher")
}
#[inline]
#[track_caller]
pub fn collect_slice<T, I>(&mut self, iter: I) -> Box<'src, [T]>
where
T: Sized,
I: IntoIterator<Item = T>,
{
self.try_collect_slice(iter)
.expect("unexpected allocation failure in `collect`")
}
pub fn try_collect_slice<T, I>(&mut self, iter: I) -> Option<Box<'src, [T]>>
where
T: Sized,
I: IntoIterator<Item = T>,
{
let alloc_layout = Layout::new::<T>();
let align_offset = align_offset(self.cursor, &alloc_layout);
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().allocation_count += 1;
}
let bytes_remaining = self.bytes_remaining();
if bytes_remaining < align_offset {
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().failed_allocations += 1;
}
return None;
}
let iter = iter.into_iter();
let (min_items, _) = iter.size_hint();
let item_capacity = (bytes_remaining - align_offset) / core::mem::size_of::<T>();
if item_capacity < min_items {
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().failed_allocations += 1;
}
return None;
}
let base = unsafe { self.cursor.add(align_offset).cast::<T>() };
let mut count = 0_usize;
let mut cursor = base;
for val in iter {
if count == item_capacity {
for i in 0..count {
unsafe {
base.add(i).drop_in_place();
}
}
#[cfg(feature = "profile")]
{
self.profile_meta_data_mut().failed_allocations += 1;
}
return None;
}
count += 1;
unsafe {
cursor.write(val);
cursor = cursor.add(1);
}
}
self.cursor = cursor.cast::<MaybeUninit<u8>>();
#[cfg(feature = "profile")]
{
let meta = self.profile_meta_data_mut();
if meta.peak_cursor_pos < cursor as usize {
meta.peak_cursor_pos = cursor as usize;
}
}
unsafe {
let slice = from_raw_parts_mut(base, count);
Some(Box::new_unchecked(slice))
}
}
#[track_caller]
pub fn collect_with_capacity<I, S, C>(&mut self, iter: I, capacity: usize) -> C
where
S: LayoutSpec,
C: From<ArenaStorage<'src, S>> + Extend<I::Item>,
I: Iterator,
{
self.try_collect_with_capacity(iter, capacity).expect("unexpected allocation failure in `collect_with_capacity`")
}
pub fn try_collect_with_capacity<I, S, C>(&mut self, iter: I, capacity: usize) -> Option<C>
where
S: LayoutSpec,
C: From<ArenaStorage<'src, S>> + Extend<I::Item>,
I: Iterator,
{
let storage = self.try_storage_with_capacity(capacity)?;
let mut result = C::from(storage);
result.extend(iter);
Some(result)
}
#[inline]
pub fn make_writer<'a>(&'a mut self) -> Writer<'a, 'src> {
Writer {
source: self,
len: 0,
}
}
}
#[cfg(feature = "profile")]
impl Arena<'_> {
#[inline]
#[allow(clippy::cast_ptr_alignment)]
pub fn utilization(&self) -> UtilizationProfile {
let layout = Layout::new::<ProfileMetaData>();
debug_assert_eq!(align_offset(self.end, &layout), 0);
let &ProfileMetaData {
initial_cursor_pos,
peak_cursor_pos,
allocation_count,
failed_allocations,
} = unsafe { &*self.end.cast::<ProfileMetaData>() };
UtilizationProfile {
peak_utilization: peak_cursor_pos - initial_cursor_pos,
allocation_count,
failed_allocations,
}
}
#[inline]
#[allow(clippy::cast_ptr_alignment)]
fn profile_meta_data_mut(&mut self) -> &mut ProfileMetaData {
let layout = Layout::new::<ProfileMetaData>();
debug_assert_eq!(align_offset(self.end, &layout), 0);
unsafe { &mut *self.end.cast::<ProfileMetaData>() }
}
}
pub struct Writer<'src, 'buf> {
source: &'src mut Arena<'buf>,
len: usize,
}
impl Write for Writer<'_, '_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes_remaining = self.source.bytes_remaining();
if s.len() > bytes_remaining {
#[cfg(feature = "profile")]
{
self.source.profile_meta_data_mut().allocation_count += 1;
self.source.profile_meta_data_mut().failed_allocations += 1;
}
return fmt::Result::Err(fmt::Error);
}
unsafe {
s.as_ptr()
.copy_to_nonoverlapping(self.source.cursor.cast::<u8>(), s.len());
}
self.source.cursor = unsafe { self.source.cursor.add(s.len()) };
self.len += s.len();
#[cfg(feature = "profile")]
{
let cursor = self.source.cursor as usize;
let meta = self.source.profile_meta_data_mut();
meta.allocation_count += 1;
if meta.peak_cursor_pos < cursor {
meta.peak_cursor_pos = cursor;
}
}
Ok(())
}
}
impl<'buf> From<Writer<'_, 'buf>> for Box<'buf, str> {
fn from(writer: Writer<'_, 'buf>) -> Self {
unsafe {
let ptr = writer.source.cursor.sub(writer.len).cast::<u8>();
let slice = from_raw_parts_mut(ptr, writer.len);
let str_ptr = NonNull::new_unchecked(slice).as_ptr() as *mut str;
Box::new_unchecked(str_ptr)
}
}
}
#[macro_export]
macro_rules! fmt {
($arena:expr, $($arg:tt)*) => {{
#[allow(unused_imports)]
use core::fmt::Write;
let mut writer = $arena.make_writer();
core::write!(writer, $($arg)*)
.ok()
.map(|_| $crate::arena::Box::<'_, str>::from(writer))
}}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn failed_collect_drops_taken_items() {
use crate::test_utils::*;
const ARENA_SIZE: usize = 321;
let mut backing_region = [MaybeUninit::uninit(); ARENA_SIZE];
let mut arena = Arena::from(&mut backing_region[..]);
let drop_count = DropCounter::new();
let mut taken_count = 0;
let result = arena.try_collect_slice((0..100).map(|_| {
taken_count += 1;
drop_count.new_droppable(())
}));
assert!(result.is_none());
assert_eq!(taken_count, drop_count.dropped());
let alloc_for_arena_size = arena.try_array_default::<u8>(ARENA_SIZE);
if cfg!(feature = "profile") {
assert!(alloc_for_arena_size.is_none());
} else {
assert!(alloc_for_arena_size.is_some());
}
}
#[test]
fn format_boxed_debug_struct() {
#[allow(dead_code)] #[derive(Debug)]
struct LinkedList<'a> {
val: i64,
next: Option<Box<'a, LinkedList<'a>>>,
}
let mut backing_region = [MaybeUninit::uninit(); 256];
let mut arena = Arena::from(&mut backing_region[..]);
let a = arena.alloc(LinkedList { val: 0, next: None });
let b = arena.alloc(LinkedList {
val: 1,
next: Some(a),
});
let output = fmt!(arena, "{:?}", b).unwrap();
let _c = arena.alloc(LinkedList {
val: 2,
next: Some(b),
});
assert_eq!(
output.as_ref(),
"LinkedList { val: 1, next: Some(LinkedList { val: 0, next: None }) }"
);
}
#[test]
fn debug_impl() {
let mut backing_region_a = [MaybeUninit::uninit(); 256];
let mut arena_a = Arena::from(&mut backing_region_a[..]);
let mut backing_region_b = [MaybeUninit::uninit(); 256];
let arena_b = Arena::from(&mut backing_region_b[..]);
let output = fmt!(arena_a, "{:?}", arena_b).unwrap();
assert_eq!(&output[..8], "Arena(0x");
let chars_per_ptr = (output.len() - 13) / 2;
assert_eq!(&output[8 + chars_per_ptr..12 + chars_per_ptr], "..0x");
assert_eq!(&output[12 + 2 * chars_per_ptr..], ")");
}
}