use std::{
any::{type_name, TypeId},
ffi::c_void,
fmt::Debug,
iter::Iterator,
marker::PhantomData,
mem::MaybeUninit,
sync::OnceLock,
};
use bones_utils::{default, fxhash::FxHasher, parking_lot::RwLock, HashMap};
use crate::{prelude::*, raw_fns::*};
use super::ResizableAlloc;
pub struct SchemaVec {
buffer: ResizableAlloc,
len: usize,
schema: &'static Schema,
}
impl std::fmt::Debug for SchemaVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SchemaVec")
.field("buffer", &"ResizableAlloc")
.field("len", &self.len)
.field("schema", &self.schema)
.finish()
}
}
unsafe impl Sync for SchemaVec {}
unsafe impl Send for SchemaVec {}
impl SchemaVec {
pub fn new(schema: &'static Schema) -> Self {
Self {
buffer: ResizableAlloc::new(schema.layout()),
len: 0,
schema,
}
}
fn grow(&mut self) {
let cap = self.buffer.capacity();
if cap == 0 {
self.buffer.resize(1).unwrap();
} else {
self.buffer.resize(cap * 2).unwrap();
}
}
unsafe fn push_raw(&mut self, item: *mut c_void) {
if self.len == self.buffer.capacity() {
self.grow();
}
unsafe {
self.buffer
.unchecked_idx(self.len)
.copy_from_nonoverlapping(item, self.buffer.layout().size());
}
self.len += 1;
}
pub fn try_push<T: HasSchema>(&mut self, mut item: T) -> Result<(), SchemaMismatchError> {
if self.schema != T::schema() {
return Err(SchemaMismatchError);
}
unsafe {
self.push_raw(&mut item as *mut T as *mut c_void);
std::mem::forget(item);
}
Ok(())
}
#[inline]
#[track_caller]
pub fn push<T: HasSchema>(&mut self, item: T) {
self.try_push(item).unwrap()
}
pub fn try_push_box(&mut self, mut item: SchemaBox) -> Result<(), SchemaMismatchError> {
if self.schema != item.schema() {
return Err(SchemaMismatchError);
}
unsafe {
self.push_raw(item.as_mut().as_ptr());
}
item.forget();
Ok(())
}
#[track_caller]
#[inline]
pub fn push_box(&mut self, item: SchemaBox) {
self.try_push_box(item).unwrap()
}
pub fn pop_box(&mut self) -> Option<SchemaBox> {
if self.len == 0 {
None
} else {
unsafe { self.raw_pop() }.map(|ptr| unsafe {
let mut b = SchemaBox::uninitialized(self.schema);
b.as_mut()
.as_ptr()
.copy_from_nonoverlapping(ptr, self.buffer.layout().size());
b
})
}
}
pub fn try_pop<T: HasSchema>(&mut self) -> Result<Option<T>, SchemaMismatchError> {
if self.schema != T::schema() {
Err(SchemaMismatchError)
} else {
let ret = unsafe { self.raw_pop() }.map(|ptr| {
let mut data = MaybeUninit::<T>::uninit();
unsafe {
(data.as_mut_ptr() as *mut c_void)
.copy_from_nonoverlapping(ptr, self.buffer.layout().size());
data.assume_init()
}
});
Ok(ret)
}
}
unsafe fn raw_pop(&mut self) -> Option<*mut c_void> {
if self.len == 0 {
None
} else {
self.len -= 1;
Some(unsafe { self.buffer.unchecked_idx(self.len) })
}
}
#[inline]
#[track_caller]
pub fn pop<T: HasSchema>(&mut self) -> Option<T> {
self.try_pop().unwrap()
}
pub fn try_get<T: HasSchema>(&self, idx: usize) -> Result<Option<&T>, SchemaMismatchError> {
self.get_ref(idx).map(|x| x.try_cast()).transpose()
}
#[inline]
#[track_caller]
pub fn get<T: HasSchema>(&self, idx: usize) -> Option<&T> {
self.try_get(idx).unwrap()
}
pub fn get_ref(&self, idx: usize) -> Option<SchemaRef<'_>> {
if idx >= self.len {
None
} else {
let ptr = unsafe { self.buffer.unchecked_idx(idx) };
unsafe { Some(SchemaRef::from_ptr_schema(ptr, self.schema)) }
}
}
pub fn try_get_mut<T: HasSchema>(
&mut self,
idx: usize,
) -> Result<Option<&mut T>, SchemaMismatchError> {
self.get_ref_mut(idx)
.map(|mut x| unsafe { x.try_cast_mut().map(|x| transmute_lifetime(x)) })
.transpose()
}
#[inline]
#[track_caller]
pub fn get_mut<T: HasSchema>(&mut self, idx: usize) -> Option<&mut T> {
self.try_get_mut(idx).unwrap()
}
pub fn get_ref_mut(&mut self, idx: usize) -> Option<SchemaRefMut<'_>> {
if idx >= self.len {
None
} else {
let ptr = unsafe { self.buffer.unchecked_idx(idx) };
unsafe { Some(SchemaRefMut::from_ptr_schema(ptr, self.schema)) }
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
#[inline]
pub fn schema(&self) -> &'static Schema {
self.schema
}
pub fn iter(&self) -> SchemaVecIter {
SchemaVecIter { vec: self, idx: 0 }
}
pub fn iter_mut(&mut self) -> SchemaVecIterMut {
SchemaVecIterMut { vec: self, idx: 0 }
}
#[track_caller]
pub fn into_svec<T: HasSchema>(self) -> SVec<T> {
self.try_into_svec().unwrap()
}
pub fn try_into_svec<T: HasSchema>(self) -> Result<SVec<T>, SchemaMismatchError> {
if T::schema() == self.schema {
Ok(SVec {
vec: self,
_phantom: PhantomData,
})
} else {
Err(SchemaMismatchError)
}
}
#[track_caller]
pub fn hash(&self) -> u64 {
use std::hash::{Hash, Hasher};
let Some(hash_fn) = &self.schema.hash_fn else {
panic!("Schema doesn't specify a hash_fn");
};
let mut hasher = FxHasher::default();
for item_ptr in self.buffer.iter() {
let item_hash = unsafe { (hash_fn.get())(item_ptr) };
item_hash.hash(&mut hasher);
}
hasher.finish()
}
pub unsafe fn raw_hash(ptr: *const c_void) -> u64 {
let this = unsafe { &*(ptr as *const Self) };
this.hash()
}
pub unsafe fn raw_eq(a: *const c_void, b: *const c_void) -> bool {
let a = &*(a as *const Self);
let b = &*(b as *const Self);
a.eq(b)
}
}
impl<T: HasSchema> FromIterator<T> for SVec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut this = Self::default();
for item in iter {
this.push(item);
}
this
}
}
impl<'a> IntoIterator for &'a SchemaVec {
type Item = SchemaRef<'a>;
type IntoIter = SchemaVecIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut SchemaVec {
type Item = SchemaRefMut<'a>;
type IntoIter = SchemaVecIterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct SchemaVecIter<'a> {
vec: &'a SchemaVec,
idx: usize,
}
impl<'a> Iterator for SchemaVecIter<'a> {
type Item = SchemaRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
let item = self.vec.get_ref(self.idx);
if item.is_some() {
self.idx += 1;
}
item
}
}
pub struct SchemaVecIterMut<'a> {
vec: &'a mut SchemaVec,
idx: usize,
}
impl<'a> Iterator for SchemaVecIterMut<'a> {
type Item = SchemaRefMut<'a>;
fn next(&mut self) -> Option<Self::Item> {
let item = self
.vec
.get_ref_mut(self.idx)
.map(|x| unsafe { SchemaRefMut::from_ptr_schema(x.as_ptr(), x.schema()) });
if item.is_some() {
self.idx += 1;
}
item
}
}
impl Eq for SchemaVec {}
impl PartialEq for SchemaVec {
#[track_caller]
fn eq(&self, other: &Self) -> bool {
if self.schema != other.schema {
panic!("Cannot compare two `SchemaVec`s with different schemas.");
}
let Some(eq_fn) = &self.schema.eq_fn else {
panic!("Schema doesn't have an eq_fn");
};
for i in 0..self.len {
unsafe {
let a = self.buffer.unchecked_idx(i);
let b = self.buffer.unchecked_idx(i);
if !(eq_fn.get())(a, b) {
return false;
}
}
}
true
}
}
impl Clone for SchemaVec {
fn clone(&self) -> Self {
let Some(clone_fn) = &self.schema.clone_fn else {
panic!("This type cannot be cloned");
};
let mut buffer_clone = ResizableAlloc::new(self.schema.layout());
buffer_clone.resize(self.len).unwrap();
for i in 0..self.len {
unsafe {
let item = self.buffer.unchecked_idx(i);
(clone_fn.get())(item, buffer_clone.unchecked_idx(i));
}
}
SchemaVec {
buffer: buffer_clone,
len: self.len,
schema: self.schema,
}
}
}
impl Drop for SchemaVec {
fn drop(&mut self) {
for _ in 0..self.len {
drop(self.pop_box().unwrap());
}
}
}
#[repr(transparent)]
#[derive(Eq, PartialEq)]
pub struct SVec<T: HasSchema> {
vec: SchemaVec,
_phantom: PhantomData<T>,
}
impl<T: HasSchema + Debug> std::fmt::Debug for SVec<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut l = f.debug_list();
for item in self.iter() {
l.entry(item);
}
l.finish()
}
}
impl<T: HasSchema> SVec<T> {
pub fn new() -> Self {
Self {
vec: SchemaVec::new(T::schema()),
_phantom: PhantomData,
}
}
pub fn push(&mut self, mut item: T) {
unsafe {
self.vec.push_raw(&mut item as *mut T as *mut c_void);
}
std::mem::forget(item);
}
pub fn pop(&mut self) -> Option<T> {
unsafe {
self.vec.raw_pop().map(|ptr| {
let mut ret = MaybeUninit::<T>::uninit();
ret.as_mut_ptr()
.copy_from_nonoverlapping(ptr as *mut T, self.vec.schema.layout().size());
ret.assume_init()
})
}
}
pub fn get(&self, idx: usize) -> Option<&T> {
self.vec
.get_ref(idx)
.map(|x| unsafe { x.cast_into_unchecked() })
}
pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
self.vec
.get_ref_mut(idx)
.map(|x| unsafe { x.cast_into_mut_unchecked() })
}
pub fn iter(&self) -> SVecIter<T> {
SVecIter {
vec: self,
idx: 0,
end: self.len() as isize - 1,
}
}
pub fn iter_mut(&mut self) -> SVecIterMut<T> {
SVecIterMut {
idx: 0,
end: self.len() as isize - 1,
vec: self,
}
}
#[inline]
pub fn len(&self) -> usize {
self.vec.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.vec.capacity()
}
#[inline]
pub fn into_schema_vec(self) -> SchemaVec {
self.vec
}
pub fn hash(&self) -> u64 {
self.vec.hash()
}
}
impl<T: HasSchema> std::ops::Index<usize> for SVec<T> {
type Output = T;
#[track_caller]
fn index(&self, idx: usize) -> &Self::Output {
self.get(idx).unwrap()
}
}
impl<T: HasSchema> std::ops::IndexMut<usize> for SVec<T> {
#[track_caller]
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
self.get_mut(idx).unwrap()
}
}
impl<T: HasSchema> std::ops::Deref for SVec<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { std::slice::from_raw_parts(self.vec.buffer.as_ptr() as *const T, self.len()) }
}
}
impl<T: HasSchema> std::ops::DerefMut for SVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::slice::from_raw_parts_mut(self.vec.buffer.as_ptr() as *mut T, self.len()) }
}
}
unsafe impl<T: HasSchema> HasSchema for SVec<T> {
fn schema() -> &'static Schema {
static S: OnceLock<RwLock<HashMap<TypeId, &'static Schema>>> = OnceLock::new();
let schema = {
S.get_or_init(default)
.read()
.get(&TypeId::of::<Self>())
.copied()
};
schema.unwrap_or_else(|| {
let schema = SCHEMA_REGISTRY.register(SchemaData {
name: type_name::<Self>().into(),
full_name: format!("{}::{}", module_path!(), type_name::<Self>()).into(),
kind: SchemaKind::Vec(T::schema()),
type_id: Some(TypeId::of::<Self>()),
clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
default_fn: Some(<Self as RawDefault>::raw_default_cb()),
hash_fn: Some(unsafe {
Unsafe::new(Box::leak(Box::new(|a| SchemaVec::raw_hash(a))))
}),
eq_fn: Some(unsafe {
Unsafe::new(Box::leak(Box::new(|a, b| SchemaVec::raw_eq(a, b))))
}),
type_data: Default::default(),
});
S.get_or_init(default)
.write()
.insert(TypeId::of::<Self>(), schema);
schema
})
}
}
impl<T: HasSchema> Default for SVec<T> {
fn default() -> Self {
Self {
vec: SchemaVec::new(T::schema()),
_phantom: Default::default(),
}
}
}
impl<T: HasSchema> Clone for SVec<T> {
fn clone(&self) -> Self {
Self {
vec: self.vec.clone(),
_phantom: self._phantom,
}
}
}
impl<'a, T: HasSchema> IntoIterator for &'a SVec<T> {
type Item = &'a T;
type IntoIter = SVecIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: HasSchema> IntoIterator for &'a mut SVec<T> {
type Item = &'a mut T;
type IntoIter = SVecIterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct SVecIter<'a, T: HasSchema> {
vec: &'a SVec<T>,
idx: usize,
end: isize,
}
impl<'a, T: HasSchema> Iterator for SVecIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.end < 0 {
return None;
}
let item = (self.idx <= self.end as usize).then(|| self.vec.get(self.idx).unwrap());
if item.is_some() {
self.idx += 1;
}
item
}
}
impl<'a, T: HasSchema> DoubleEndedIterator for SVecIter<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.end < 0 {
return None;
}
let item =
(self.end as usize >= self.idx).then(|| self.vec.get(self.end as usize).unwrap());
if item.is_some() {
self.end -= 1;
}
item
}
}
pub struct SVecIterMut<'a, T: HasSchema> {
vec: &'a mut SVec<T>,
idx: usize,
end: isize,
}
impl<'a, T: HasSchema> Iterator for SVecIterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if self.end < 0 {
return None;
}
let item = (self.idx <= self.end as usize).then(|| self.vec.get_mut(self.idx).unwrap());
if item.is_some() {
self.idx += 1;
}
item.map(|x| unsafe { transmute_lifetime(x) })
}
}
impl<'a, T: HasSchema> DoubleEndedIterator for SVecIterMut<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.end < 0 {
return None;
}
let item =
(self.end as usize >= self.idx).then(|| self.vec.get_mut(self.end as usize).unwrap());
if item.is_some() {
self.end -= 1;
}
item.map(|x| unsafe { transmute_lifetime(x) })
}
}
unsafe fn transmute_lifetime<'b, T>(v: &mut T) -> &'b mut T {
std::mem::transmute(v)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn double_ended() {
let mut v = [1, 2, 3, 4, 5, 6].into_iter().collect::<SVec<_>>();
let mut iter = v.iter();
assert_eq!(iter.next_back(), Some(&6));
assert_eq!(iter.next_back(), Some(&5));
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next_back(), Some(&4));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
let mut iter = v.iter_mut();
assert_eq!(iter.next_back(), Some(&mut 6));
assert_eq!(iter.next_back(), Some(&mut 5));
assert_eq!(iter.next(), Some(&mut 1));
assert_eq!(iter.next(), Some(&mut 2));
assert_eq!(iter.next_back(), Some(&mut 4));
assert_eq!(iter.next(), Some(&mut 3));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
let v = [].into_iter().collect::<SVec<u8>>();
let mut iter = v.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
let mut iter = v.iter();
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
}
}