Skip to content

Commit

Permalink
Represent FlagSets with UmbraSlice<Flag>
Browse files Browse the repository at this point in the history
This is an extension of the parent commit to use the "German string"
optimization for the `FlagSet` type as well.

These commits are separate to make benchmarking each change easier.
  • Loading branch information
the-mikedavis committed Nov 3, 2024
1 parent 7584301 commit cefc026
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 14 deletions.
28 changes: 14 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use aff::parser::{
};
use suggester::Suggester;

use crate::alloc::{borrow::Cow, boxed::Box, slice, string::String, vec::Vec};
use crate::alloc::{borrow::Cow, slice, string::String, vec::Vec};
use aff::AffData;
use checker::Checker;
use core::{cmp::Ordering, fmt, hash::BuildHasher};
Expand Down Expand Up @@ -301,25 +301,26 @@ type Flag = core::num::NonZeroU16;
/// unless the value needs to be mutated at some point. Once a dictionary is initialized it's
/// immutable so we don't need a Vec.
#[derive(Default, PartialEq, Eq, Clone)]
struct FlagSet(Box<[Flag]>);
struct FlagSet(umbra_slice::FlagSlice);

impl From<Vec<Flag>> for FlagSet {
fn from(mut flags: Vec<Flag>) -> Self {
flags.sort_unstable();
flags.dedup();
Self(flags.into_boxed_slice())
assert!(flags.len() <= u16::MAX as usize);
Self(umbra_slice::UmbraSlice::try_from(flags.as_slice()).unwrap())
}
}

impl FlagSet {
#[inline]
pub fn as_slice(&self) -> &[Flag] {
&self.0
self.0.as_slice()
}

#[inline]
pub fn iter(&self) -> slice::Iter<'_, Flag> {
self.0.iter()
self.as_slice().iter()
}

#[inline]
Expand All @@ -329,7 +330,7 @@ impl FlagSet {

#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
self.0.is_empty()
}

/// Returns `true` if both sets have at least one element in common.
Expand Down Expand Up @@ -374,7 +375,7 @@ impl FlagSet {
}
}

Self(intersection.into_boxed_slice())
Self(intersection.as_slice().try_into().unwrap())
}

pub fn union(&self, other: &Self) -> Self {
Expand Down Expand Up @@ -411,28 +412,27 @@ impl FlagSet {
}
}

Self(union.into_boxed_slice())
Self(union.as_slice().try_into().unwrap())
}

/// Checks whether the given flag is contained in the flagset.
#[inline]
pub fn contains(&self, flag: &Flag) -> bool {
// See the docs for `slice::binary_search`: it's preferable to `slice::contains` since
// it runs in logarithmic time rather than linear w.r.t. slice length. It requires that
// the slice is sorted (true for flagsets, see `new`).
self.0.binary_search(flag).is_ok()
// In the From (TODO: TryFrom) impl for `FlagSet` we sort the flags so this method can
// be used:
self.0.sorted_contains(flag)
}

pub fn with_flag(&self, flag: Flag) -> Self {
let mut flagset = Vec::from(self.0.clone());
let mut flagset = Vec::from(self.0.as_slice());
flagset.push(flag);
flagset.into()
}
}

impl fmt::Debug for FlagSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("flagset!{:?}", self.0))
f.write_fmt(format_args!("flagset!{:?}", self.0.as_slice()))
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/umbra_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ use core::{

use crate::alloc::{alloc, string::String};

// NOTE: ideally this type would be defined in `src/lib.rs` but that would mean leaking out the
// `prefix_len`/`suffix_len` `const fn`s below. As noted below the type, those should ideally
// not exist anyways.
use crate::Flag;
pub type FlagSlice = UmbraSlice<Flag, { prefix_len::<Flag>() }, { suffix_len::<Flag>() }>;

// NOTE: this module would be miles cleaner if the `generic_const_exprs` Rust feature were
// implemented. Without it we need to pass in the prefix and suffix lengths as const type
// parameters which is quite verbose. Don't fear, though the generics make my eyes bleed, it's
Expand Down

0 comments on commit cefc026

Please sign in to comment.