325 lines
10 KiB
Rust
325 lines
10 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
use fayalite::{expr::ops::ExprIndex, int::UIntInRangeInclusiveType, prelude::*};
|
|
use std::fmt;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ArrayVecFullError<V, I: Iterator> {
|
|
pub value: V,
|
|
pub rest: std::iter::Chain<std::iter::Once<I::Item>, I>,
|
|
}
|
|
|
|
impl<V, I: Iterator> fmt::Display for ArrayVecFullError<V, I> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "ArrayVec is full")
|
|
}
|
|
}
|
|
|
|
impl<V: fmt::Debug, I: Iterator<Item: fmt::Debug> + fmt::Debug> std::error::Error
|
|
for ArrayVecFullError<V, I>
|
|
{
|
|
}
|
|
|
|
#[hdl]
|
|
pub type Length<Max: Size> = UIntInRangeInclusiveType<ConstUsize<0>, Max>;
|
|
|
|
/// like [`std::vec::Vec`], except with a [`Expr`] for [`len()`][`Self::len()`] and a fixed capacity
|
|
#[hdl]
|
|
pub struct ArrayVec<T: Type, N: Size> {
|
|
elements: ArrayType<T, N>,
|
|
len: Length<N>,
|
|
}
|
|
|
|
impl<T: Type, N: Size> ArrayVec<T, N> {
|
|
#[hdl]
|
|
pub fn new(self) -> Expr<Self> {
|
|
#[hdl]
|
|
ArrayVec {
|
|
elements: self.elements.uninit(),
|
|
len: 0u8.cast_to(self.len),
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn new_sim(self, uninit_element: impl ToSimValueWithType<T>) -> SimValue<Self> {
|
|
let uninit_element = uninit_element.into_sim_value_with_type(self.element());
|
|
#[hdl(sim)]
|
|
ArrayVec::<_, _> {
|
|
elements: SimValue::from_array_elements(
|
|
self.elements,
|
|
(0..self.elements.len()).map(|_| uninit_element.clone()),
|
|
),
|
|
len: 0u8.cast_to(self.len),
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn new_full_sim(
|
|
self,
|
|
elements: impl ToSimValueWithType<ArrayType<T, N>>,
|
|
) -> SimValue<Self> {
|
|
let elements = elements.to_sim_value_with_type(self.elements);
|
|
#[hdl(sim)]
|
|
Self {
|
|
elements,
|
|
len: self.elements.len().to_sim_value_with_type(self.len),
|
|
}
|
|
}
|
|
pub fn from_iter_sim<I: IntoIterator<Item: ToSimValueWithType<T>>>(
|
|
self,
|
|
uninit_element: impl ToSimValueWithType<T>,
|
|
iter: I,
|
|
) -> Result<SimValue<Self>, ArrayVecFullError<SimValue<Self>, I::IntoIter>> {
|
|
let mut value = Self::new_sim(self, uninit_element);
|
|
let element = self.element();
|
|
let mut iter = iter.into_iter();
|
|
for i in 0..self.capacity() {
|
|
let Some(v) = iter.next() else {
|
|
break;
|
|
};
|
|
value.elements[i] = v.into_sim_value_with_type(element);
|
|
*value.len = i + 1;
|
|
}
|
|
if let Some(extra) = iter.next() {
|
|
Err(ArrayVecFullError {
|
|
value,
|
|
rest: std::iter::once(extra).chain(iter),
|
|
})
|
|
} else {
|
|
Ok(value)
|
|
}
|
|
}
|
|
pub fn element(self) -> T {
|
|
self.elements.element()
|
|
}
|
|
pub fn elements_ty(self) -> ArrayType<T, N> {
|
|
self.elements
|
|
}
|
|
pub fn elements_unchecked(this: impl ToExpr<Type = Self>) -> Expr<ArrayType<T, N>> {
|
|
this.to_expr().elements
|
|
}
|
|
#[hdl]
|
|
pub fn from_parts_unchecked(
|
|
elements: impl ToExpr<Type = ArrayType<T, N>>,
|
|
len: impl ToExpr<Type = Length<N>>,
|
|
) -> Expr<Self> {
|
|
let elements = elements.to_expr();
|
|
let len = len.to_expr();
|
|
assert_eq!(
|
|
Length[N::from_usize(elements.ty().len())],
|
|
len.ty(),
|
|
"len type mismatch",
|
|
);
|
|
#[hdl]
|
|
Self { elements, len }
|
|
}
|
|
pub fn len_ty(self) -> Length<N> {
|
|
self.len
|
|
}
|
|
pub fn len(this: impl ToExpr<Type = Self>) -> Expr<Length<N>> {
|
|
this.to_expr().len
|
|
}
|
|
pub fn len_sim(this: &SimValue<Self>) -> &SimValue<Length<N>> {
|
|
&this.len
|
|
}
|
|
pub fn is_empty(this: impl ToExpr<Type = Self>) -> Expr<Bool> {
|
|
let len = Self::len(this);
|
|
len.cmp_eq(0u8)
|
|
}
|
|
pub fn capacity(self) -> usize {
|
|
self.elements.len()
|
|
}
|
|
pub fn get_unchecked<Idx>(this: impl ToExpr<Type = Self>, index: Idx) -> Expr<T>
|
|
where
|
|
ArrayType<T, N>: ExprIndex<Idx, Output = T>,
|
|
{
|
|
this.to_expr().elements[index]
|
|
}
|
|
#[hdl]
|
|
pub fn for_each(this: impl ToExpr<Type = Self>, mut f: impl FnMut(usize, Expr<T>)) {
|
|
let this = this.to_expr();
|
|
for (index, element) in this.elements.into_iter().enumerate() {
|
|
#[hdl]
|
|
if index.cmp_lt(this.len) {
|
|
f(index, element);
|
|
}
|
|
}
|
|
}
|
|
pub fn elements_sim_ref(this: &SimValue<Self>) -> &[SimValue<T>] {
|
|
&this.elements[..*this.len]
|
|
}
|
|
pub fn elements_sim_mut(this: &mut SimValue<Self>) -> &mut [SimValue<T>] {
|
|
let len = *this.len;
|
|
&mut this.elements[..len]
|
|
}
|
|
#[hdl]
|
|
pub async fn async_for_each_sim(
|
|
this: impl ToSimValue<Type = Self>,
|
|
mut f: impl AsyncFnMut(usize, SimValue<T>),
|
|
) {
|
|
#[hdl(sim)]
|
|
let ArrayVec::<_, _> { elements, len } = this.into_sim_value();
|
|
for (index, element) in elements.into_iter().enumerate() {
|
|
if index.cmp_lt(*len) {
|
|
f(index, element).await;
|
|
}
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub async fn async_for_each_sim_ref<'a>(
|
|
this: &'a SimValue<Self>,
|
|
mut f: impl AsyncFnMut(usize, &'a SimValue<T>),
|
|
) {
|
|
#[hdl(sim)]
|
|
let ArrayVec::<_, _> { elements, len } = this;
|
|
for (index, element) in elements.iter().enumerate() {
|
|
if index.cmp_lt(**len) {
|
|
f(index, element).await;
|
|
}
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub async fn async_for_each_sim_mut<'a>(
|
|
this: &'a mut SimValue<Self>,
|
|
mut f: impl AsyncFnMut(usize, &'a mut SimValue<T>),
|
|
) {
|
|
#[hdl(sim)]
|
|
let ArrayVec::<_, _> { elements, len } = this;
|
|
for (index, element) in elements.iter_mut().enumerate() {
|
|
if index.cmp_lt(**len) {
|
|
f(index, element).await;
|
|
}
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn try_push_sim(
|
|
this: &mut SimValue<Self>,
|
|
value: impl ToSimValueWithType<T>,
|
|
) -> Result<(), SimValue<T>> {
|
|
let value = value.into_sim_value_with_type(this.ty().element());
|
|
let capacity = this.ty().capacity();
|
|
#[hdl(sim)]
|
|
let ArrayVec::<_, _> { elements, len } = this;
|
|
if **len < capacity {
|
|
elements[**len] = value;
|
|
**len += 1;
|
|
Ok(())
|
|
} else {
|
|
Err(value)
|
|
}
|
|
}
|
|
pub fn truncate_sim(this: &mut SimValue<Self>, len: usize) {
|
|
*this.len = len.min(*this.len);
|
|
}
|
|
pub fn mapped_ty<U: Type>(self, new_element_ty: U) -> ArrayVec<U, N> {
|
|
ArrayVec {
|
|
elements: ArrayType[new_element_ty][N::from_usize(self.elements.len())],
|
|
len: self.len,
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn map<U: Type>(
|
|
this: impl ToExpr<Type = Self>,
|
|
new_element_ty: U,
|
|
mut f: impl FnMut(usize, Expr<T>) -> Expr<U>,
|
|
) -> Expr<ArrayVec<U, N>> {
|
|
let this = this.to_expr();
|
|
#[hdl]
|
|
let mapped_array_vec = wire(this.ty().mapped_ty(new_element_ty));
|
|
connect(mapped_array_vec.len, this.len);
|
|
connect(
|
|
mapped_array_vec.elements,
|
|
mapped_array_vec.ty().elements.uninit(),
|
|
);
|
|
Self::for_each(this, |index, element| {
|
|
connect(mapped_array_vec[index], f(index, element));
|
|
});
|
|
mapped_array_vec
|
|
}
|
|
#[hdl]
|
|
pub fn map_sim<U: Type>(
|
|
this: impl ToSimValue<Type = Self>,
|
|
uninit_element: impl ToSimValue<Type = U>,
|
|
mut f: impl FnMut(usize, SimValue<T>) -> SimValue<U>,
|
|
) -> SimValue<ArrayVec<U, N>> {
|
|
let this = this.into_sim_value();
|
|
let uninit_element = uninit_element.into_sim_value();
|
|
let ty = this.ty().mapped_ty(uninit_element.ty());
|
|
#[hdl(sim)]
|
|
let Self { elements, len } = this;
|
|
#[hdl(sim)]
|
|
ArrayVec::<_, _> {
|
|
elements: SimValue::from_array_elements(
|
|
ty.elements,
|
|
SimValue::into_value(elements)
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(index, element)| {
|
|
if index < *len {
|
|
f(index, element)
|
|
} else {
|
|
uninit_element.clone()
|
|
}
|
|
}),
|
|
),
|
|
len,
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn as_array_of_options(this: impl ToExpr<Type = Self>) -> Expr<ArrayType<HdlOption<T>, N>> {
|
|
let this = this.to_expr();
|
|
#[hdl]
|
|
let array_vec_as_array_of_options =
|
|
wire(ArrayType[HdlOption[this.ty().element()]][N::from_usize(this.ty().capacity())]);
|
|
for element in array_vec_as_array_of_options {
|
|
connect(element, element.ty().HdlNone());
|
|
}
|
|
Self::for_each(this, |index, element| {
|
|
connect(array_vec_as_array_of_options[index], HdlSome(element))
|
|
});
|
|
array_vec_as_array_of_options
|
|
}
|
|
}
|
|
|
|
impl<T: Type, N: Size, Idx, IdxWidth: Size> ExprIndex<Idx> for ArrayVec<T, N>
|
|
where
|
|
ArrayType<T, N>: ExprIndex<Idx, Output = T>,
|
|
Idx: ToExpr<Type = UIntType<IdxWidth>>,
|
|
{
|
|
type Output = T;
|
|
|
|
fn expr_index(this: &Expr<Self>, index: Idx) -> &Expr<Self::Output> {
|
|
// TODO: add assert that index is in-bounds
|
|
<ArrayType<T, N> as ExprIndex<Idx>>::expr_index(&this.elements, index)
|
|
}
|
|
}
|
|
|
|
impl<T: Type> ArrayVec<T, ConstUsize<1>> {
|
|
#[hdl]
|
|
pub fn from_opt_sim(
|
|
opt: impl ToSimValue<Type = HdlOption<T>>,
|
|
uninit_element: impl ToSimValueWithType<T>,
|
|
) -> SimValue<Self> {
|
|
let opt = opt.into_sim_value();
|
|
let ty = ArrayVec[opt.ty().HdlSome][ConstUsize];
|
|
#[hdl(sim)]
|
|
match opt {
|
|
HdlSome(v) => ty.new_full_sim([v]),
|
|
HdlNone => ty.new_sim(uninit_element),
|
|
}
|
|
}
|
|
#[hdl]
|
|
pub fn into_opt_sim(this: impl ToSimValue<Type = Self>) -> SimValue<HdlOption<T>> {
|
|
let this = this.into_sim_value();
|
|
#[hdl(sim)]
|
|
let Self { elements, len } = this;
|
|
let [element] = SimValue::into_value(elements);
|
|
let ty = HdlOption[element.ty()];
|
|
if *len == 0 {
|
|
#[hdl(sim)]
|
|
ty.HdlNone()
|
|
} else {
|
|
#[hdl(sim)]
|
|
ty.HdlSome(element)
|
|
}
|
|
}
|
|
}
|