Compare commits
2 commits
305805d237
...
be025c14ca
Author | SHA1 | Date | |
---|---|---|---|
Jacob Lifshay | be025c14ca | ||
Jacob Lifshay | 14a9c23697 |
|
@ -114,7 +114,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// based on https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target
|
/// based on [LLVM Circt's recommended lowering options
|
||||||
|
/// ](https://circt.llvm.org/docs/VerilogGeneration/#recommended-loweringoptions-by-target)
|
||||||
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum VerilogDialect {
|
pub enum VerilogDialect {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
61
crates/fayalite/src/util/const_bool.rs
Normal file
61
crates/fayalite/src/util/const_bool.rs
Normal 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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
crates/fayalite/src/util/const_cmp.rs
Normal file
51
crates/fayalite/src/util/const_cmp.rs
Normal 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
|
||||||
|
}
|
157
crates/fayalite/src/util/misc.rs
Normal file
157
crates/fayalite/src/util/misc.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue