diff --git a/Cargo.toml b/Cargo.toml index 3436d91..0060a57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,9 @@ members = [ "crates/rfd-demo", "crates/iter-prod", "crates/tpdemo", + "crates/datastructures", "crates/graph", - "crates/sdl-idiot", "minitree", + "crates/sdl-idiot" ] default-members = ["."] diff --git a/crates/datastructures/Cargo.toml b/crates/datastructures/Cargo.toml new file mode 100644 index 0000000..1ae4ad0 --- /dev/null +++ b/crates/datastructures/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "minitree" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/crates/datastructures/src/btree.rs b/crates/datastructures/src/btree.rs new file mode 100644 index 0000000..7d64f34 --- /dev/null +++ b/crates/datastructures/src/btree.rs @@ -0,0 +1,87 @@ +#[derive(Clone, Debug, Default)] +pub struct BTree { + root: Option>, + len: usize, +} + +#[derive(Clone, Debug)] +pub struct Node { + key: K, + value: V, +} + +impl Node { + pub fn new_root(key: K, value: V) -> Self { + Node { key, value } + } +} + +impl BTree { + pub fn new() -> Self { + Self { root: None, len: 0 } + } + + pub fn clear(&mut self) { + self.root = None; + self.len = 0; + } + + pub fn rebalance() { + todo!() + } + + pub fn insert(&mut self, key: K, value: V) -> Option { + if let Some(root) = &self.root { + todo!() + } else { + let new_node = Node::new_root(key, value); + self.root = Some(new_node); + None + } + } + + pub fn remove(&mut self, key: K) -> Option { + todo!() + } + + pub fn has_key(&self, key: K) -> bool { + todo!() + } + + pub fn get(&self, key: K) -> Option<&V> { + todo!() + } + + pub fn get_mut(&mut self, key: K) -> Option<&mut V> { + todo!() + } + + pub fn get_node(&self, key: K) -> Option<&Node> { + todo!() + } + + pub fn get_node_mut(&mut self, key: K) -> Option<&mut Node> { + todo!() + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_create() { + let _tree = BTree::::default(); + } + + #[test] + fn test_insert() { + let mut tree = BTree::::default(); + tree.insert(19, "19"); + tree.insert(9, "09"); + tree.insert(14, "14"); + + assert!(tree.get(19).is_some_and(|s| *s == "19")); + assert!(tree.get(9).is_some_and(|s| *s == "09")); + assert!(tree.get(14).is_some_and(|s| *s == "14")); + } +} diff --git a/crates/datastructures/src/lib.rs b/crates/datastructures/src/lib.rs new file mode 100644 index 0000000..9ff8ce3 --- /dev/null +++ b/crates/datastructures/src/lib.rs @@ -0,0 +1,2 @@ +pub mod raw_vec; +pub mod vec; diff --git a/crates/datastructures/src/raw_vec.rs b/crates/datastructures/src/raw_vec.rs new file mode 100644 index 0000000..ac87146 --- /dev/null +++ b/crates/datastructures/src/raw_vec.rs @@ -0,0 +1,58 @@ +use std::{ + alloc::{self, Layout}, + ptr::NonNull, +}; + +#[derive(Clone, Debug)] +pub(crate) struct RawVec { + pub(crate) ptr: NonNull, + pub(crate) capacity: usize, +} + +impl RawVec { + pub(crate) fn new() -> Self { + Self { + ptr: NonNull::dangling(), + capacity: 0, + } + } + + // See rustonomicon, chapter 9.2 + pub(crate) fn grow(&mut self) { + let (new_cap, new_layout) = if self.capacity == 0 { + (1, Layout::array::(1).unwrap()) + } else { + // This can't overflow since self.cap <= isize::MAX. + let new_cap = 2 * self.capacity; + + // `Layout::array` checks that the number of bytes is <= usize::MAX, + // but this is redundant since old_layout.size() <= isize::MAX, + // so the `unwrap` should never fail. + let new_layout = Layout::array::(new_cap).unwrap(); + (new_cap, new_layout) + }; + + // Ensure that the new allocation doesn't exceed `isize::MAX` bytes. + if new_layout.size() > isize::MAX as usize { + alloc::handle_alloc_error(new_layout); + } + + let new_ptr = if self.capacity == 0 { + unsafe { alloc::alloc(new_layout) } + } else { + let old_layout = Layout::array::(self.capacity).unwrap(); + let old_ptr = self.ptr.as_ptr() as *mut u8; + unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } + }; + + // If allocation fails, `new_ptr` will be null, in which case we abort. + self.ptr = match NonNull::new(new_ptr as *mut T) { + Some(p) => p, + None => alloc::handle_alloc_error(new_layout), + }; + self.capacity = new_cap; + } +} + +unsafe impl Send for RawVec {} +unsafe impl Sync for RawVec {} diff --git a/crates/datastructures/src/vec.rs b/crates/datastructures/src/vec.rs new file mode 100644 index 0000000..8ac1423 --- /dev/null +++ b/crates/datastructures/src/vec.rs @@ -0,0 +1,192 @@ +//! Custom implementation of the Vector datastructure +//! +//! Many thanks to the rustonomicon, chapter 9: +//! https://doc.rust-lang.org/nomicon/vec/vec.html + +use std::{ + alloc::{self, Layout}, + marker::PhantomData, + mem, + ops::{Deref, DerefMut, Index}, + ptr::{self, NonNull}, +}; + +use crate::raw_vec::RawVec; + +#[derive(Clone, Debug)] +pub struct Vec { + used: usize, + marker: PhantomData, + buf: RawVec, +} + +impl Default for Vec { + fn default() -> Self { + Self::new() + } +} + +impl Vec { + pub fn new() -> Self { + if mem::size_of::() == 0 { + panic!("We're not ready to handle ZSTs"); + } + Vec { + used: 0, + marker: PhantomData::default(), + buf: RawVec::new(), + } + } + + pub fn pop(&mut self) -> Option { + if self.used == 0 { + None + } else { + self.used -= 1; + unsafe { Some(ptr::read(self.buf.ptr.as_ptr().add(self.used))) } + } + } + + #[must_use] + pub fn get(&self, index: usize) -> &T { + todo!() + } + + #[must_use] + pub fn get_mut(&self, index: usize) -> &mut T { + todo!() + } + + pub fn push(&mut self, value: T) { + if self.used == self.buf.capacity { + self.buf.grow(); + } + + unsafe { + ptr::write(self.buf.ptr.as_ptr().add(self.used), value); + } + + self.used += 1; + } + + #[must_use] + pub fn len(&self) -> usize { + self.used + } + + #[must_use] + pub fn capacity(&self) -> usize { + self.buf.capacity + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn insert(&mut self, index: usize, elem: T) { + // Note: `<=` because it's valid to insert after everything + // which would be equivalent to push. + assert!(index <= self.used, "index out of bounds"); + if self.used == self.buf.capacity { + self.buf.grow(); + } + + unsafe { + // ptr::copy(src, dest, len): "copy from src to dest len elems" + ptr::copy( + self.buf.ptr.as_ptr().add(index), + self.buf.ptr.as_ptr().add(index + 1), + self.used - index, + ); + ptr::write(self.buf.ptr.as_ptr().add(index), elem); + } + + self.used += 1; + } + + pub fn remove(&mut self, index: usize) -> T { + // Note: `<` because it's *not* valid to remove after everything + assert!(index < self.used, "index out of bounds"); + unsafe { + self.used -= 1; + let result = ptr::read(self.buf.ptr.as_ptr().add(index)); + ptr::copy( + self.buf.ptr.as_ptr().add(index + 1), + self.buf.ptr.as_ptr().add(index), + self.used - index, + ); + result + } + } +} + +impl Index for Vec { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + self.get(index) + } +} + +impl Drop for Vec { + fn drop(&mut self) { + if self.buf.capacity != 0 { + while self.pop().is_some() {} + let layout = Layout::array::(self.buf.capacity).unwrap(); + unsafe { + alloc::dealloc(self.buf.ptr.as_ptr() as *mut u8, layout); + } + } + } +} + +impl Deref for Vec { + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { std::slice::from_raw_parts(self.buf.ptr.as_ptr(), self.used) } + } +} + +impl DerefMut for Vec { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.buf.ptr.as_ptr(), self.used) } + } +} + +unsafe impl Send for Vec {} +unsafe impl Sync for Vec {} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_create() { + let _v = Vec::::new(); + } + + #[test] + fn test_pushpop_num() { + let mut v = Vec::new(); + let vals = &[19, 9, 14, 255, 19191919, 13890, 21521, 1251, 6216, 1830]; + + for val in vals { + v.push(*val); + } + for val in vals.iter().rev() { + assert_eq!(v.pop().unwrap(), *val); + } + } + + #[test] + fn test_pushpop_str() { + let mut v = Vec::new(); + let vals = &["AAAA", "ABBAB", "BBABBABBAJJJ"]; + + for val in vals { + v.push(*val); + } + for val in vals.iter().rev() { + assert_eq!(v.pop().unwrap(), *val); + } + } +}