diff options
author | HampusM <hampus@hampusmat.com> | 2025-03-25 15:30:50 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-03-28 11:50:38 +0100 |
commit | 5a7da770478646ac85f14f49af77c7afc1377cec (patch) | |
tree | ac35d1bda8b59cfb410d06484a3d1f653c0120a4 /src/lib.rs | |
parent | 5f6da82e31d3e4e0096428899c5b4c67361c1bb3 (diff) |
feat: allow for dynamic fields
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 751 |
1 files changed, 324 insertions, 427 deletions
@@ -1,10 +1,55 @@ #![deny(clippy::all, clippy::pedantic)] -use std::alloc::{alloc, handle_alloc_error, realloc, Layout}; +use std::alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout}; +use std::any::Any; use std::cmp::max; -use std::marker::PhantomData; -use std::mem::{forget, needs_drop}; use std::ptr::NonNull; +pub struct OwnedAnyPtr +{ + ptr: *mut dyn Any, +} + +impl OwnedAnyPtr +{ + pub fn new<Value: Any>(value: Value) -> Self + { + Self::from_boxed(Box::new(value)) + } + + pub fn from_boxed<Value: Any>(boxed_value: Box<Value>) -> Self + { + Self { ptr: Box::into_raw(boxed_value) } + } + + pub fn as_ptr(&self) -> *const dyn Any + { + self.ptr + } + + pub fn size(&self) -> usize + { + size_of_val(unsafe { &*self.ptr }) + } + + pub fn alignment(&self) -> usize + { + align_of_val(unsafe { &*self.ptr }) + } +} + +impl Drop for OwnedAnyPtr +{ + fn drop(&mut self) + { + unsafe { + dealloc( + self.ptr.cast::<u8>(), + Layout::from_size_align(self.size(), self.alignment()).unwrap(), + ); + } + } +} + /// A list of `ItemT`. This data structure stores a list for every field of `ItemT`, /// reducing memory usage if `ItemT` contains padding and improves memory cache usage if /// only certain fields are needed when iterating. @@ -28,68 +73,72 @@ use std::ptr::NonNull; /// age, age, age, /// ``` #[derive(Debug)] -pub struct MultiVec<ItemT> -where - ItemT: Item, +pub struct MultiVec { - _pd: PhantomData<ItemT>, ptr: NonNull<u8>, field_arr_byte_offsets: Vec<usize>, + field_sizes: Vec<usize>, length: usize, capacity: usize, layout: Option<Layout>, } -impl<ItemT> MultiVec<ItemT> -where - ItemT: Item, +impl MultiVec { - // The following is borrow from std's RawVec implementation: - // Skip to: - // - 8 if the element size is 1, because any heap allocators is likely to round up a - // request of less than 8 bytes to at least 8 bytes. - // - 4 if elements are moderate-sized (<= 1 KiB). - // - 1 otherwise, to avoid wasting too much space for very short Vecs. - const MIN_NON_ZERO_CAP: usize = if size_of::<ItemT>() == 1 { - 8 - } else if size_of::<ItemT>() <= 1024 { - 4 - } else { - 1 - }; + fn get_min_non_zero_cap(fields: impl AsRef<[OwnedAnyPtr]>) -> usize + { + let total_size = fields + .as_ref() + .iter() + .fold(0usize, |acc, field| acc + field.size()); + + // The following is borrow from std's RawVec implementation: + // Skip to: + // - 8 if the element size is 1, because any heap allocators is likely to round up + // a request of less than 8 bytes to at least 8 bytes. + // - 4 if elements are moderate-sized (<= 1 KiB). + // - 1 otherwise, to avoid wasting too much space for very short Vecs. + if total_size == 1 { + 8 + } else if total_size <= 1024 { + 4 + } else { + 1 + } + } /// Returns a new `MultiVec`. This function does not allocate any memory. #[must_use] pub const fn new() -> Self { Self { - _pd: PhantomData, ptr: NonNull::dangling(), field_arr_byte_offsets: Vec::new(), + field_sizes: Vec::new(), length: 0, capacity: 0, layout: None, } } - /// Returns a new `MultiVec` with a capacity for `capacity` items. This function will - /// allocate memory. - #[must_use] - pub fn with_capacity(capacity: usize) -> Self - { - let mut this = Self { - _pd: PhantomData, - ptr: NonNull::dangling(), - field_arr_byte_offsets: Vec::new(), - length: 0, - capacity: 0, - layout: None, - }; - - this.do_first_alloc(capacity); - - this - } + ///// Returns a new `MultiVec` with a capacity for `capacity` items. This function + ///// will allocate memory. + //#[must_use] + //pub fn with_capacity(capacity: usize) -> Self + //{ + // let mut this = Self { + // _pd: PhantomData, + // ptr: NonNull::dangling(), + // field_arr_byte_offsets: Vec::new(), + // length: 0, + // capacity: 0, + // layout: None, + // }; + // + // this.do_first_alloc(capacity); + // + // this + //} /// Pushes a item to the `MultiVec`. /// @@ -97,60 +146,63 @@ where /// Pushing can be pretty slow. Since all of the field lists are stored in the same /// allocation, when pushing and the `MultiVec` needs to grow, all lists except the /// first has to be moved to new locations for them to not overlap. - pub fn push(&mut self, item: ItemT) + pub fn push( + &mut self, + fields: impl AsRef<[OwnedAnyPtr]> + IntoIterator<Item = OwnedAnyPtr>, + ) { if self.capacity != 0 { if self.capacity == self.length { - self.grow_amortized(1); + self.grow_amortized(1, &fields); } - self.write_item(self.length, item); + self.write_item(self.length, fields); self.length += 1; return; } - self.do_first_alloc(1); + self.field_sizes = fields.as_ref().iter().map(|field| field.size()).collect(); + + self.do_first_alloc(1, &fields); - self.write_item(0, item); + self.write_item(0, fields); self.length = 1; } - /// Returns a field of the item with the given index. - /// - /// This function is equivalant to doing `.get_all().get(index)` - #[must_use] - pub fn get<FieldSel>( - &self, - index: usize, - ) -> Option<&<FieldSel as ItemFieldSelection<ItemT>>::Field> - where - FieldSel: ItemFieldSelection<ItemT>, - { - if index >= self.length { - return None; - } - - let field_metadata = FieldSel::metadata(); - - let field_arr_byte_offset = self.field_arr_byte_offsets[FieldSel::INDEX]; - - let field_arr_ptr = unsafe { self.ptr.byte_add(field_arr_byte_offset) }; - - let field_ptr = unsafe { field_arr_ptr.add(field_metadata.size * index) }; - - Some(unsafe { field_ptr.cast().as_ref() }) - } + ///// Returns a field of the item with the given index. + ///// + ///// This function is equivalant to doing `.get_all().get(index)` + //#[must_use] + //pub fn get<FieldSel>( + // &self, + // index: usize, + //) -> Option<&<FieldSel as ItemFieldSelection<ItemT>>::Field> + //where + // FieldSel: ItemFieldSelection<ItemT>, + //{ + // if index >= self.length { + // return None; + // } + // + // let field_metadata = FieldSel::metadata(); + // + // let field_arr_byte_offset = self.field_arr_byte_offsets[FieldSel::INDEX]; + // + // let field_arr_ptr = unsafe { self.ptr.byte_add(field_arr_byte_offset) }; + // + // let field_ptr = unsafe { field_arr_ptr.add(field_metadata.size * index) }; + // + // Some(unsafe { field_ptr.cast().as_ref() }) + //} /// Returns a slice containing the specified field of all items. #[must_use] - pub fn get_all<FieldSel>(&self) -> &[<FieldSel as ItemFieldSelection<ItemT>>::Field] - where - FieldSel: ItemFieldSelection<ItemT>, + pub fn get_all(&self, field_index: usize) -> &[()] { - let field_arr_byte_offset = self.field_arr_byte_offsets[FieldSel::INDEX]; + let field_arr_byte_offset = self.field_arr_byte_offsets[field_index]; let field_arr_ptr = unsafe { self.ptr.byte_add(field_arr_byte_offset) }; @@ -178,18 +230,19 @@ where self.length == 0 } - fn grow_amortized(&mut self, additional: usize) + fn grow_amortized(&mut self, additional: usize, fields: impl AsRef<[OwnedAnyPtr]>) { let required_cap = self.capacity.checked_add(additional).unwrap(); // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. let new_capacity = max(self.capacity * 2, required_cap); - let new_capacity = max(Self::MIN_NON_ZERO_CAP, new_capacity); + let new_capacity = max(Self::get_min_non_zero_cap(&fields), new_capacity); let layout = &self.layout.unwrap(); - let (new_layout, new_field_arr_byte_offsets) = Self::create_layout(new_capacity); + let (new_layout, new_field_arr_byte_offsets) = + Self::create_layout(new_capacity, &fields); let Some(new_ptr) = NonNull::new(unsafe { realloc(self.ptr.as_ptr(), *layout, new_layout.size()) @@ -197,9 +250,7 @@ where handle_alloc_error(new_layout); }; - for (field_index, field_metadata) in - ItemT::iter_field_metadata().enumerate().rev() - { + for (field_index, field) in fields.as_ref().iter().enumerate().rev() { let old_field_arr_byte_offset = self.field_arr_byte_offsets[field_index]; let new_field_arr_byte_offset = new_field_arr_byte_offsets[field_index]; @@ -213,7 +264,7 @@ where std::ptr::copy( old_field_arr_ptr.as_ptr(), new_field_arr_ptr.as_ptr(), - field_metadata.size * self.capacity, + field.size() * self.capacity, ); } } @@ -224,9 +275,9 @@ where self.field_arr_byte_offsets = new_field_arr_byte_offsets; } - fn do_first_alloc(&mut self, capacity: usize) + fn do_first_alloc(&mut self, capacity: usize, fields: impl AsRef<[OwnedAnyPtr]>) { - let (layout, field_arr_byte_offsets) = Self::create_layout(capacity); + let (layout, field_arr_byte_offsets) = Self::create_layout(capacity, fields); let Some(ptr) = NonNull::new(unsafe { alloc(layout) }) else { handle_alloc_error(layout); @@ -238,29 +289,25 @@ where self.layout = Some(layout); } - fn create_layout(length: usize) -> (Layout, Vec<usize>) + fn create_layout( + length: usize, + fields: impl AsRef<[OwnedAnyPtr]>, + ) -> (Layout, Vec<usize>) { - let mut field_metadata_iter = ItemT::iter_field_metadata(); + let mut field_iter = fields.as_ref().iter(); - let first_field_metadata = field_metadata_iter.next().unwrap(); + let first_field = field_iter.next().unwrap(); - let mut layout = array_layout( - first_field_metadata.size, - first_field_metadata.alignment, - length, - ) - .unwrap(); + let mut layout = + array_layout(first_field.size(), first_field.alignment(), length).unwrap(); - let mut field_arr_byte_offsets = Vec::with_capacity(ItemT::FIELD_CNT); + let mut field_arr_byte_offsets = Vec::with_capacity(fields.as_ref().len()); field_arr_byte_offsets.push(0); - for field_metadata in field_metadata_iter { + for field in field_iter { let (new_layout, array_byte_offset) = layout - .extend( - array_layout(field_metadata.size, field_metadata.alignment, length) - .unwrap(), - ) + .extend(array_layout(field.size(), field.alignment(), length).unwrap()) .unwrap(); layout = new_layout; @@ -271,60 +318,58 @@ where (layout, field_arr_byte_offsets) } - fn write_item(&mut self, index: usize, item: ItemT) + fn write_item(&mut self, index: usize, fields: impl IntoIterator<Item = OwnedAnyPtr>) { - for (field_index, field_metadata) in ItemT::iter_field_metadata().enumerate() { + for (field_index, item_field) in fields.into_iter().enumerate() { + let field_size = item_field.size(); + let field_arr_byte_offset = self.field_arr_byte_offsets[field_index]; let field_arr_ptr = unsafe { self.ptr.byte_add(field_arr_byte_offset) }; - let field_ptr = unsafe { field_arr_ptr.add(field_metadata.size * index) }; + let field_dst_ptr = unsafe { field_arr_ptr.add(field_size * index) }; + + let item_field_ptr = item_field.as_ptr().cast::<u8>(); unsafe { std::ptr::copy_nonoverlapping( - std::ptr::from_ref(&item) - .byte_add(field_metadata.offset) - .cast::<u8>(), - field_ptr.as_ptr(), - field_metadata.size, + item_field_ptr, + field_dst_ptr.as_ptr(), + field_size, ); } } - - forget(item); - } -} - -impl<ItemT> FromIterator<ItemT> for MultiVec<ItemT> -where - ItemT: Item, -{ - fn from_iter<ItemIter: IntoIterator<Item = ItemT>>(iter: ItemIter) -> Self - { - let iter = iter.into_iter(); - - let initial_capacity = - max(Self::MIN_NON_ZERO_CAP, iter.size_hint().0.saturating_add(1)); - - let mut this = Self::with_capacity(initial_capacity); - - for item in iter { - if this.capacity == this.length { - this.grow_amortized(1); - } - - this.write_item(this.length, item); - - this.length += 1; - } - - this } } -impl<ItemT> Default for MultiVec<ItemT> -where - ItemT: Item, +//impl<ItemT> FromIterator<ItemT> for MultiVec<ItemT> +//where +// ItemT: Item, +//{ +// fn from_iter<ItemIter: IntoIterator<Item = ItemT>>(iter: ItemIter) -> Self +// { +// let iter = iter.into_iter(); +// +// let initial_capacity = +// max(Self::MIN_NON_ZERO_CAP, iter.size_hint().0.saturating_add(1)); +// +// let mut this = Self::with_capacity(initial_capacity); +// +// for item in iter { +// if this.capacity == this.length { +// this.grow_amortized(1); +// } +// +// this.write_item(this.length, item); +// +// this.length += 1; +// } +// +// this +// } +//} + +impl Default for MultiVec { fn default() -> Self { @@ -332,29 +377,27 @@ where } } -impl<ItemT> Drop for MultiVec<ItemT> -where - ItemT: Item, +impl Drop for MultiVec { fn drop(&mut self) { - if needs_drop::<ItemT>() { - for index in 0..self.length { - for (field_index, field_metadata) in - ItemT::iter_field_metadata().enumerate() - { - let field_arr_byte_offset = self.field_arr_byte_offsets[field_index]; - - let field_arr_ptr = - unsafe { self.ptr.byte_add(field_arr_byte_offset) }; - - let field_ptr = - unsafe { field_arr_ptr.add(field_metadata.size * index) }; - - unsafe { - ItemT::drop_field_inplace(field_index, field_ptr.as_ptr()); - } + assert_eq!(self.field_sizes.len(), self.field_arr_byte_offsets.len()); + + for index in 0..self.length { + for (field_index, field_size) in self.field_sizes.iter().enumerate() { + let field_arr_byte_offset = self.field_arr_byte_offsets[field_index]; + + let field_arr_ptr = unsafe { self.ptr.byte_add(field_arr_byte_offset) }; + + let field_ptr = unsafe { field_arr_ptr.add(field_size * index) }; + + unsafe { + field_ptr.drop_in_place(); } + + //unsafe { + // ItemT::drop_field_inplace(field_index, field_ptr.as_ptr()); + //} } } @@ -366,93 +409,6 @@ where } } -/// Usable as a item of a [`MultiVec`]. -/// -/// # Safety -/// The iterator returned by `iter_field_metadata` must yield [`ItemFieldMetadata`] that -/// correctly represents fields of the implementor type. -pub unsafe trait Item -{ - type FieldMetadataIter<'a>: Iterator<Item = &'a ItemFieldMetadata> - + DoubleEndedIterator - + ExactSizeIterator; - - /// The number of fields this item has. - const FIELD_CNT: usize; - - /// Returns a iterator of metadata of the fields of this item. - fn iter_field_metadata() -> Self::FieldMetadataIter<'static>; - - /// Drops a field of this item inplace. - /// - /// # Safety - /// Behavior is undefined if any of the following conditions are violated: - /// - /// * `field_index` must be a valid field index for this item. - /// * The value `field_ptr` points to must be valid for the type of the field with - /// index `field_index`. - /// * `field_ptr` must be [valid] for both reads and writes. - /// * `field_ptr` must be properly aligned, even if the field type has size 0. - /// * `field_ptr` must be nonnull, even if the field type has size 0. - /// * The value `field_ptr` points to must be valid for dropping, which may mean it - /// must uphold additional invariants. These invariants depend on the type of the - /// value being dropped. For instance, when dropping a Box, the box's pointer to the - /// heap must be valid. - /// * While `drop_field_inplace` is executing, the only way to access parts of - /// `field_ptr` is through the `&mut self` references supplied to the [`Drop::drop`] - /// methods that `drop_field_inplace` invokes. - /// - /// Additionally, if the field type is not [`Copy`], using the pointed-to value after - /// calling `drop_field_inplace` can cause undefined behavior. Note that `*field_ptr = - /// foo` counts as a use because it will cause the value to be dropped again. - /// - /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety - unsafe fn drop_field_inplace(field_index: usize, field_ptr: *mut u8); -} - -/// Contains metadata of a field of a [`Item`]. -/// -/// # Examples -/// ``` -/// # use multi_vec::ItemFieldMetadata; -/// # -/// struct CatFood -/// { -/// label: String, -/// age_days: u16, -/// quality: u8, -/// } -/// -/// let cat_food_field_metadata = ItemFieldMetadata { -/// offset: std::mem::offset_of!(CatFood, age_days), -/// size: std::mem::size_of::<u16>(), -/// alignment: std::mem::align_of::<u16>(), -/// }; -/// ``` -pub struct ItemFieldMetadata -{ - pub offset: usize, - pub size: usize, - pub alignment: usize, -} - -/// A field selection for `ItemT`. -/// -/// # Safety -/// The constant `INDEX`, the type `Field` and the `ItemFieldMetadata` returned by the -/// `metadata` function must correctly represent a field of `ItemT`; -pub unsafe trait ItemFieldSelection<ItemT> -where - ItemT: Item, -{ - const INDEX: usize; - - type Field; - - /// Returns metadata of this item field. - fn metadata() -> &'static ItemFieldMetadata; -} - #[inline] const fn array_layout( element_size: usize, @@ -509,84 +465,16 @@ struct CoolLayoutError; #[cfg(test)] mod tests { - use std::mem::offset_of; - use std::ptr::{drop_in_place, NonNull}; - - use crate::{Item, ItemFieldMetadata, ItemFieldSelection, MultiVec}; - - struct Foo - { - num_a: u32, - num_b: u16, - } - - struct FooFieldNumA; - struct FooFieldNumB; - - unsafe impl ItemFieldSelection<Foo> for FooFieldNumA - { - type Field = u32; - - const INDEX: usize = 0; - - fn metadata() -> &'static ItemFieldMetadata - { - &FOO_FIELD_METADATA[0] - } - } - - unsafe impl ItemFieldSelection<Foo> for FooFieldNumB - { - type Field = u16; - - const INDEX: usize = 1; - - fn metadata() -> &'static ItemFieldMetadata - { - &FOO_FIELD_METADATA[1] - } - } - - static FOO_FIELD_METADATA: [ItemFieldMetadata; 2] = [ - ItemFieldMetadata { - offset: offset_of!(Foo, num_a), - size: size_of::<u32>(), - alignment: align_of::<u32>(), - }, - ItemFieldMetadata { - offset: offset_of!(Foo, num_b), - size: size_of::<u16>(), - alignment: align_of::<u16>(), - }, - ]; - - unsafe impl Item for Foo - { - type FieldMetadataIter<'a> = std::slice::Iter<'a, ItemFieldMetadata>; - - const FIELD_CNT: usize = 2; + use std::ptr::NonNull; - fn iter_field_metadata() -> Self::FieldMetadataIter<'static> - { - FOO_FIELD_METADATA.iter() - } - - unsafe fn drop_field_inplace(field_index: usize, field_ptr: *mut u8) - { - if field_index == 0 { - unsafe { drop_in_place::<u32>(field_ptr.cast()) } - } else if field_index == 1 { - unsafe { drop_in_place::<u16>(field_ptr.cast()) } - } - } - } + use crate::{MultiVec, OwnedAnyPtr}; #[test] fn single_push_works() { - let mut multi_vec = MultiVec::<Foo>::new(); + let mut multi_vec = MultiVec::new(); - multi_vec.push(Foo { num_a: 123, num_b: 654 }); + multi_vec.push([OwnedAnyPtr::new(123), OwnedAnyPtr::new(654)]); assert_eq!(multi_vec.capacity, 1); assert_eq!(multi_vec.length, 1); @@ -614,11 +502,11 @@ mod tests #[test] fn multiple_pushes_works() { - let mut multi_vec = MultiVec::<Foo>::new(); + let mut multi_vec = MultiVec::new(); - multi_vec.push(Foo { num_a: u32::MAX / 2, num_b: 654 }); - multi_vec.push(Foo { num_a: 765, num_b: u16::MAX / 3 }); - multi_vec.push(Foo { num_a: u32::MAX / 5, num_b: 337 }); + multi_vec.push([OwnedAnyPtr::new(u32::MAX / 2), OwnedAnyPtr::new::<u16>(654)]); + multi_vec.push([OwnedAnyPtr::new(765), OwnedAnyPtr::new::<u16>(u16::MAX / 3)]); + multi_vec.push([OwnedAnyPtr::new(u32::MAX / 5), OwnedAnyPtr::new::<u16>(337)]); assert_eq!(multi_vec.capacity, 4); assert_eq!(multi_vec.length, 3); @@ -643,134 +531,143 @@ mod tests ); } - #[test] - fn multiple_pushes_in_preallocated_works() - { - let mut multi_vec = MultiVec::<Foo>::with_capacity(2); - - multi_vec.push(Foo { num_a: 83710000, num_b: 654 }); - multi_vec.push(Foo { num_a: 765, num_b: u16::MAX / 7 }); - - assert_eq!(multi_vec.capacity, 2); - assert_eq!(multi_vec.length, 2); - - assert_eq!(multi_vec.field_arr_byte_offsets, [0, size_of::<u32>() * 2]); - - assert_eq!( - unsafe { - std::slice::from_raw_parts::<u32>(multi_vec.ptr.as_ptr().cast(), 2) - }, - [83710000, 765] - ); - - assert_eq!( - unsafe { - std::slice::from_raw_parts::<u16>( - multi_vec.ptr.as_ptr().byte_add(size_of::<u32>() * 2).cast(), - 2, - ) - }, - [654, u16::MAX / 7] - ); - } - - #[test] - fn get_works() + //#[test] + //fn multiple_pushes_in_preallocated_works() + //{ + // let mut multi_vec = MultiVec::<Foo>::with_capacity(2); + // + // multi_vec.push(Foo { num_a: 83710000, num_b: 654 }); + // multi_vec.push(Foo { num_a: 765, num_b: u16::MAX / 7 }); + // + // assert_eq!(multi_vec.capacity, 2); + // assert_eq!(multi_vec.length, 2); + // + // assert_eq!(multi_vec.field_arr_byte_offsets, [0, size_of::<u32>() * 2]); + // + // assert_eq!( + // unsafe { + // std::slice::from_raw_parts::<u32>(multi_vec.ptr.as_ptr().cast(), 2) + // }, + // [83710000, 765] + // ); + // + // assert_eq!( + // unsafe { + // std::slice::from_raw_parts::<u16>( + // multi_vec.ptr.as_ptr().byte_add(size_of::<u32>() * 2).cast(), + // 2, + // ) + // }, + // [654, u16::MAX / 7] + // ); + //} + + //#[test] + //fn get_works() + //{ + // let mut multi_vec = MultiVec::<Foo>::new(); + // + // #[repr(packed)] + // #[allow(dead_code)] + // struct Data + // { + // num_a: [u32; 3], + // num_b: [u16; 3], + // } + // + // let data = Data { + // num_a: [u32::MAX - 3000, 901, 5560000], + // num_b: [20210, 7120, 1010], + // }; + // + // multi_vec.ptr = NonNull::from(&data).cast(); + // multi_vec.field_arr_byte_offsets = vec![0, size_of::<u32>() * 3]; + // multi_vec.length = 3; + // multi_vec.capacity = 3; + // + // assert_eq!( + // multi_vec.get::<FooFieldNumA>(0).copied(), + // Some(u32::MAX - 3000) + // ); + // assert_eq!(multi_vec.get::<FooFieldNumB>(0).copied(), Some(20210)); + // + // assert_eq!(multi_vec.get::<FooFieldNumA>(1).copied(), Some(901)); + // assert_eq!(multi_vec.get::<FooFieldNumB>(1).copied(), Some(7120)); + // + // assert_eq!(multi_vec.get::<FooFieldNumA>(2).copied(), Some(5560000)); + // assert_eq!(multi_vec.get::<FooFieldNumB>(2).copied(), Some(1010)); + //} + + //#[test] + //fn from_iter_works() + //{ + // let multi_vec = MultiVec::<Foo>::from_iter([ + // Foo { num_a: 456456, num_b: 9090 }, + // Foo { num_a: 79541, num_b: 2233 }, + // Foo { num_a: 1761919, num_b: u16::MAX - 75 }, + // Foo { num_a: u32::MAX / 9, num_b: 8182 }, + // ]); + // + // assert_eq!(multi_vec.length, 4); + // assert_eq!(multi_vec.capacity, 5); + // + // assert_eq!(multi_vec.field_arr_byte_offsets, [0, size_of::<u32>() * 5]); + // + // assert_eq!( + // unsafe { + // std::slice::from_raw_parts::<u32>(multi_vec.ptr.as_ptr().cast(), 4) + // }, + // [456456, 79541, 1761919, u32::MAX / 9] + // ); + // + // assert_eq!( + // unsafe { + // std::slice::from_raw_parts::<u16>( + // multi_vec.ptr.as_ptr().byte_add(size_of::<u32>() * 5).cast(), + // 4, + // ) + // }, + // [9090, 2233, u16::MAX - 75, 8182] + // ); + //} + + unsafe fn cast_slice<SrcItem, DstItem>(slice: &[SrcItem]) -> &[DstItem] { - let mut multi_vec = MultiVec::<Foo>::new(); - - #[repr(packed)] - #[allow(dead_code)] - struct Data - { - num_a: [u32; 3], - num_b: [u16; 3], + unsafe { + std::slice::from_raw_parts(slice.as_ptr().cast::<DstItem>(), slice.len()) } - - let data = Data { - num_a: [u32::MAX - 3000, 901, 5560000], - num_b: [20210, 7120, 1010], - }; - - multi_vec.ptr = NonNull::from(&data).cast(); - multi_vec.field_arr_byte_offsets = vec![0, size_of::<u32>() * 3]; - multi_vec.length = 3; - multi_vec.capacity = 3; - - assert_eq!( - multi_vec.get::<FooFieldNumA>(0).copied(), - Some(u32::MAX - 3000) - ); - assert_eq!(multi_vec.get::<FooFieldNumB>(0).copied(), Some(20210)); - - assert_eq!(multi_vec.get::<FooFieldNumA>(1).copied(), Some(901)); - assert_eq!(multi_vec.get::<FooFieldNumB>(1).copied(), Some(7120)); - - assert_eq!(multi_vec.get::<FooFieldNumA>(2).copied(), Some(5560000)); - assert_eq!(multi_vec.get::<FooFieldNumB>(2).copied(), Some(1010)); - } - - #[test] - fn from_iter_works() - { - let multi_vec = MultiVec::<Foo>::from_iter([ - Foo { num_a: 456456, num_b: 9090 }, - Foo { num_a: 79541, num_b: 2233 }, - Foo { num_a: 1761919, num_b: u16::MAX - 75 }, - Foo { num_a: u32::MAX / 9, num_b: 8182 }, - ]); - - assert_eq!(multi_vec.length, 4); - assert_eq!(multi_vec.capacity, 5); - - assert_eq!(multi_vec.field_arr_byte_offsets, [0, size_of::<u32>() * 5]); - - assert_eq!( - unsafe { - std::slice::from_raw_parts::<u32>(multi_vec.ptr.as_ptr().cast(), 4) - }, - [456456, 79541, 1761919, u32::MAX / 9] - ); - - assert_eq!( - unsafe { - std::slice::from_raw_parts::<u16>( - multi_vec.ptr.as_ptr().byte_add(size_of::<u32>() * 5).cast(), - 4, - ) - }, - [9090, 2233, u16::MAX - 75, 8182] - ); } #[test] fn get_all_works() { - let mut multi_vec = MultiVec::<Foo>::new(); + let mut multi_vec = MultiVec::new(); - #[repr(packed)] - #[allow(dead_code)] struct Data { - num_a: [u32; 3], - num_b: [u16; 3], + _a: [u32; 3], + _b: [u16; 3], } let data = Data { - num_a: [u32::MAX - 3000, 901, 5560000], - num_b: [20210, 7120, 1010], + _a: [u32::MAX - 3000, 901, 5560000], + _b: [20210, 7120, 1010], }; multi_vec.ptr = NonNull::from(&data).cast(); multi_vec.field_arr_byte_offsets = vec![0, size_of::<u32>() * 3]; + multi_vec.field_sizes = vec![size_of::<u32>(), size_of::<u16>()]; multi_vec.length = 3; multi_vec.capacity = 3; assert_eq!( - multi_vec.get_all::<FooFieldNumA>(), + unsafe { cast_slice::<_, u32>(multi_vec.get_all(0)) }, [u32::MAX - 3000, 901, 5560000] ); - assert_eq!(multi_vec.get_all::<FooFieldNumB>(), [20210, 7120, 1010]); + assert_eq!( + unsafe { cast_slice::<_, u16>(multi_vec.get_all(1)) }, + [20210, 7120, 1010] + ); } } |