mirror of
https://github.com/gay-pizza/jaarg.git
synced 2025-12-19 07:20:18 +00:00
Clean up ordered bitset (implementation & usages) and write tests
This commit is contained in:
@@ -94,8 +94,6 @@ impl From<core::num::ParseFloatError> for ParseError<'_> {
|
|||||||
|
|
||||||
impl core::error::Error for ParseError<'_> {}
|
impl core::error::Error for ParseError<'_> {}
|
||||||
|
|
||||||
type RequiredParamsBitSet = ordered_bitset::OrderedBitSet<BitSetType, BITSET_SLOTS>;
|
|
||||||
|
|
||||||
/// Internal state tracked by the parser
|
/// Internal state tracked by the parser
|
||||||
struct ParserState<ID: 'static> {
|
struct ParserState<ID: 'static> {
|
||||||
positional_index: usize,
|
positional_index: usize,
|
||||||
@@ -195,7 +193,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
|
|
||||||
// Match a suitable option by name (ignoring the first flag character & skipping positional arguments)
|
// Match a suitable option by name (ignoring the first flag character & skipping positional arguments)
|
||||||
let (name, option) = self.options.iter()
|
let (name, option) = self.options.iter()
|
||||||
.filter(|opt| matches!(opt.r#type, OptType::Flag | OptType::Value)) .find_map(|opt| {
|
.filter(|opt| matches!(opt.r#type, OptType::Flag | OptType::Value)).find_map(|opt| {
|
||||||
if let Some(name) = opt.match_name(option_str, 1) {
|
if let Some(name) = opt.match_name(option_str, 1) {
|
||||||
Some((name, opt))
|
Some((name, opt))
|
||||||
} else {
|
} else {
|
||||||
@@ -204,7 +202,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}) .ok_or(ParseError::UnknownOption(option_str))?;
|
}).ok_or(ParseError::UnknownOption(option_str))?;
|
||||||
|
|
||||||
// Mark required option as visited
|
// Mark required option as visited
|
||||||
if option.is_required() {
|
if option.is_required() {
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ pub struct Opts<ID: 'static> {
|
|||||||
description: Option<&'static str>,
|
description: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BitSetType = u32;
|
type RequiredParamsBitSet = ordered_bitset::OrderedBitSet<u32, 4>;
|
||||||
const BITSET_SLOTS: usize = 4;
|
|
||||||
/// The maximum amount of allowed required non-positional options.
|
/// The maximum amount of allowed required non-positional options.
|
||||||
pub const MAX_REQUIRED_OPTIONS: usize = BitSetType::BITS as usize * BITSET_SLOTS;
|
pub const MAX_REQUIRED_OPTIONS: usize = RequiredParamsBitSet::CAPACITY;
|
||||||
|
|
||||||
impl<ID: 'static> Opts<ID> {
|
impl<ID: 'static> Opts<ID> {
|
||||||
/// Build argument parser options with the default flag character of '-'
|
/// Build argument parser options with the default flag character of '-'
|
||||||
@@ -30,7 +30,7 @@ impl<ID: 'static> Opts<ID> {
|
|||||||
}
|
}
|
||||||
opt_idx += 1;
|
opt_idx += 1;
|
||||||
}
|
}
|
||||||
assert!(num_required_parameters <= MAX_REQUIRED_OPTIONS,
|
assert!(num_required_parameters <= RequiredParamsBitSet::CAPACITY,
|
||||||
"More than 128 non-positional required option entries is not supported at this time");
|
"More than 128 non-positional required option entries is not supported at this time");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ impl<T: OrderedBitSetStorage, const S: usize> Default for OrderedBitSet<T, S> {
|
|||||||
|
|
||||||
// TODO: Obvious target for improvement when const traits land
|
// TODO: Obvious target for improvement when const traits land
|
||||||
impl<T: OrderedBitSetStorage, const S: usize> OrderedBitSet<T, S> {
|
impl<T: OrderedBitSetStorage, const S: usize> OrderedBitSet<T, S> {
|
||||||
|
/// Number of slots in the bit set.
|
||||||
|
pub(crate) const CAPACITY: usize = T::BITS as usize * S;
|
||||||
|
|
||||||
|
/// Creates a new, empty bit set.
|
||||||
pub(crate) const fn new() -> Self { Self([T::ZERO; S]) }
|
pub(crate) const fn new() -> Self { Self([T::ZERO; S]) }
|
||||||
|
|
||||||
|
/// Sets the slot at `index` to a binary value.
|
||||||
pub(crate) fn insert(&mut self, index: usize, value: bool) {
|
pub(crate) fn insert(&mut self, index: usize, value: bool) {
|
||||||
let array_idx = index >> T::SHIFT;
|
let (array_idx, bit_idx) = self.internal_index(index);
|
||||||
debug_assert!(array_idx < S, "Index out of range");
|
|
||||||
let bit_idx = index & T::MASK;
|
|
||||||
let bit_mask = T::from_usize(0b1) << T::from_usize(bit_idx);
|
let bit_mask = T::from_usize(0b1) << T::from_usize(bit_idx);
|
||||||
if value {
|
if value {
|
||||||
self.0[array_idx] |= bit_mask;
|
self.0[array_idx] |= bit_mask;
|
||||||
@@ -29,21 +32,30 @@ impl<T: OrderedBitSetStorage, const S: usize> OrderedBitSet<T, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the binary value at slot `index`.
|
||||||
pub(crate) fn get(&self, index: usize) -> bool {
|
pub(crate) fn get(&self, index: usize) -> bool {
|
||||||
let array_idx = index >> T::SHIFT;
|
let (array_idx, bit_idx) = self.internal_index(index);
|
||||||
debug_assert!(array_idx < S, "Index out of range");
|
|
||||||
let bit_idx = index & T::MASK;
|
|
||||||
let bit_mask = T::from_usize(0b1) << T::from_usize(bit_idx);
|
let bit_mask = T::from_usize(0b1) << T::from_usize(bit_idx);
|
||||||
(self.0[array_idx] & bit_mask) != T::from_usize(0)
|
(self.0[array_idx] & bit_mask) != T::from_usize(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn internal_index(&self, index: usize) -> (usize, usize) {
|
||||||
|
debug_assert!(index < Self::CAPACITY, "Index out of range");
|
||||||
|
let array_idx = index >> T::SHIFT;
|
||||||
|
let bit_idx = index & T::MASK;
|
||||||
|
(array_idx, bit_idx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait OrderedBitSetStorage: Default + Copy + Clone + Eq + PartialEq
|
trait OrderedBitSetStorage: core::fmt::Debug
|
||||||
|
+ Default + Copy + Clone + Eq + PartialEq
|
||||||
+ BitAnd<Output = Self> + Shl<Output = Self> + Not<Output = Self>
|
+ BitAnd<Output = Self> + Shl<Output = Self> + Not<Output = Self>
|
||||||
+ BitAndAssign + BitOrAssign {
|
+ BitAndAssign + BitOrAssign {
|
||||||
const ZERO: Self;
|
const ZERO: Self;
|
||||||
const SHIFT: u32;
|
const SHIFT: u32;
|
||||||
const MASK: usize;
|
const MASK: usize;
|
||||||
|
const BITS: u32;
|
||||||
fn from_usize(value: usize) -> Self;
|
fn from_usize(value: usize) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +65,8 @@ macro_rules! impl_bitset_storage {
|
|||||||
const ZERO: $t = 0;
|
const ZERO: $t = 0;
|
||||||
const SHIFT: u32 = $b.ilog2();
|
const SHIFT: u32 = $b.ilog2();
|
||||||
const MASK: usize = $b as usize - 1;
|
const MASK: usize = $b as usize - 1;
|
||||||
|
const BITS: u32 = $b;
|
||||||
|
#[inline(always)]
|
||||||
fn from_usize(value: usize) -> $t { value as $t }
|
fn from_usize(value: usize) -> $t { value as $t }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -63,3 +77,49 @@ impl_bitset_storage!(u16, u16::BITS);
|
|||||||
impl_bitset_storage!(u32, u32::BITS);
|
impl_bitset_storage!(u32, u32::BITS);
|
||||||
impl_bitset_storage!(u64, u64::BITS);
|
impl_bitset_storage!(u64, u64::BITS);
|
||||||
impl_bitset_storage!(u128, u128::BITS);
|
impl_bitset_storage!(u128, u128::BITS);
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bitset_storage() {
|
||||||
|
fn harness<T: OrderedBitSetStorage + core::fmt::Debug>(bits_expect: u32, shift_expect: u32) {
|
||||||
|
assert_eq!(T::ZERO, T::from_usize(0));
|
||||||
|
assert_eq!(T::SHIFT, shift_expect);
|
||||||
|
assert_eq!(T::MASK, bits_expect as usize - 1);
|
||||||
|
assert_eq!(T::BITS, bits_expect);
|
||||||
|
}
|
||||||
|
|
||||||
|
harness::<u8>(8, 3);
|
||||||
|
harness::<u16>(16, 4);
|
||||||
|
harness::<u32>(32, 5);
|
||||||
|
harness::<u64>(64, 6);
|
||||||
|
harness::<u128>(128, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ordered_bitset() {
|
||||||
|
fn harness<T: OrderedBitSetStorage, const S: usize>(indices: &[usize]) {
|
||||||
|
let mut bitset = OrderedBitSet::<T, S>::new();
|
||||||
|
for &index in indices {
|
||||||
|
bitset.insert(index, true);
|
||||||
|
}
|
||||||
|
for slot in 0..OrderedBitSet::<u32, 4>::CAPACITY {
|
||||||
|
assert_eq!(bitset.get(slot), indices.contains(&slot));
|
||||||
|
}
|
||||||
|
for &index in indices {
|
||||||
|
bitset.insert(index, false);
|
||||||
|
assert!(!bitset.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let indices = [1, 32, 33, 127, 44, 47, 49];
|
||||||
|
harness::<u8, 16>(&indices);
|
||||||
|
harness::<u16, 8>(&indices);
|
||||||
|
harness::<u32, 4>(&indices);
|
||||||
|
harness::<u64, 2>(&indices);
|
||||||
|
harness::<u128, 1>(&indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user