use alloc::borrow::ToOwned;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter};
use crate::program::constants::{
ISAE_SEGMENT_MAX_COUNT, ISAE_SEGMENT_MAX_LEN, ISA_ID_ALLOWED_CHARS, ISA_ID_ALLOWED_FIRST_CHAR,
ISA_ID_MAX_LEN, ISA_ID_MIN_LEN, LIBS_SEGMENT_MAX_COUNT,
};
use crate::program::{LibId, LibSite};
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
#[cfg_attr(feature = "std", derive(Error))]
#[display(doc_comments)]
pub enum SegmentError {
CodeSegmentTooLarge(usize),
DataSegmentTooLarge(usize),
#[display(inner)]
#[from]
IsaeSegment(IsaSegError),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[cfg_attr(feature = "std", derive(Error))]
#[display(doc_comments)]
pub enum IsaSegError {
SegmentTooLarge(usize),
SegmentTooManyExt(usize),
IsaIdWrongLength(String),
IsaIdWrongSymbols(String),
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
#[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct IsaSeg(BTreeSet<String>);
impl IsaSeg {
#[inline]
pub fn iter(&self) -> ::alloc::collections::btree_set::Iter<String> { self.0.iter() }
}
impl<'a> IntoIterator for &'a IsaSeg {
type Item = &'a String;
type IntoIter = ::alloc::collections::btree_set::Iter<'a, String>;
#[inline]
fn into_iter(self) -> Self::IntoIter { self.0.iter() }
}
impl IsaSeg {
#[inline]
pub fn with(s: impl AsRef<str>) -> Result<Self, IsaSegError> {
IsaSeg::from_iter(s.as_ref().split(' '))
}
#[allow(clippy::should_implement_trait)]
pub fn from_iter(
source: impl IntoIterator<Item = impl AsRef<str>>,
) -> Result<Self, IsaSegError> {
let isa_codes =
source.into_iter().map(|s| s.as_ref().trim().to_owned()).collect::<BTreeSet<String>>();
if isa_codes.len() > ISAE_SEGMENT_MAX_COUNT {
return Err(IsaSegError::SegmentTooManyExt(isa_codes.len()));
}
let mut total_len = 0usize;
for isae in &isa_codes {
if !(ISA_ID_MIN_LEN..=ISA_ID_MAX_LEN).contains(&isae.len()) {
return Err(IsaSegError::IsaIdWrongLength(isae.to_owned()));
}
if isae.chars().any(|ch| !ISA_ID_ALLOWED_CHARS.contains(&ch))
|| isae
.chars()
.next()
.map(|ch| !ISA_ID_ALLOWED_FIRST_CHAR.contains(&ch))
.unwrap_or_default()
{
return Err(IsaSegError::IsaIdWrongSymbols(isae.to_owned()));
}
total_len += isae.len();
}
if total_len > ISAE_SEGMENT_MAX_LEN {
return Err(IsaSegError::SegmentTooLarge(total_len));
}
Ok(IsaSeg(isa_codes))
}
#[inline]
pub fn count(&self) -> u8 { self.0.len() as u8 }
#[inline]
pub fn at(&self, index: u8) -> Option<String> {
self.0.iter().enumerate().nth(index as usize).map(|(_, isa)| isa).cloned()
}
}
impl Display for IsaSeg {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0.iter().cloned().collect::<Vec<_>>().join(" "))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Display)]
#[cfg_attr(feature = "std", derive(Error))]
#[display(doc_comments)]
pub struct LibSegOverflow;
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
#[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct LibSeg {
set: BTreeSet<LibId>,
table: BTreeMap<u8, LibId>,
}
impl LibSeg {
#[inline]
pub fn iter(&self) -> ::alloc::collections::btree_set::Iter<LibId> { self.into_iter() }
}
impl<'a> IntoIterator for &'a LibSeg {
type Item = &'a LibId;
type IntoIter = ::alloc::collections::btree_set::Iter<'a, LibId>;
#[inline]
fn into_iter(self) -> Self::IntoIter { self.set.iter() }
}
impl LibSeg {
pub fn with(source: impl IntoIterator<Item = LibSite>) -> Result<Self, LibSegOverflow> {
LibSeg::from_iter(source.into_iter().map(|site| site.lib))
}
#[allow(clippy::should_implement_trait)]
pub fn from_iter(source: impl IntoIterator<Item = LibId>) -> Result<Self, LibSegOverflow> {
let set = source.into_iter().collect::<BTreeSet<LibId>>();
if set.len() > LIBS_SEGMENT_MAX_COUNT {
return Err(LibSegOverflow);
}
let table = set.iter().enumerate().map(|(index, id)| (index as u8, *id)).collect();
Ok(LibSeg { set, table })
}
#[inline]
pub fn count(&self) -> u8 { self.set.len() as u8 }
#[inline]
pub fn at(&self, index: u8) -> Option<LibId> { self.table.get(&index).copied() }
#[inline]
pub fn index(&self, lib: LibId) -> Option<u8> {
self.set.iter().position(|l| *l == lib).map(|i| i as u8)
}
#[inline]
pub fn add_lib(&mut self, id: LibId) -> Result<bool, LibSegOverflow> {
if self.set.len() >= LIBS_SEGMENT_MAX_COUNT {
Err(LibSegOverflow)
} else if self.index(id).is_some() {
Ok(true)
} else {
self.set.insert(id);
let pos = self.index(id).expect("library inserted into a set is absent in the set");
self.table.insert(pos, id);
Ok(false)
}
}
}
impl Display for LibSeg {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.set.iter().enumerate().try_for_each(|(line, lib)| {
writeln!(
f,
"{:>2$}{}",
"",
lib,
if line == 0 { 0 } else { f.width().unwrap_or_default() }
)
})
}
}