mirror of
https://github.com/Noratrieb/vechonk.git
synced 2026-01-14 16:45:10 +01:00
safe push for one element!
This commit is contained in:
parent
b6cd4a1d06
commit
dfd35624a1
2 changed files with 87 additions and 15 deletions
81
src/lib.rs
81
src/lib.rs
|
|
@ -16,9 +16,9 @@
|
|||
//! ```txt
|
||||
//!
|
||||
//! Vechonk<str>
|
||||
//! ------------------------
|
||||
//! | ptr | len | cap |
|
||||
//! ---|---------------------
|
||||
//! ---------------------------------
|
||||
//! | ptr | len | cap | filled |
|
||||
//! ---|-----------------------------
|
||||
//! |
|
||||
//! |___
|
||||
//! |
|
||||
|
|
@ -37,37 +37,54 @@ mod test;
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::alloc::Layout;
|
||||
use core::marker::PhantomData;
|
||||
use core::num::NonZeroUsize;
|
||||
use core::ptr::NonNull;
|
||||
use core::ptr::{NonNull, Pointee};
|
||||
use core::{mem, ptr};
|
||||
|
||||
/// chonky af
|
||||
///
|
||||
/// only works for copy types, but this is WIP and will be removed
|
||||
pub struct Vechonk<T: ?Sized + Copy> {
|
||||
/// note: it does not run destructors for now, thankfully that is 100% safe :))))
|
||||
pub struct Vechonk<T: ?Sized> {
|
||||
/// A pointer to the first element
|
||||
ptr: NonNull<u8>,
|
||||
/// How many elements the Vechonk has
|
||||
len: usize,
|
||||
/// How much memory the Vechonk owns
|
||||
cap: usize,
|
||||
/// How much memory has been used already for elements + meta
|
||||
filled: usize,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Copy> Vechonk<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
impl<T: ?Sized> Vechonk<T> {
|
||||
pub const fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Create a new empty Vechonk that doesn't allocate anything
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
// SAFETY: 1 is not 0
|
||||
ptr: unsafe { NonNull::new_unchecked(1 as *mut u8) },
|
||||
len: 0,
|
||||
cap: 0,
|
||||
filled: 0,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new Vechonk that allocates `capacity` bytes. `capacity` gets shrunken down
|
||||
/// to the next multiple of the alignment of the metadata of `T`
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let capacity = capacity - (capacity % mem::align_of::<Meta<T>>());
|
||||
|
||||
let mut vechonk = Self::new();
|
||||
|
||||
if capacity == 0 {
|
||||
|
|
@ -81,6 +98,36 @@ impl<T: ?Sized + Copy> Vechonk<T> {
|
|||
vechonk
|
||||
}
|
||||
|
||||
pub fn push(&mut self, element: Box<T>) {
|
||||
assert_eq!(self.len, 0, "we only support a single element for now :)");
|
||||
|
||||
let element_size = mem::size_of_val(element.as_ref());
|
||||
|
||||
let ptr = element.as_ref();
|
||||
let meta = ptr::metadata(ptr);
|
||||
|
||||
let meta_size = mem::size_of::<Meta<T>>();
|
||||
|
||||
// just panic here instead of a proper realloc
|
||||
assert!(element_size + meta_size < self.cap - self.filled);
|
||||
|
||||
// SAFETY: none for now
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(ptr as *const T as *mut u8, self.ptr.as_ptr(), element_size);
|
||||
}
|
||||
|
||||
// SAFETY: none for now
|
||||
let meta_ptr = unsafe { self.ptr.as_ptr().add(self.cap - meta_size) };
|
||||
let meta_ptr = meta_ptr as *mut _;
|
||||
|
||||
// SAFETY: none for now
|
||||
unsafe {
|
||||
*meta_ptr = meta;
|
||||
}
|
||||
|
||||
self.len += 1;
|
||||
}
|
||||
|
||||
/// Grows the `Vechonk` to a new capacity. This will not copy any elements. This will put the `Vechonk`
|
||||
/// into an invalid state, since the `len` is still the length of the old allocation.
|
||||
///
|
||||
|
|
@ -88,8 +135,7 @@ impl<T: ?Sized + Copy> Vechonk<T> {
|
|||
/// The caller must either set the `len` to zero, or copy the elements to the new allocation by saving
|
||||
/// `self.ptr` before calling this function.
|
||||
unsafe fn grow_to(&mut self, size: NonZeroUsize) {
|
||||
// SAFETY: 1 is not 0 and a power of two. `size > usize::MAX` must always be true
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(size.get(), 1) };
|
||||
let layout = Layout::from_size_align(size.get(), mem::align_of::<Meta<T>>()).unwrap();
|
||||
|
||||
// SAFETY: layout is guaranteed to have a non-zero size
|
||||
let alloced_ptr = unsafe { alloc::alloc::alloc(layout) };
|
||||
|
|
@ -101,17 +147,24 @@ impl<T: ?Sized + Copy> Vechonk<T> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// we can only drop copy for now because destructors 🤮
|
||||
impl<T: ?Sized + Copy> Drop for Vechonk<T> {
|
||||
impl<T: ?Sized> Drop for Vechonk<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.cap == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// SAFETY: 1 is not 0 and a power of two. `size > usize::MAX` must always be true
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(self.cap, 1) };
|
||||
let layout = Layout::from_size_align(self.cap, mem::align_of::<Meta<T>>()).unwrap();
|
||||
|
||||
unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), layout) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for Vechonk<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
type Meta<T> = <T as Pointee>::Metadata;
|
||||
|
|
|
|||
19
src/test.rs
19
src/test.rs
|
|
@ -1,6 +1,7 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::Vechonk;
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
|
|
@ -22,3 +23,21 @@ fn some_capacity() {
|
|||
|
||||
assert_eq!(chonk.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_single_sized_elem() {
|
||||
let mut chonk = Vechonk::<u8>::with_capacity(100);
|
||||
|
||||
chonk.push(Box::new(1));
|
||||
|
||||
assert_eq!(chonk.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_single_unsized_elem() {
|
||||
let mut chonk = Vechonk::<str>::with_capacity(100);
|
||||
|
||||
chonk.push("hello".into());
|
||||
|
||||
assert_eq!(chonk.len(), 1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue