fayalite 0.2.0

Hardware Description Language embedded in Rust, using FIRRTL's semantics
Documentation
// 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)
    }
}