diff --git a/src/datastructures/mod.rs b/src/datastructures/mod.rs index ecb60de..62b2434 100644 --- a/src/datastructures/mod.rs +++ b/src/datastructures/mod.rs @@ -1,2 +1,3 @@ pub mod btree; pub mod queue; +pub mod string; diff --git a/src/datastructures/queue.rs b/src/datastructures/queue.rs index 2e3c110..a59a158 100644 --- a/src/datastructures/queue.rs +++ b/src/datastructures/queue.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::ops::Range; use zerocopy::{AsBytes, FromBytes, FromZeroes, Unaligned}; @@ -108,40 +109,82 @@ impl FilePointer _phantom: PhantomData, }; - // println!("length: {}", old.length.get()); - let mut ptr = data.head; let skip_n = old.length.get().checked_sub(n).unwrap(); - // println!("skip_n: {skip_n}"); - // skip the head for i in 0..skip_n { - // println!("skip [{i}] {ptr:?}"); - ptr = *transaction.read(field_ptr!(ptr, QueueElement, next)); } // remove the tail for i in 0..n { - // println!("free [{i}] {ptr:?}"); - let element = *transaction.read(ptr); - transaction.free(ptr); - f(element.data); + transaction.free(ptr); ptr = element.next; } Some(queue) } + pub fn dequeue_many_back( + self, + transaction: &mut TransactionHandle, + n: u64, + ) -> Option<(Self, Vec)> { + let mut res = Vec::with_capacity(n.try_into().unwrap()); + self.dequeue_many_inner(transaction, n, |t| res.push(t)) + .map(|ptr| { + res.reverse(); + (ptr, res) + }) + } + + // NOTE: calls f with the elements in reverse order. + fn dequeue_many_back_inner( + self, + transaction: &mut TransactionHandle, + n: u64, + mut f: impl FnMut(T), + ) -> Option { + if n == 0 { + return Some(self); + } + + let old = *transaction.read(self); + + if old.length.get() < n { + return None; + } + + let mut ptr = old.head; + + for i in 0..n { + let element = *transaction.read(ptr); + f(element.data); + transaction.free(ptr); + ptr = element.next; + } + + let (queue, data) = transaction.modify(self); + + *data = Queue { + head: ptr, + length: (old.length.get().checked_sub(n).unwrap()).into(), + _phantom: PhantomData, + }; + + Some(queue) + } + pub fn is_empty(self, reader: &impl ReaderTrait) -> bool { self.length(reader) == 0 } - pub fn last(self, reader: &impl ReaderTrait) -> Option { + // TODO: use get(0) instead + pub fn next(self, reader: &impl ReaderTrait) -> Option { let this = reader.read(self); if this.length.get() == 0 { @@ -159,6 +202,68 @@ impl FilePointer Some(*reader.read(field_ptr!(next, QueueElement, data))) } + pub fn get_range(self, reader: &impl ReaderTrait, range: Range) -> Option> { + let mut res = Vec::with_capacity(range.clone().count()); + + self.get_range_inner(reader, range, |element| { + res.push(element); + }); + + res.reverse(); + Some(res) + } + + // 0 is the next item to be dequeued + pub fn get_range_inner( + self, + reader: &impl ReaderTrait, + range: Range, + mut f: impl FnMut(T), + ) -> Option<()> { + if range.is_empty() { + return Some(()); + } + + let n = range.clone().count(); + + let this = reader.read(self); + + if range.end > this.length.get() { + return None; + } + + let len = this.length.get(); + let start = len.checked_sub(range.end).unwrap(); + let end = len.checked_sub(range.start).unwrap(); + + let range = start..end; + + let mut ptr = this.head; + + // skip the elements before the start of the list + for i in 0..range.start { + ptr = *reader.read(field_ptr!(ptr, QueueElement, next)); + } + + for _ in 0..n { + let element = *reader.read(ptr); + + f(element.data); + + ptr = element.next; + } + + Some(()) + } + + pub fn get(self, reader: &impl ReaderTrait, index: u64) -> Option { + let mut res = None; + self.get_range_inner(reader, index..index + 1, |element| { + assert!(res.replace(element).is_none()); + })?; + res + } + pub fn length(self, reader: &impl ReaderTrait) -> u64 { reader.read(field_ptr!(self, Queue, length)).get() } diff --git a/src/datastructures/string.rs b/src/datastructures/string.rs new file mode 100644 index 0000000..4f668fc --- /dev/null +++ b/src/datastructures/string.rs @@ -0,0 +1,54 @@ +use zerocopy::{AsBytes, FromBytes, FromZeroes, Unaligned}; + +use crate::{ + transaction, FilePointer, FileRange, RawFilePointer, ReaderTrait, TransactionHandle, U64, +}; + +#[derive(Clone, Copy, FromBytes, FromZeroes, AsBytes, Unaligned)] +#[repr(transparent)] +struct Str { + data: FileRange, +} + +impl Str { + fn new() -> Self { + Self { + data: RawFilePointer::null().range(0), + } + } +} + +impl Str { + fn set(self, transaction: &mut TransactionHandle, s: &str) -> Self { + let data = if s.is_empty() { + Str::new().data + } else { + let (range, data) = transaction.allocate_range(s.len() as u64); + data.copy_from_slice(s.as_bytes()); + range + }; + + if self.data.len() != 0 { + transaction.free_range(self.data); + } + + Str { data } + } + + fn get(self, reader: &impl ReaderTrait) -> &str { + std::str::from_utf8(reader.read_raw(self.data)).unwrap() + } +} + +impl FilePointer { + fn set(self, transaction: &mut TransactionHandle, s: &str) -> FilePointer { + let new_str = transaction.read::(self).set(transaction, s); + let (ptr, data) = transaction.modify(self); + *data = new_str; + ptr + } + + fn get(self, reader: &impl ReaderTrait) -> &str { + reader.read(self).get(reader) + } +} diff --git a/src/tests.rs b/src/tests.rs index 49b9be3..f8c246e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -862,8 +862,6 @@ fn queue() { let n = queue.length(snapshot); - dbg!(n); - let mut next = *snapshot.read(field_ptr!(queue, Queue, head)); for i in 0..n { @@ -927,6 +925,18 @@ fn queue() { } } + if !root.is_empty(transaction) { + let s = rng.gen_range(0..root.length(transaction)); + let e = rng.gen_range(s..=root.length(transaction)); + + let elements = root.get_range(transaction, s..e).unwrap(); + + dbg!(&elements); + for (i, element) in elements.into_iter().enumerate() { + assert_eq!(element.get(), j + s + i as u64); + } + } + root });