mirror of
https://github.com/Noratrieb/vechonk.git
synced 2026-01-14 16:45:10 +01:00
iterators and trait impls!
This commit is contained in:
parent
0550582d03
commit
dbd07bb4c8
4 changed files with 235 additions and 52 deletions
63
src/iter.rs
63
src/iter.rs
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{RawVechonk, Vechonk};
|
||||
use alloc::boxed::Box;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
|
||||
/// An iterator over the elements of a [`Vechonk`]
|
||||
pub struct Iter<'a, T: ?Sized> {
|
||||
|
|
@ -47,3 +49,64 @@ impl<'a, T: ?Sized> ExactSizeIterator for Iter<'a, T> {
|
|||
self.raw.len - self.current_index
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the elements of a [`Vechonk`]
|
||||
pub struct IntoIter<T: ?Sized> {
|
||||
raw: RawVechonk<T>,
|
||||
current_index: usize,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> IntoIter<T> {
|
||||
pub(super) fn new(chonk: Vechonk<T>) -> IntoIter<T> {
|
||||
let raw = chonk.raw.copy();
|
||||
|
||||
// We don't want to free the memory!
|
||||
mem::forget(chonk);
|
||||
|
||||
Self {
|
||||
raw,
|
||||
current_index: 0,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Iterator for IntoIter<T> {
|
||||
type Item = Box<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current_index == self.raw.len {
|
||||
return None;
|
||||
}
|
||||
|
||||
// SAFETY: We just did a bounds check above
|
||||
// We also increment the `current_index`, to make sure that we never access it again
|
||||
let ptr = unsafe { self.raw.box_elem_unchecked(self.current_index) };
|
||||
|
||||
self.current_index += 1;
|
||||
|
||||
Some(ptr)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let count = self.raw.len - self.current_index;
|
||||
|
||||
(count, Some(count))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> ExactSizeIterator for IntoIter<T> {
|
||||
fn len(&self) -> usize {
|
||||
self.raw.len - self.current_index
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for IntoIter<T> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: We as `Vechonk` do own the data, and it has the length `self.raw.cap`
|
||||
unsafe {
|
||||
RawVechonk::<T>::dealloc(self.raw.cap, self.raw.ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
127
src/lib.rs
127
src/lib.rs
|
|
@ -43,8 +43,12 @@ extern crate alloc;
|
|||
|
||||
use crate::raw::RawVechonk;
|
||||
use alloc::boxed::Box;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::cmp;
|
||||
use core::cmp::Ordering;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::Index;
|
||||
|
||||
use crate::iter::IntoIter;
|
||||
pub use iter::Iter;
|
||||
|
||||
/// chonky af
|
||||
|
|
@ -91,6 +95,7 @@ impl<T: ?Sized> Vechonk<T> {
|
|||
self.raw.pop()
|
||||
}
|
||||
|
||||
/// An iterator over the elements yielding shared references
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter::new(self)
|
||||
}
|
||||
|
|
@ -105,16 +110,6 @@ impl<T: ?Sized> Vechonk<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
|
@ -124,16 +119,6 @@ impl<T: ?Sized> Vechonk<T> {
|
|||
unsafe { &*self.raw.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.raw.get_unchecked_ptr(index) }
|
||||
}
|
||||
|
||||
/// used for debugging memory layout
|
||||
/// safety: cap must be 96
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -159,17 +144,6 @@ 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
|
||||
impl<T: ?Sized> Drop for Vechonk<T> {
|
||||
fn drop(&mut self) {
|
||||
|
|
@ -180,12 +154,101 @@ impl<T: ?Sized> Drop for Vechonk<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> IntoIterator for Vechonk<T> {
|
||||
type Item = Box<T>;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
// default trait impls
|
||||
|
||||
impl<T: ?Sized> Default for Vechonk<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Vechonk<T>
|
||||
where
|
||||
T: ?Sized + PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.len() != other.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.iter().zip(other.iter()).all(|(a, b)| a == b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Vechonk<T> where T: ?Sized + PartialEq + Eq {}
|
||||
|
||||
impl<T> PartialOrd for Vechonk<T>
|
||||
where
|
||||
T: ?Sized + PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// see core::slice::cmp::SlicePartialOrd::partial_compare
|
||||
|
||||
let len = cmp::min(self.len(), other.len());
|
||||
for i in 0..len {
|
||||
// SAFETY: We did the bounds check above
|
||||
let ordering = unsafe { self.get_unchecked(i).partial_cmp(other.get_unchecked(i)) };
|
||||
|
||||
match ordering {
|
||||
Some(Ordering::Equal) => {}
|
||||
non_eq => return non_eq,
|
||||
}
|
||||
}
|
||||
|
||||
self.len().partial_cmp(&other.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Vechonk<T>
|
||||
where
|
||||
T: ?Sized + PartialOrd + Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// see core::slice::cmp::SliceOrd::compare
|
||||
|
||||
let len = cmp::min(self.len(), other.len());
|
||||
|
||||
for i in 0..len {
|
||||
// SAFETY: We did the bounds check above
|
||||
let ordering = unsafe { self.get_unchecked(i).cmp(other.get_unchecked(i)) };
|
||||
|
||||
match ordering {
|
||||
Ordering::Equal => {}
|
||||
non_eq => return non_eq,
|
||||
}
|
||||
}
|
||||
|
||||
self.len().cmp(&other.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hash for Vechonk<T>
|
||||
where
|
||||
T: ?Sized + Hash,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.iter().for_each(|elem| elem.hash(state))
|
||||
}
|
||||
}
|
||||
|
||||
const fn force_align(size: usize, align: usize) -> usize {
|
||||
size - (size % align)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vechonk {
|
||||
($($x:expr),* $(,)?) => {{
|
||||
let mut chonk = $crate::Vechonk::new();
|
||||
$( chonk.push($x); )*
|
||||
chonk
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
27
src/raw.rs
27
src/raw.rs
|
|
@ -148,8 +148,23 @@ impl<T: ?Sized> RawVechonk<T> {
|
|||
return None;
|
||||
}
|
||||
|
||||
// SAFETY: `self.len - 1` is the last element, and therefore not out of bounds
|
||||
let data = unsafe { self.get_data(self.len - 1) };
|
||||
// SAFETY: `self.len` cannot be 0 and must therefore be in bounds, we decrement the len below
|
||||
let boxed = unsafe { self.box_elem_unchecked(self.len - 1) };
|
||||
|
||||
// We don't need to care about our memory, we can just decrement the `len` and let the old memory be, it's
|
||||
// now semantically uninitialized
|
||||
self.len -= 1;
|
||||
|
||||
Some(boxed)
|
||||
}
|
||||
|
||||
/// Moves one element into a Box
|
||||
/// # Safety
|
||||
/// The index must not be out of bounds. The element is moved out, so it must be made sure that
|
||||
/// this element can't be used again
|
||||
pub unsafe fn box_elem_unchecked(&self, index: usize) -> Box<T> {
|
||||
// SAFETY: We can rely on `index` not being out of bounds
|
||||
let data = unsafe { self.get_data(index) };
|
||||
|
||||
// SAFETY: We can assume that the `offset` from `data` is not out of bounds
|
||||
let elem_ptr = unsafe { self.ptr.as_ptr().add(data.offset) };
|
||||
|
|
@ -179,15 +194,9 @@ impl<T: ?Sized> RawVechonk<T> {
|
|||
// SAFETY: See above for both variables. `data.meta` is the valid metadata for the element
|
||||
let box_fat_ptr = ptr::from_raw_parts_mut(box_ptr as *mut (), data.meta);
|
||||
|
||||
// We don't need to care about our memory, we can just decrement the `len` and let the old memory be, it's
|
||||
// now semantically uninitialized
|
||||
self.len -= 1;
|
||||
|
||||
// SAFETY: We decremented the `len`, so no one else can get access to the element,
|
||||
// therefore it's safe to transfer ownership to the Box here
|
||||
let return_box = unsafe { Box::from_raw(box_fat_ptr) };
|
||||
|
||||
Some(return_box)
|
||||
unsafe { Box::from_raw(box_fat_ptr) }
|
||||
}
|
||||
|
||||
/// Get a raw ptr to an element. Be careful about casting this into a `mut &T`
|
||||
|
|
|
|||
70
src/test.rs
70
src/test.rs
|
|
@ -1,6 +1,6 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::Vechonk;
|
||||
use crate::{vechonk, Vechonk};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[repr(align(2048))]
|
||||
|
|
@ -168,23 +168,71 @@ fn popping() {
|
|||
let mut chonk = Vechonk::<str>::with_capacity(512);
|
||||
|
||||
chonk.push("hello".into());
|
||||
//chonk.push("uwu".into());
|
||||
//chonk.push("I'm popping off!".into());
|
||||
chonk.push("uwu".into());
|
||||
chonk.push("I'm popping off!".into());
|
||||
|
||||
//let popping = chonk.pop().unwrap();
|
||||
//let uwu = chonk.pop().unwrap();
|
||||
let popping = chonk.pop().unwrap();
|
||||
let uwu = chonk.pop().unwrap();
|
||||
let hello = chonk.pop().unwrap();
|
||||
let end = chonk.pop();
|
||||
|
||||
//assert_eq!(popping.as_ref(), "hello");
|
||||
//assert_eq!(uwu.as_ref(), "uwu");
|
||||
assert_eq!(popping.as_ref(), "I'm popping off!");
|
||||
assert_eq!(uwu.as_ref(), "uwu");
|
||||
assert_eq!(hello.as_ref(), "hello");
|
||||
assert_eq!(end, None);
|
||||
}
|
||||
|
||||
//drop(popping);
|
||||
//drop(uwu);
|
||||
drop(hello);
|
||||
drop(chonk);
|
||||
#[test]
|
||||
fn iter() {
|
||||
let chonk: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
|
||||
let mut iter = chonk.iter();
|
||||
|
||||
assert_eq!(iter.next(), Some("hello"));
|
||||
assert_eq!(iter.next(), Some("uwu"));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_iter() {
|
||||
let chonk: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
|
||||
|
||||
let mut iter = chonk.into_iter();
|
||||
|
||||
assert_eq!(iter.next().unwrap().as_ref(), "hello");
|
||||
assert_eq!(iter.next().unwrap().as_ref(), "uwu");
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_eq_eq() {
|
||||
let chonk1 = vechonk![235.0.into(), 325.8.into()];
|
||||
let chonk2 = vechonk![235.0.into(), 325.8.into()];
|
||||
|
||||
assert!(chonk1.eq(&chonk2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_eq_ne() {
|
||||
let chonk1: Vechonk<f32> = vechonk![235.0.into(), 325.9.into()];
|
||||
let chonk2: Vechonk<f32> = vechonk![235.0.into(), 325.8.into()];
|
||||
|
||||
assert!(!chonk1.eq(&chonk2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eq_eq() {
|
||||
let chonk1: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
|
||||
let chonk2: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
|
||||
|
||||
assert!(chonk1.eq(&chonk2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eq_ne() {
|
||||
let chonk1: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
|
||||
let chonk2: Vechonk<str> = vechonk!["hewwo".into(), "owo".into()];
|
||||
|
||||
assert!(!chonk1.eq(&chonk2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue