split up util.rs
All checks were successful
/ test (push) Successful in 14m43s

This commit is contained in:
Jacob Lifshay 2024-07-28 22:08:51 -07:00
parent 14a9c23697
commit be025c14ca
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
4 changed files with 285 additions and 263 deletions

View file

@ -1,267 +1,20 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::intern::{Intern, Interned};
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; mod const_bool;
use std::{ mod const_cmp;
cmp::Ordering, mod misc;
fmt::{self, Debug, Write},
hash::Hash, #[doc(inline)]
mem::ManuallyDrop, pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
ptr,
rc::Rc, #[doc(inline)]
sync::{Arc, OnceLock}, pub use const_cmp::{
const_bytes_cmp, const_str_array_is_strictly_ascending, const_str_cmp, const_u8_cmp,
const_usize_cmp,
}; };
mod sealed { #[doc(inline)]
pub trait Sealed {} pub use misc::{
} interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
};
/// # Safety
/// the only implementation is `ConstBool<Self::VALUE>`
pub unsafe trait GenericConstBool:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: bool;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstBool<const VALUE: bool>;
impl<const VALUE: bool> Debug for ConstBool<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstBool").field(&Self::VALUE).finish()
}
}
impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {}
/// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> {
const VALUE: bool = VALUE;
}
pub trait ConstBoolDispatchTag {
type Type<Select: GenericConstBool>;
}
pub enum ConstBoolDispatch<FalseT, TrueT> {
False(FalseT),
True(TrueT),
}
impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> {
pub fn new<
Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT>
+ ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>,
Select: GenericConstBool,
>(
v: Tag::Type<Select>,
) -> Self {
let v = ManuallyDrop::new(v);
let v_ptr: *const Tag::Type<Select> = &*v;
// SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
unsafe {
if Select::VALUE {
ConstBoolDispatch::True(ptr::read(v_ptr.cast()))
} else {
ConstBoolDispatch::False(ptr::read(v_ptr.cast()))
}
}
}
}
/// replacement for Iterator::eq_by which isn't stable yet
pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>(
l: L,
r: R,
mut f: F,
) -> bool {
let mut l = l.into_iter();
let mut r = r.into_iter();
l.try_for_each(|l| {
if let Some(r) = r.next() {
f(l, r).then_some(())
} else {
None
}
})
.is_some()
&& r.next().is_none()
}
pub struct DebugAsDisplay<T>(pub T);
impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering {
let mut i = 0;
while i < a.len() && i < b.len() {
match const_u8_cmp(a[i], b[i]) {
Ordering::Equal => {}
retval => return retval,
}
i += 1;
}
const_usize_cmp(a.len(), b.len())
}
pub const fn const_str_cmp(a: &str, b: &str) -> Ordering {
const_bytes_cmp(a.as_bytes(), b.as_bytes())
}
pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool {
let mut i = 1;
while i < a.len() {
match const_str_cmp(a[i - 1], a[i]) {
Ordering::Less => {}
_ => return false,
}
i += 1;
}
true
}
pub trait MakeMutSlice {
type Element: Clone;
fn make_mut_slice(&mut self) -> &mut [Self::Element];
}
impl<T: Clone> MakeMutSlice for Arc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Arc::get_mut(self).is_none() {
*self = Arc::<[T]>::from(&**self);
}
Arc::get_mut(self).unwrap()
}
}
impl<T: Clone> MakeMutSlice for Rc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Rc::get_mut(self).is_none() {
*self = Rc::<[T]>::from(&**self);
}
Rc::get_mut(self).unwrap()
}
}
pub struct DebugAsRawString<'a>(pub &'a str);
impl fmt::Debug for DebugAsRawString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pounds = self
.0
.split_inclusive('"')
.skip(1)
.map(|v| v.len() - v.trim_start_matches('#').len())
.max()
.map(|v| v + 1)
.unwrap_or(0);
f.write_char('r')?;
for _ in 0..pounds {
f.write_char('#')?;
}
f.write_char('"')?;
f.write_str(self.0)?;
f.write_char('"')?;
for _ in 0..pounds {
f.write_char('#')?;
}
Ok(())
}
}
pub fn interned_bit(v: bool) -> Interned<BitSlice> {
static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new();
// use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
}
#[derive(Copy, Clone, Debug)]
pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice);
impl BitSliceWriteWithBase<'_> {
fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1);
// TODO: optimize
let mut buf = String::with_capacity(digit_count);
for digit_index in (0..digit_count).rev() {
let mut digit = 0;
let src = self
.0
.get(digit_index * BITS_PER_DIGIT..)
.unwrap_or_default();
let src = src.get(..BITS_PER_DIGIT).unwrap_or(src);
digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src);
let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap();
if UPPER_CASE {
ch = ch.to_ascii_uppercase();
}
buf.push(ch);
}
f.pad_integral(
true,
match BITS_PER_DIGIT {
1 => "0b",
3 => "0o",
4 => "0x",
_ => "",
},
&buf,
)
}
}
impl fmt::Binary for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<1, false>(f)
}
}
impl fmt::Octal for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<3, false>(f)
}
}
impl fmt::LowerHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, false>(f)
}
}
impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, true>(f)
}
}

