extend queue api and fix string

This commit is contained in:
soruh 2023-08-14 16:59:40 +02:00
parent fe9b7df81f
commit 03455df253
4 changed files with 183 additions and 13 deletions

View File

@ -1,2 +1,3 @@
pub mod btree;
pub mod queue;
pub mod string;

View File

@ -1,4 +1,5 @@
use std::marker::PhantomData;
use std::ops::Range;
use zerocopy::{AsBytes, FromBytes, FromZeroes, Unaligned};
@ -108,40 +109,82 @@ impl<T: FromBytes + FromZeroes + AsBytes + Unaligned + Clone + Copy> 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<T>, 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<R>(
self,
transaction: &mut TransactionHandle<R>,
n: u64,
) -> Option<(Self, Vec<T>)> {
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<R>(
self,
transaction: &mut TransactionHandle<R>,
n: u64,
mut f: impl FnMut(T),
) -> Option<Self> {
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<T> {
// TODO: use get(0) instead
pub fn next(self, reader: &impl ReaderTrait) -> Option<T> {
let this = reader.read(self);
if this.length.get() == 0 {
@ -159,6 +202,68 @@ impl<T: FromBytes + FromZeroes + AsBytes + Unaligned + Clone + Copy> FilePointer
Some(*reader.read(field_ptr!(next, QueueElement<T>, data)))
}
pub fn get_range(self, reader: &impl ReaderTrait, range: Range<u64>) -> Option<Vec<T>> {
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<u64>,
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<T>, 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<T> {
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<T>, length)).get()
}

View File

@ -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<R>(self, transaction: &mut TransactionHandle<R>, 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<Str> {
fn set<R>(self, transaction: &mut TransactionHandle<R>, s: &str) -> FilePointer<Str> {
let new_str = transaction.read::<Str>(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)
}
}

View File

@ -862,8 +862,6 @@ fn queue() {
let n = queue.length(snapshot);
dbg!(n);
let mut next = *snapshot.read(field_ptr!(queue, Queue<U64>, 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
});