use core::{mem, ptr::NonNull, sync::atomic::Ordering};
use rarena_allocator::Allocator as ArenaAllocator;
use crate::Header;
use super::{
allocator::{Allocator, AllocatorExt, Link, Meta, Node, NodePointer, Sealed as AllocatorSealed},
error::Error,
options::Options,
types::Height,
};
pub trait Constructable: Sized {
type Allocator: Allocator;
type Comparator;
fn allocator(&self) -> &Self::Allocator;
fn allocator_mut(&mut self) -> &mut Self::Allocator;
fn magic_version(&self) -> u16;
#[inline]
fn version(&self) -> u16 {
ArenaAllocator::magic_version(self.allocator().arena())
}
fn len(&self) -> usize;
fn height(&self) -> u8;
fn random_height(&self) -> Height;
fn header(&self) -> Option<&Header>;
fn construct(
arena: Self::Allocator,
meta: NonNull<<Self::Allocator as AllocatorSealed>::Meta>,
head: <<Self::Allocator as AllocatorSealed>::Node as Node>::Pointer,
tail: <<Self::Allocator as AllocatorSealed>::Node as Node>::Pointer,
header: Option<Header>,
cmp: Self::Comparator,
) -> Self;
}
pub trait List: Sized + From<Self::Constructable> {
type Constructable: Constructable;
fn as_ref(&self) -> &Self::Constructable;
fn as_mut(&mut self) -> &mut Self::Constructable;
fn meta(&self) -> &<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Meta;
fn construct(
arena: <<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Allocator,
opts: Options,
exist: bool,
cmp: <Self::Constructable as Constructable>::Comparator,
) -> Result<Self, Error> {
use std::boxed::Box;
let arena =
<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::new(arena, opts);
let opts = arena.options();
let max_height: u8 = opts.max_height().into();
if arena.read_only() || exist {
let header = arena.calculate_header(max_height)?;
let (meta, head, tail) = arena.get_pointers(header);
return Ok(Self::from(
<Self::Constructable as Constructable>::construct(
arena,
meta,
head,
tail,
Some(header),
cmp,
),
));
}
let (header_offset, meta) = if AllocatorSealed::unify(&arena) {
arena
.allocate_header(opts.magic_version())
.map(|(header_offset, meta)| (Some(header_offset as u32), meta))?
} else {
unsafe {
(None, NonNull::new_unchecked(Box::into_raw(Box::new(
<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Meta as Meta>::new(opts.magic_version()),
))))
}
};
let head = arena.allocate_full_node(max_height)?;
let tail = arena.allocate_full_node(max_height)?;
let head_offset = head.offset();
let tail_offset = tail.offset();
unsafe {
for i in 0..(max_height as usize) {
let head_link = head.tower(&arena, i);
let tail_link = tail.tower(&arena, i);
head_link.store_next_offset(tail.offset(), Ordering::Relaxed);
tail_link.store_prev_offset(head.offset(), Ordering::Relaxed);
}
}
let header =
header_offset.map(|meta_offset| Header::new(meta_offset, head_offset, tail_offset));
Ok(Self::from(
<Self::Constructable as Constructable>::construct(arena, meta, head, tail, header, cmp),
))
}
unsafe fn try_open_from_allocator<L: List>(
arena: <L::Constructable as Constructable>::Allocator,
cmp: <L::Constructable as Constructable>::Comparator,
header: Header,
) -> Result<L, Error> {
let (meta, head, tail) = arena.get_pointers(header);
Ok(L::from(<L::Constructable as Constructable>::construct(
arena,
meta,
head,
tail,
Some(header),
cmp,
)))
}
fn try_create_from_allocator<L: List>(
arena: <L::Constructable as Constructable>::Allocator,
cmp: <L::Constructable as Constructable>::Comparator,
) -> Result<L, Error> {
use std::boxed::Box;
let opts = arena.options();
let max_height: u8 = opts.max_height().into();
if arena.read_only() {
return Err(Error::read_only());
}
let (header_offset, meta) = if AllocatorSealed::unify(&arena) {
arena
.allocate_header(opts.magic_version())
.map(|(header_offset, meta)| (Some(header_offset as u32), meta))?
} else {
unsafe {
(None, NonNull::new_unchecked(Box::into_raw(Box::new(
<<<L::Constructable as Constructable>::Allocator as AllocatorSealed>::Meta as Meta>::new(opts.magic_version()),
))))
}
};
let head = arena.allocate_full_node(max_height)?;
let tail = arena.allocate_full_node(max_height)?;
let head_offset = NodePointer::offset(&head);
let tail_offset = NodePointer::offset(&tail);
unsafe {
for i in 0..(max_height as usize) {
let head_link = head.tower(&arena, i);
let tail_link = tail.tower(&arena, i);
head_link.store_next_offset(tail_offset, Ordering::Relaxed);
tail_link.store_prev_offset(head_offset, Ordering::Relaxed);
}
}
Ok(L::from(<L::Constructable as Constructable>::construct(
arena,
meta,
head,
tail,
header_offset.map(|offset| Header::new(offset, head_offset, tail_offset)),
cmp,
)))
}
}
pub trait Arena: List {
#[inline]
fn reserved_bytes(&self) -> usize {
self.as_ref().allocator().reserved_bytes()
}
#[inline]
fn reserved_slice(&self) -> &[u8] {
self.as_ref().allocator().reserved_slice()
}
unsafe fn clear(&mut self) -> Result<(), Error> {
self.allocator_mut().clear().map_err(Into::into)
}
#[allow(clippy::mut_from_ref)]
#[inline]
unsafe fn reserved_slice_mut(&self) -> &mut [u8] {
self.as_ref().allocator().reserved_slice_mut()
}
#[cfg(all(feature = "memmap", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "memmap", not(target_family = "wasm")))))]
#[inline]
fn path(
&self,
) -> Option<&<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Allocator as ArenaAllocator>::Path>{
self.as_ref().allocator().path()
}
#[cfg(all(feature = "memmap", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "memmap", not(target_family = "wasm")))))]
#[inline]
fn remove_on_drop(&self, val: bool) {
self.as_ref().allocator().remove_on_drop(val)
}
#[inline]
fn magic_version(&self) -> u16 {
self.as_ref().magic_version()
}
#[inline]
fn allocated(&self) -> usize {
self.as_ref().allocator().allocated()
}
#[inline]
fn capacity(&self) -> usize {
self.as_ref().allocator().capacity()
}
#[inline]
fn remaining(&self) -> usize {
self.as_ref().allocator().remaining()
}
#[inline]
fn refs(&self) -> usize {
self.as_ref().allocator().refs()
}
#[inline]
fn discarded(&self) -> u32 {
self.as_ref().allocator().discarded()
}
#[inline]
fn unify(&self) -> bool {
self.as_ref().allocator().unify()
}
#[inline]
fn allocator(&self) -> &<Self::Constructable as Constructable>::Allocator {
self.as_ref().allocator()
}
#[inline]
fn allocator_mut(&mut self) -> &mut <Self::Constructable as Constructable>::Allocator {
self.as_mut().allocator_mut()
}
#[inline]
fn estimated_node_size(height: Height, key_size: usize, value_size: usize) -> usize {
let height: usize = height.into();
7 + mem::size_of::<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Node>()
+ mem::size_of::<<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Node as Node>::Link>() * height
+ key_size
+ value_size
}
#[inline]
fn full_node_size(max_height: Height) -> usize {
let max_height: usize = max_height.into();
mem::size_of::<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Node>()
+ mem::size_of::<<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Node as Node>::Link>() * max_height
}
#[inline]
fn meta_size() -> usize {
mem::size_of::<<<Self::Constructable as Constructable>::Allocator as AllocatorSealed>::Meta>()
}
#[cfg(all(feature = "memmap", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "memmap", not(target_family = "wasm")))))]
#[inline]
fn flush(&self) -> std::io::Result<()> {
self.as_ref().allocator().flush()
}
#[cfg(all(feature = "memmap", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "memmap", not(target_family = "wasm")))))]
#[inline]
fn flush_async(&self) -> std::io::Result<()> {
self.as_ref().allocator().flush_async()
}
}
impl<T> Arena for T where T: List {}