View file

@ -0,0 +1,61 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr};
mod sealed {
pub trait Sealed {}
}
/// # Safety
/// the only implementation is `ConstBool<Self::VALUE>`
pub unsafe trait GenericConstBool:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: bool;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstBool<const VALUE: bool>;
impl<const VALUE: bool> Debug for ConstBool<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstBool").field(&Self::VALUE).finish()
}
}
impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {}
/// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> {
const VALUE: bool = VALUE;
}
pub trait ConstBoolDispatchTag {
type Type<Select: GenericConstBool>;
}
pub enum ConstBoolDispatch<FalseT, TrueT> {
False(FalseT),
True(TrueT),
}
impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> {
pub fn new<
Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT>
+ ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>,
Select: GenericConstBool,
>(
v: Tag::Type<Select>,
) -> Self {
let v = ManuallyDrop::new(v);
let v_ptr: *const Tag::Type<Select> = &*v;
// SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
unsafe {
if Select::VALUE {
ConstBoolDispatch::True(ptr::read(v_ptr.cast()))
} else {
ConstBoolDispatch::False(ptr::read(v_ptr.cast()))
}
}
}
}

View file

@ -0,0 +1,51 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::cmp::Ordering;
pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering {
let mut i = 0;
while i < a.len() && i < b.len() {
match const_u8_cmp(a[i], b[i]) {
Ordering::Equal => {}
retval => return retval,
}
i += 1;
}
const_usize_cmp(a.len(), b.len())
}
pub const fn const_str_cmp(a: &str, b: &str) -> Ordering {
const_bytes_cmp(a.as_bytes(), b.as_bytes())
}
pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool {
let mut i = 1;
while i < a.len() {
match const_str_cmp(a[i - 1], a[i]) {
Ordering::Less => {}
_ => return false,
}
i += 1;
}
true
}

View file

@ -0,0 +1,157 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::intern::{Intern, Interned};
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
use std::{
fmt::{self, Debug, Write},
rc::Rc,
sync::{Arc, OnceLock},
};
/// replacement for Iterator::eq_by which isn't stable yet
pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>(
l: L,
r: R,
mut f: F,
) -> bool {
let mut l = l.into_iter();
let mut r = r.into_iter();
l.try_for_each(|l| {
if let Some(r) = r.next() {
f(l, r).then_some(())
} else {
None
}
})
.is_some()
&& r.next().is_none()
}
pub struct DebugAsDisplay<T>(pub T);
impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub trait MakeMutSlice {
type Element: Clone;
fn make_mut_slice(&mut self) -> &mut [Self::Element];
}
impl<T: Clone> MakeMutSlice for Arc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Arc::get_mut(self).is_none() {
*self = Arc::<[T]>::from(&**self);
}
Arc::get_mut(self).unwrap()
}
}
impl<T: Clone> MakeMutSlice for Rc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Rc::get_mut(self).is_none() {
*self = Rc::<[T]>::from(&**self);
}
Rc::get_mut(self).unwrap()
}
}
pub struct DebugAsRawString<'a>(pub &'a str);
impl fmt::Debug for DebugAsRawString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pounds = self
.0
.split_inclusive('"')
.skip(1)
.map(|v| v.len() - v.trim_start_matches('#').len())
.max()
.map(|v| v + 1)
.unwrap_or(0);
f.write_char('r')?;
for _ in 0..pounds {
f.write_char('#')?;
}
f.write_char('"')?;
f.write_str(self.0)?;
f.write_char('"')?;
for _ in 0..pounds {
f.write_char('#')?;
}
Ok(())
}
}
pub fn interned_bit(v: bool) -> Interned<BitSlice> {
static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new();
// use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
}
#[derive(Copy, Clone, Debug)]
pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice);
impl BitSliceWriteWithBase<'_> {
fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1);
// TODO: optimize
let mut buf = String::with_capacity(digit_count);
for digit_index in (0..digit_count).rev() {
let mut digit = 0;
let src = self
.0
.get(digit_index * BITS_PER_DIGIT..)
.unwrap_or_default();
let src = src.get(..BITS_PER_DIGIT).unwrap_or(src);
digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src);
let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap();
if UPPER_CASE {
ch = ch.to_ascii_uppercase();
}
buf.push(ch);
}
f.pad_integral(
true,
match BITS_PER_DIGIT {
1 => "0b",
3 => "0o",
4 => "0x",
_ => "",
},
&buf,
)
}
}
impl fmt::Binary for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<1, false>(f)
}
}
impl fmt::Octal for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<3, false>(f)
}
}
impl fmt::LowerHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, false>(f)
}
}
impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, true>(f)
}
}