Skip to content

Commit

Permalink
More docs on builder utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc committed Jul 16, 2023
1 parent 2395d30 commit 9ab08a6
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 22 deletions.
4 changes: 3 additions & 1 deletion experimental/zerotrie/src/builder/branch_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
/// Intermediate metadata for a branch node under construction.
#[derive(Debug, Clone, Copy)]
pub(crate) struct BranchMeta {
/// The lead byte for this branch.
/// The lead byte for this branch. Formerly it was required to be an ASCII byte, but now
/// it can be any byte.
pub ascii: u8,
/// The size in bytes of the trie data reachable from this branch.
pub local_length: usize,
Expand All @@ -16,6 +17,7 @@ pub(crate) struct BranchMeta {
}

impl BranchMeta {
/// Creates a new empty [`BranchMeta`].
pub const fn const_default() -> Self {
BranchMeta {
ascii: 0,
Expand Down
4 changes: 4 additions & 0 deletions experimental/zerotrie/src/builder/bytestr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::borrow::Borrow;
#[cfg(feature = "serde")]
use alloc::boxed::Box;

/// A struct transparent over `[u8]` with convenient helper functions.
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct ByteStr([u8]);
Expand Down Expand Up @@ -71,10 +72,12 @@ impl ByteStr {
self.0.get(index).copied()
}

/// Returns the byte at the given index, panicking if out of bounds.
pub(crate) const fn byte_at_or_panic(&self, index: usize) -> u8 {
self.0[index]
}

/// Const function to evaluate `self < other`.
pub(crate) const fn is_less_then(&self, other: &Self) -> bool {
let mut i = 0;
while i < self.len() && i < other.len() {
Expand All @@ -89,6 +92,7 @@ impl ByteStr {
self.len() < other.len()
}

/// Const function to evaluate `self[..prefix_len] == other[..prefix_len]`
pub(crate) const fn prefix_eq(&self, other: &ByteStr, prefix_len: usize) -> bool {
assert!(prefix_len <= self.len());
assert!(prefix_len <= other.len());
Expand Down
77 changes: 57 additions & 20 deletions experimental/zerotrie/src/builder/konst/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@

use super::super::branch_meta::BranchMeta;

/// A const-friendly slice type.
/// A const-friendly slice type. It is backed by a full slice but is primarily intended
/// to represent subslices of the full slice. We need this only because we can't take
/// subslices in const Rust.
#[derive(Debug, Copy, Clone)]
pub(crate) struct ConstSlice<'a, T> {
/// The full slice.
full_slice: &'a [T],
/// The start index of the slice represented by this [`ConstSlice`].
start: usize,
/// The non-inclusive end index of the slice represented by this [`ConstSlice`].
limit: usize,
}

impl<'a, T> ConstSlice<'a, T> {
/// Creates a [`ConstSlice`] representing an entire slice.
pub const fn from_slice(other: &'a [T]) -> Self {
ConstSlice {
full_slice: other,
Expand All @@ -23,6 +29,7 @@ impl<'a, T> ConstSlice<'a, T> {
}
}

/// Creates a [`ConstSlice`] with the given start and limit.
pub const fn from_manual_slice(full_slice: &'a [T], start: usize, limit: usize) -> Self {
ConstSlice {
full_slice,
Expand All @@ -31,14 +38,17 @@ impl<'a, T> ConstSlice<'a, T> {
}
}

/// Returns the length of the [`ConstSlice`].
pub const fn len(&self) -> usize {
self.limit - self.start
}

/// Gets the element at `index`, panicking if not present.
pub const fn get_or_panic(&self, index: usize) -> &T {
&self.full_slice[index + self.start]
}

/// Gets the first element or `None` if empty.
#[cfg(test)]
pub const fn first(&self) -> Option<&T> {
if self.len() == 0 {
Expand All @@ -48,6 +58,7 @@ impl<'a, T> ConstSlice<'a, T> {
}
}

/// Gets the last element or `None` if empty.
pub const fn last(&self) -> Option<&T> {
if self.len() == 0 {
None
Expand All @@ -56,6 +67,7 @@ impl<'a, T> ConstSlice<'a, T> {
}
}

/// Gets a subslice of this slice.
#[cfg(test)]
pub const fn get_subslice_or_panic(
&self,
Expand All @@ -71,6 +83,7 @@ impl<'a, T> ConstSlice<'a, T> {
}
}

/// Non-const function that returns this [`ConstSlice`] as a regular slice.
#[cfg(any(test, feature = "alloc"))]
pub fn as_slice(&self) -> &'a [T] {
&self.full_slice[self.start..self.limit]
Expand Down Expand Up @@ -98,6 +111,9 @@ impl<const N: usize, T: Default> Default for ConstArrayBuilder<N, T> {
}

impl<const N: usize, T> ConstArrayBuilder<N, T> {
/// Creates a new, empty builder of the given size. `cursor` indicates where in the
/// array new elements will be inserted first. Since we use a lot of prepend operations,
/// it is common to set `cursor` to `N`.
pub const fn new_empty(full_array: [T; N], cursor: usize) -> Self {
assert!(cursor <= N);
Self {
Expand All @@ -107,6 +123,7 @@ impl<const N: usize, T> ConstArrayBuilder<N, T> {
}
}

/// Creates a new builder with some initial content in `[start, limit)`.
pub const fn from_manual_slice(full_array: [T; N], start: usize, limit: usize) -> Self {
assert!(start <= limit);
assert!(limit <= N);
Expand All @@ -117,48 +134,52 @@ impl<const N: usize, T> ConstArrayBuilder<N, T> {
}
}

/// Returns the number of initialized elements in the builder.
pub const fn len(&self) -> usize {
self.limit - self.start
}

/// Whether there are no initialized elements in the builder.
#[allow(dead_code)]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns the initialized elements as a [`ConstSlice`].
pub const fn as_const_slice(&self) -> ConstSlice<T> {
ConstSlice::from_manual_slice(&self.full_array, self.start, self.limit)
}

/// Non-const function that returns a slice of the initialized elements.
#[cfg(feature = "alloc")]
pub fn as_slice(&self) -> &[T] {
&self.full_array[self.start..self.limit]
}
}

impl<const N: usize> ConstArrayBuilder<N, u8> {
pub const fn const_bitor_assign(mut self, index: usize, other: u8) -> Self {
self.full_array[self.start + index] |= other;
self
}
// Can't be generic because T has a destructor
pub const fn const_take_or_panic(self) -> [u8; N] {
// Certain functions that involve dropping `T` require that it be `Copy`
impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
/// Takes a fully initialized builder as an array. Panics if the builder is not
/// fully initialized.
pub const fn const_take_or_panic(self) -> [T; N] {
if self.start != 0 || self.limit != N {
panic!("AsciiTrieBuilder buffer too large");
}
self.full_array
}
// Can't be generic because T has a destructor
pub const fn const_push_front_or_panic(mut self, value: u8) -> Self {

/// Prepends an element to the front of the builder, panicking if there is no room.
pub const fn const_push_front_or_panic(mut self, value: T) -> Self {
if self.start == 0 {
panic!("AsciiTrieBuilder buffer too small");
}
self.start -= 1;
self.full_array[self.start] = value;
self
}
// Can't be generic because T has a destructor
pub const fn const_extend_front_or_panic(mut self, other: ConstSlice<u8>) -> Self {

/// Prepends multiple elements to the front of the builder, panicking if there is no room.
pub const fn const_extend_front_or_panic(mut self, other: ConstSlice<T>) -> Self {
if self.start < other.len() {
panic!("AsciiTrieBuilder buffer too small");
}
Expand All @@ -172,22 +193,28 @@ impl<const N: usize> ConstArrayBuilder<N, u8> {
}
}

impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
pub const fn push_front_or_panic(mut self, value: T) -> Self {
if self.start == 0 {
panic!("AsciiTrieBuilder buffer too small");
}
self.start -= 1;
self.full_array[self.start] = value;
impl<const N: usize> ConstArrayBuilder<N, u8> {
/// Specialized function that performs `self[index] |= other`
pub const fn const_bitor_assign(mut self, index: usize, other: u8) -> Self {
self.full_array[self.start + index] |= other;
self
}
}

impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
/// Swaps the elements at positions `i` and `j`.
#[cfg(feature = "alloc")]
pub fn swap_or_panic(mut self, i: usize, j: usize) -> Self {
self.full_array.swap(self.start + i, self.start + j);
self
}
}

/// Evaluates a block over each element of a const slice. Takes three arguments:
///
/// 1. Expression that resolves to the [`ConstSlice`].
/// 2. Token that will be assigned the value of the element.
/// 3. Block to evaluate for each element.
macro_rules! const_for_each {
($safe_const_slice:expr, $item:tt, $inner:expr) => {{
let mut i = 0;
Expand All @@ -201,6 +228,7 @@ macro_rules! const_for_each {

pub(crate) use const_for_each;

/// A data structure that holds up to N [`BranchMeta`] items.
pub(crate) struct ConstLengthsStack<const N: usize> {
data: [Option<BranchMeta>; N],
idx: usize,
Expand All @@ -213,17 +241,20 @@ impl<const N: usize> core::fmt::Debug for ConstLengthsStack<N> {
}

impl<const N: usize> ConstLengthsStack<N> {
/// Creates a new empty [`ConstLengthsStack`].
pub const fn new() -> Self {
Self {
data: [None; N],
idx: 0,
}
}

/// Returns whether the stack is empty.
pub const fn is_empty(&self) -> bool {
self.idx == 0
}

/// Adds a [`BranchMeta`] to the stack, panicking if there is no room.
#[must_use]
pub const fn push_or_panic(mut self, meta: BranchMeta) -> Self {
if self.idx >= N {
Expand All @@ -238,13 +269,16 @@ impl<const N: usize> ConstLengthsStack<N> {
self
}

/// Returns a copy of the [`BranchMeta`] on the top of the stack, panicking if
/// the stack is empty.
pub const fn peek_or_panic(&self) -> BranchMeta {
if self.idx == 0 {
panic!("AsciiTrie Builder: Attempted to peek from an empty stack");
}
self.get_or_panic(0)
}

/// Returns a copy of the [`BranchMeta`] at the specified index.
const fn get_or_panic(&self, index: usize) -> BranchMeta {
if self.idx <= index {
panic!("AsciiTrie Builder: Attempted to get too deep in a stack");
Expand All @@ -255,6 +289,7 @@ impl<const N: usize> ConstLengthsStack<N> {
}
}

/// Removes many [`BranchMeta`]s from the stack, returning them in a [`ConstArrayBuilder`].
pub const fn pop_many_or_panic(
mut self,
len: usize,
Expand All @@ -267,7 +302,7 @@ impl<const N: usize> ConstLengthsStack<N> {
break;
}
let i = self.idx - ix - 1;
result = result.push_front_or_panic(match self.data[i] {
result = result.const_push_front_or_panic(match self.data[i] {
Some(x) => x,
None => panic!("Not enough items in the ConstLengthsStack"),
});
Expand All @@ -277,12 +312,14 @@ impl<const N: usize> ConstLengthsStack<N> {
(self, result)
}

/// Non-const function that returns the initialized elements as a slice.
fn as_slice(&self) -> &[Option<BranchMeta>] {
&self.data[0..self.idx]
}
}

impl<const N: usize> ConstArrayBuilder<N, BranchMeta> {
/// Converts this builder-array of [`BranchMeta`] to one of the `ascii` fields.
pub const fn map_to_ascii_bytes(&self) -> ConstArrayBuilder<N, u8> {
let mut result = ConstArrayBuilder::new_empty([0; N], N);
let self_as_slice = self.as_const_slice();
Expand Down
Loading

0 comments on commit 9ab08a6

Please sign in to comment.