mirror of
https://github.com/Noratrieb/vechonk.git
synced 2026-01-14 08:35:10 +01:00
add fancy indexing and getting
This commit is contained in:
parent
54bbbbaa6d
commit
c49c716c9a
2 changed files with 67 additions and 10 deletions
|
|
@ -4,7 +4,7 @@ It's implemented by laying out the elements in memory contiguously like `alloc::
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
|
|
||||||
A `Vechonk` is 3 `usize` long. It owns a single allocation, containing the elements and the metadata.
|
A `Vechonk` is 4 `usize` long. It owns a single allocation, containing the elements and the metadata.
|
||||||
The elements are laid out contiguously from the front, while the metadata is laid out contiguously from the back.
|
The elements are laid out contiguously from the front, while the metadata is laid out contiguously from the back.
|
||||||
Both grow towards the center until they meet and get realloced to separate them again.
|
Both grow towards the center until they meet and get realloced to separate them again.
|
||||||
|
|
||||||
|
|
|
||||||
75
src/lib.rs
75
src/lib.rs
|
|
@ -9,7 +9,7 @@
|
||||||
//!
|
//!
|
||||||
//! # Layout
|
//! # Layout
|
||||||
//!
|
//!
|
||||||
//! A [`Vechonk`] is 3 `usize` long. It owns a single allocation, containing the elements and the metadata.
|
//! A [`Vechonk`] is 4 `usize` long. It owns a single allocation, containing the elements and the metadata.
|
||||||
//! The elements are laid out contiguously from the front, while the metadata is laid out contiguously from the back.
|
//! The elements are laid out contiguously from the front, while the metadata is laid out contiguously from the back.
|
||||||
//! Both grow towards the center until they meet and get realloced to separate them again.
|
//! Both grow towards the center until they meet and get realloced to separate them again.
|
||||||
//!
|
//!
|
||||||
|
|
@ -42,7 +42,7 @@ use alloc::boxed::Box;
|
||||||
use core::alloc::Layout;
|
use core::alloc::Layout;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::num::NonZeroUsize;
|
use core::num::NonZeroUsize;
|
||||||
use core::ops::Index;
|
use core::ops::{Index, IndexMut};
|
||||||
use core::ptr::{NonNull, Pointee};
|
use core::ptr::{NonNull, Pointee};
|
||||||
use core::{mem, ptr};
|
use core::{mem, ptr};
|
||||||
|
|
||||||
|
|
@ -128,6 +128,13 @@ impl<T: ?Sized> Vechonk<T> {
|
||||||
let required_align_offset =
|
let required_align_offset =
|
||||||
unsafe { self.ptr.as_ptr().add(elem_offset).align_offset(elem_align) };
|
unsafe { self.ptr.as_ptr().add(elem_offset).align_offset(elem_align) };
|
||||||
|
|
||||||
|
if required_align_offset == usize::MAX {
|
||||||
|
panic!(
|
||||||
|
"Cannot align pointer for element with size: {}, alignment: {}",
|
||||||
|
elem_size, elem_align
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// just panic here instead of a proper realloc
|
// just panic here instead of a proper realloc
|
||||||
if self.needs_grow(elem_size + data_size + required_align_offset) {
|
if self.needs_grow(elem_size + data_size + required_align_offset) {
|
||||||
self.regrow(self.cap + elem_size + data_size);
|
self.regrow(self.cap + elem_size + data_size);
|
||||||
|
|
@ -177,6 +184,47 @@ impl<T: ?Sized> Vechonk<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to an element at the index. Returns `None` if the index is out of bounds
|
||||||
|
pub fn get(&self, index: usize) -> Option<&T> {
|
||||||
|
if index < self.len {
|
||||||
|
// SAFETY: The index has been checked above
|
||||||
|
unsafe { Some(self.get_unchecked(index)) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to an element at the index. Returns `None` if the index is out of bounds
|
||||||
|
pub fn get_mut(&mut self, index: usize) -> Option<&T> {
|
||||||
|
if index < self.len {
|
||||||
|
// SAFETY: The index has been checked above
|
||||||
|
unsafe { Some(self.get_unchecked_mut(index)) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// The index must be in bounds
|
||||||
|
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
|
||||||
|
// SAFETY: The metadata is only assigned directly from the pointer metadata of the original object and therefore valid
|
||||||
|
// The pointer is calculated from the offset, which is also valid
|
||||||
|
// The pointer is aligned, because it has been aligned manually in `Self::push`
|
||||||
|
unsafe { &*self.get_unchecked_ptr(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// The index must be in bounds
|
||||||
|
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
||||||
|
// SAFETY: The metadata is only assigned directly from the pointer metadata of the original object and therefore valid
|
||||||
|
// The pointer is calculated from the offset, which is also valid
|
||||||
|
// The pointer is aligned, because it has been aligned manually in `Self::push`
|
||||||
|
// This function takes `*mut self`, so we have exclusive access to ourselves
|
||||||
|
unsafe { &mut *self.get_unchecked_ptr(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// private helper methods
|
||||||
|
|
||||||
fn regrow(&mut self, min_size: usize) {
|
fn regrow(&mut self, min_size: usize) {
|
||||||
// new_cap must be properly "aligned" for `PtrData<T>`
|
// new_cap must be properly "aligned" for `PtrData<T>`
|
||||||
let new_cap = force_align(min_size * 2, Self::data_align());
|
let new_cap = force_align(min_size * 2, Self::data_align());
|
||||||
|
|
@ -248,7 +296,10 @@ impl<T: ?Sized> Vechonk<T> {
|
||||||
self.cap = size.get();
|
self.cap = size.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_unchecked(&self, index: usize) -> &T {
|
/// Get a raw ptr to an element. Be careful about casting this into a `mut &T`
|
||||||
|
/// # SAFETY
|
||||||
|
/// The index must be in bounds
|
||||||
|
unsafe fn get_unchecked_ptr(&self, index: usize) -> *mut T {
|
||||||
let data_offset = self.offset_for_data(index);
|
let data_offset = self.offset_for_data(index);
|
||||||
|
|
||||||
// SAFETY: We can assume that the index is valid.
|
// SAFETY: We can assume that the index is valid.
|
||||||
|
|
@ -261,12 +312,7 @@ impl<T: ?Sized> Vechonk<T> {
|
||||||
|
|
||||||
let elem_ptr = unsafe { self.ptr.as_ptr().add(data.offset) };
|
let elem_ptr = unsafe { self.ptr.as_ptr().add(data.offset) };
|
||||||
|
|
||||||
let elem_meta_ptr = ptr::from_raw_parts(elem_ptr as *const (), data.meta);
|
ptr::from_raw_parts_mut(elem_ptr as *mut (), data.meta)
|
||||||
|
|
||||||
// SAFETY: The metadata is only assigned directly from the pointer metadata of the original object and therefore valid
|
|
||||||
// The pointer is calculated from the offset, which is also valid
|
|
||||||
// The pointer is not aligned btw, todo lol
|
|
||||||
unsafe { &*elem_meta_ptr }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: The allocation must be owned by `ptr` and have the length `cap`
|
// SAFETY: The allocation must be owned by `ptr` and have the length `cap`
|
||||||
|
|
@ -324,6 +370,17 @@ impl<T: ?Sized> Index<usize> for Vechonk<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> IndexMut<usize> for Vechonk<T> {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
if index >= self.len {
|
||||||
|
panic!("Out of bounds, index {} for len {}", index, self.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: The index is not out of bounds
|
||||||
|
unsafe { self.get_unchecked_mut(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// don't bother with destructors for now
|
/// don't bother with destructors for now
|
||||||
impl<T: ?Sized> Drop for Vechonk<T> {
|
impl<T: ?Sized> Drop for Vechonk<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue