iterators and trait impls!

This commit is contained in:
nora 2021-12-21 20:55:43 +01:00
parent 0550582d03
commit dbd07bb4c8
4 changed files with 235 additions and 52 deletions

View file

@ -1,5 +1,7 @@
use crate::{RawVechonk, Vechonk}; use crate::{RawVechonk, Vechonk};
use alloc::boxed::Box;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem;
/// An iterator over the elements of a [`Vechonk`] /// An iterator over the elements of a [`Vechonk`]
pub struct Iter<'a, T: ?Sized> { 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 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());
}
}
}

View file

@ -43,8 +43,12 @@ extern crate alloc;
use crate::raw::RawVechonk; use crate::raw::RawVechonk;
use alloc::boxed::Box; 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; pub use iter::Iter;
/// chonky af /// chonky af
@ -91,6 +95,7 @@ impl<T: ?Sized> Vechonk<T> {
self.raw.pop() self.raw.pop()
} }
/// An iterator over the elements yielding shared references
pub fn iter(&self) -> Iter<T> { pub fn iter(&self) -> Iter<T> {
Iter::new(self) 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 /// # Safety
/// The index must be in bounds /// The index must be in bounds
pub unsafe fn get_unchecked(&self, index: usize) -> &T { 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) } 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 /// used for debugging memory layout
/// safety: cap must be 96 /// safety: cap must be 96
#[allow(dead_code)] #[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 /// 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) {
@ -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> { impl<T: ?Sized> Default for Vechonk<T> {
fn default() -> Self { fn default() -> Self {
Self::new() 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 { const fn force_align(size: usize, align: usize) -> usize {
size - (size % align) size - (size % align)
} }
#[macro_export]
macro_rules! vechonk {
($($x:expr),* $(,)?) => {{
let mut chonk = $crate::Vechonk::new();
$( chonk.push($x); )*
chonk
}};
}

View file

@ -148,8 +148,23 @@ impl<T: ?Sized> RawVechonk<T> {
return None; return None;
} }
// SAFETY: `self.len - 1` is the last element, and therefore not out of bounds // SAFETY: `self.len` cannot be 0 and must therefore be in bounds, we decrement the len below
let data = unsafe { self.get_data(self.len - 1) }; 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 // 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) }; 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 // 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); 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, // 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 // therefore it's safe to transfer ownership to the Box here
let return_box = unsafe { Box::from_raw(box_fat_ptr) }; unsafe { Box::from_raw(box_fat_ptr) }
Some(return_box)
} }
/// Get a raw ptr to an element. Be careful about casting this into a `mut &T` /// Get a raw ptr to an element. Be careful about casting this into a `mut &T`

View file

@ -1,6 +1,6 @@
#![cfg(test)] #![cfg(test)]
use crate::Vechonk; use crate::{vechonk, Vechonk};
use alloc::boxed::Box; use alloc::boxed::Box;
#[repr(align(2048))] #[repr(align(2048))]
@ -168,23 +168,71 @@ fn popping() {
let mut chonk = Vechonk::<str>::with_capacity(512); let mut chonk = Vechonk::<str>::with_capacity(512);
chonk.push("hello".into()); chonk.push("hello".into());
//chonk.push("uwu".into()); chonk.push("uwu".into());
//chonk.push("I'm popping off!".into()); chonk.push("I'm popping off!".into());
//let popping = chonk.pop().unwrap(); let popping = chonk.pop().unwrap();
//let uwu = chonk.pop().unwrap(); let uwu = chonk.pop().unwrap();
let hello = chonk.pop().unwrap(); let hello = chonk.pop().unwrap();
let end = chonk.pop(); let end = chonk.pop();
//assert_eq!(popping.as_ref(), "hello"); assert_eq!(popping.as_ref(), "I'm popping off!");
//assert_eq!(uwu.as_ref(), "uwu"); assert_eq!(uwu.as_ref(), "uwu");
assert_eq!(hello.as_ref(), "hello"); assert_eq!(hello.as_ref(), "hello");
assert_eq!(end, None); assert_eq!(end, None);
}
//drop(popping); #[test]
//drop(uwu); fn iter() {
drop(hello); let chonk: Vechonk<str> = vechonk!["hello".into(), "uwu".into()];
drop(chonk); 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] #[test]