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
|
//! ```txt
|
||||||
//!
|
//!
|
||||||
//! Vechonk<str>
|
//! Vechonk<str>
|
||||||
//! ------------------------
|
//! ---------------------------------
|
||||||
//! | ptr | len | cap |
|
//! | ptr | len | cap | filled |
|
||||||
//! ---|---------------------
|
//! ---|-----------------------------
|
||||||
//! |
|
//! |
|
||||||
//! |___
|
//! |___
|
||||||
//! |
|
//! |
|
||||||
|
|
@ -37,37 +37,54 @@ mod test;
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
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::ptr::NonNull;
|
use core::ptr::{NonNull, Pointee};
|
||||||
|
use core::{mem, ptr};
|
||||||
|
|
||||||
/// chonky af
|
/// chonky af
|
||||||
///
|
///
|
||||||
/// only works for copy types, but this is WIP and will be removed
|
/// note: it does not run destructors for now, thankfully that is 100% safe :))))
|
||||||
pub struct Vechonk<T: ?Sized + Copy> {
|
pub struct Vechonk<T: ?Sized> {
|
||||||
|
/// A pointer to the first element
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
|
/// How many elements the Vechonk has
|
||||||
len: usize,
|
len: usize,
|
||||||
|
/// How much memory the Vechonk owns
|
||||||
cap: usize,
|
cap: usize,
|
||||||
|
/// How much memory has been used already for elements + meta
|
||||||
|
filled: usize,
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Copy> Vechonk<T> {
|
impl<T: ?Sized> Vechonk<T> {
|
||||||
pub fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
self.len
|
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 {
|
Self {
|
||||||
// SAFETY: 1 is not 0
|
// SAFETY: 1 is not 0
|
||||||
ptr: unsafe { NonNull::new_unchecked(1 as *mut u8) },
|
ptr: unsafe { NonNull::new_unchecked(1 as *mut u8) },
|
||||||
len: 0,
|
len: 0,
|
||||||
cap: 0,
|
cap: 0,
|
||||||
|
filled: 0,
|
||||||
_marker: PhantomData,
|
_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 {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
let capacity = capacity - (capacity % mem::align_of::<Meta<T>>());
|
||||||
|
|
||||||
let mut vechonk = Self::new();
|
let mut vechonk = Self::new();
|
||||||
|
|
||||||
if capacity == 0 {
|
if capacity == 0 {
|
||||||
|
|
@ -81,6 +98,36 @@ impl<T: ?Sized + Copy> Vechonk<T> {
|
||||||
vechonk
|
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`
|
/// 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.
|
/// 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
|
/// 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.
|
/// `self.ptr` before calling this function.
|
||||||
unsafe fn grow_to(&mut self, size: NonZeroUsize) {
|
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 = Layout::from_size_align(size.get(), mem::align_of::<Meta<T>>()).unwrap();
|
||||||
let layout = unsafe { Layout::from_size_align_unchecked(size.get(), 1) };
|
|
||||||
|
|
||||||
// SAFETY: layout is guaranteed to have a non-zero size
|
// SAFETY: layout is guaranteed to have a non-zero size
|
||||||
let alloced_ptr = unsafe { alloc::alloc::alloc(layout) };
|
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 🤮
|
// 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) {
|
fn drop(&mut self) {
|
||||||
if self.cap == 0 {
|
if self.cap == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: 1 is not 0 and a power of two. `size > usize::MAX` must always be true
|
// 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) };
|
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)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use crate::Vechonk;
|
use crate::Vechonk;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new() {
|
fn new() {
|
||||||
|
|
@ -22,3 +23,21 @@ fn some_capacity() {
|
||||||
|
|
||||||
assert_eq!(chonk.len(), 0);
|
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