#![allow(clippy::copy_iterator)]
use mf2_model::MarkupKind;
use crate::bytes::{Cur, u32_at};
use crate::format::tag;
use crate::reader::{Catalog, StrRef};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Malformed;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum VarRef {
External(u32),
Local(u32),
}
impl VarRef {
#[inline]
fn from_raw(r: u32) -> Self {
if r & 1 == 0 {
VarRef::External(r >> 1)
} else {
VarRef::Local(r >> 1)
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Operand {
Literal(StrRef),
Variable(VarRef),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum KeyView {
CatchAll,
Literal(StrRef),
}
#[derive(Clone, Copy)]
pub struct Names<'a> {
table: &'a [u8],
externals: u32,
locals: u32,
}
impl<'a> Names<'a> {
pub const EMPTY: Names<'static> = Names {
table: &[],
externals: 0,
locals: 0,
};
pub(crate) fn at(names: &'a [u8], names_ref: u32) -> Option<Names<'a>> {
let Some(off) = names_ref.checked_sub(1) else {
return Some(Names {
table: &[],
externals: 0,
locals: 0,
});
};
let mut c = Cur::new(names, off as usize);
let externals = c.varint()?;
let locals = c.varint()?;
let n = (externals as usize).checked_add(locals as usize)?;
let table = c.take(n.checked_mul(4)?)?;
Some(Names {
table,
externals,
locals,
})
}
pub fn external_count(&self) -> u32 {
self.externals
}
pub fn local_count(&self) -> u32 {
self.locals
}
pub fn external(&self, slot: u32) -> Option<StrRef> {
if slot >= self.externals {
return None;
}
u32_at(self.table, (slot as usize).checked_mul(4)?).map(StrRef)
}
pub fn local(&self, index: u32) -> Option<StrRef> {
if index >= self.locals {
return None;
}
let i = (self.externals as usize).checked_add(index as usize)?;
u32_at(self.table, i.checked_mul(4)?).map(StrRef)
}
pub fn var(&self, v: VarRef) -> Option<StrRef> {
match v {
VarRef::External(s) => self.external(s),
VarRef::Local(i) => self.local(i),
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct Head {
pub(crate) names_ref: u32,
#[cfg_attr(not(feature = "decode"), allow(dead_code))]
pub(crate) cold: Option<u32>,
pub(crate) decl_count: u32,
pub(crate) decls_at: usize,
}
#[derive(Clone, Copy)]
pub struct MsgView<'a> {
cat: &'a Catalog,
at: usize,
select: bool,
}
impl<'a> MsgView<'a> {
pub(crate) fn new(cat: &'a Catalog, at: usize, select: bool) -> Self {
MsgView { cat, at, select }
}
#[inline]
fn bytes(&self) -> &'a [u8] {
self.cat.messages.of(self.cat.as_bytes())
}
pub fn is_select(&self) -> bool {
self.select
}
pub fn catalog(&self) -> &'a Catalog {
self.cat
}
pub(crate) fn head(&self) -> Result<Head, Malformed> {
let mut c = Cur::new(self.bytes(), self.at);
let names_ref = c.varint().ok_or(Malformed)?;
let decls = c.varint().ok_or(Malformed)?;
let cold = if decls & 1 == 1 {
Some(c.varint().ok_or(Malformed)?)
} else {
None
};
Ok(Head {
names_ref,
cold,
decl_count: decls >> 1,
decls_at: c.pos(),
})
}
pub(crate) fn try_names(&self) -> Option<Names<'a>> {
let head = self.head().ok()?;
Names::at(self.cat.names.of(self.cat.as_bytes()), head.names_ref)
}
pub fn names(&self) -> Names<'a> {
self.try_names().unwrap_or(Names::EMPTY)
}
pub fn declarations(&self) -> Declarations<'a> {
match self.head() {
Ok(h) => Declarations {
c: Cur::new(self.bytes(), h.decls_at),
left: h.decl_count,
locals: 0,
failed: false,
select: self.select,
},
Err(Malformed) => Declarations {
c: Cur::new(&[], 0),
left: 0,
locals: 0,
failed: true,
select: self.select,
},
}
}
pub fn body(&self) -> Result<Body<'a>, Malformed> {
self.declarations().body()
}
}
#[derive(Clone, Copy)]
pub enum Body<'a> {
Pattern(PatternView<'a>),
Select(SelectView<'a>),
}
#[derive(Clone, Copy)]
pub enum DeclView<'a> {
Input(ExprView<'a>),
Local {
index: u32,
expr: ExprView<'a>,
},
}
#[derive(Clone, Copy)]
pub struct Declarations<'a> {
c: Cur<'a>,
left: u32,
locals: u32,
failed: bool,
select: bool,
}
impl<'a> Declarations<'a> {
pub fn len(&self) -> u32 {
self.left
}
pub fn is_empty(&self) -> bool {
self.left == 0
}
fn step(&mut self) -> Result<DeclView<'a>, Malformed> {
let t = self.c.u8().ok_or(Malformed)?;
let local = t & tag::LOCAL != 0;
if t & tag::KIND_MASK != tag::EXPRESSION || t & !(tag::EXPRESSION_BITS | tag::LOCAL) != 0 {
return Err(Malformed);
}
let expr = expr(&mut self.c, t)?;
if local {
let index = self.locals;
self.locals = self.locals.checked_add(1).ok_or(Malformed)?;
Ok(DeclView::Local { index, expr })
} else if matches!(expr.operand, Some(Operand::Variable(_))) {
Ok(DeclView::Input(expr))
} else {
Err(Malformed)
}
}
pub fn body(mut self) -> Result<Body<'a>, Malformed> {
while self.left > 0 {
self.next().ok_or(Malformed)??;
}
if self.failed {
return Err(Malformed);
}
let mut c = self.c;
if !self.select {
return pattern(&mut c).map(Body::Pattern);
}
let nsel = c.varint().ok_or(Malformed)?;
let sel_at = c.pos();
for _ in 0..nsel {
c.varint().ok_or(Malformed)?;
}
let nvar = c.varint().ok_or(Malformed)?;
Ok(Body::Select(SelectView {
b: c.bytes(),
sel_at,
nsel,
var_at: c.pos(),
nvar,
}))
}
}
impl<'a> Iterator for Declarations<'a> {
type Item = Result<DeclView<'a>, Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed {
return None;
}
if self.left == 0 {
return None;
}
self.left -= 1;
let r = self.step();
if r.is_err() {
self.failed = true;
self.left = 0;
}
Some(r)
}
}
fn expr<'a>(c: &mut Cur<'a>, t: u8) -> Result<ExprView<'a>, Malformed> {
let has_fn = t & tag::FUNCTION != 0;
let operand = match (t & tag::OP_MASK) >> tag::OP_SHIFT {
tag::OP_NONE if has_fn => None,
tag::OP_LITERAL => Some(Operand::Literal(StrRef(c.varint().ok_or(Malformed)?))),
tag::OP_VARIABLE => Some(Operand::Variable(VarRef::from_raw(
c.varint().ok_or(Malformed)?,
))),
_ => return Err(Malformed),
};
let function = if has_fn {
let index = c.varint().ok_or(Malformed)?;
Some(FunctionView {
index,
options: options(c)?,
})
} else {
None
};
Ok(ExprView { operand, function })
}
fn options<'a>(c: &mut Cur<'a>) -> Result<OptionsView<'a>, Malformed> {
let n = c.varint().ok_or(Malformed)?;
let at = c.pos();
for _ in 0..n {
c.varint().ok_or(Malformed)?;
c.varint().ok_or(Malformed)?;
}
Ok(OptionsView {
c: Cur::new(c.bytes(), at),
left: n,
})
}
fn pattern<'a>(c: &mut Cur<'a>) -> Result<PatternView<'a>, Malformed> {
let n = c.varint().ok_or(Malformed)?;
Ok(PatternView {
b: c.bytes(),
at: c.pos(),
n,
})
}
#[derive(Clone, Copy)]
pub struct ExprView<'a> {
operand: Option<Operand>,
function: Option<FunctionView<'a>>,
}
impl<'a> ExprView<'a> {
pub fn operand(&self) -> Option<Operand> {
self.operand
}
pub fn function(&self) -> Option<FunctionView<'a>> {
self.function
}
}
#[derive(Clone, Copy)]
pub struct FunctionView<'a> {
index: u32,
options: OptionsView<'a>,
}
impl<'a> FunctionView<'a> {
pub fn index(&self) -> u32 {
self.index
}
pub fn options(&self) -> OptionsView<'a> {
self.options
}
}
#[derive(Clone, Copy)]
pub struct OptionsView<'a> {
c: Cur<'a>,
left: u32,
}
impl OptionsView<'_> {
const NONE: OptionsView<'static> = OptionsView {
c: Cur::new(&[], 0),
left: 0,
};
pub fn len(&self) -> u32 {
self.left
}
pub fn is_empty(&self) -> bool {
self.left == 0
}
}
impl Iterator for OptionsView<'_> {
type Item = Result<(StrRef, Operand), Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
self.left -= 1;
let r = (|| {
let name = StrRef(self.c.varint()?);
let v = self.c.varint()?;
let value = if v & 1 == 0 {
Operand::Literal(StrRef(v >> 1))
} else {
Operand::Variable(VarRef::from_raw(v >> 1))
};
Some((name, value))
})();
if r.is_none() {
self.left = 0;
}
Some(r.ok_or(Malformed))
}
}
#[derive(Clone, Copy)]
pub struct MarkupView<'a> {
kind: MarkupKind,
name: StrRef,
options: OptionsView<'a>,
}
impl<'a> MarkupView<'a> {
pub fn kind(&self) -> MarkupKind {
self.kind
}
pub fn name(&self) -> StrRef {
self.name
}
pub fn options(&self) -> OptionsView<'a> {
self.options
}
}
#[derive(Clone, Copy)]
pub enum PartView<'a> {
Text(StrRef),
Expression(ExprView<'a>),
Markup(MarkupView<'a>),
}
#[derive(Clone, Copy)]
pub struct PatternView<'a> {
b: &'a [u8],
at: usize,
n: u32,
}
impl<'a> PatternView<'a> {
pub fn len(&self) -> u32 {
self.n
}
pub fn is_empty(&self) -> bool {
self.n == 0
}
pub fn parts(&self) -> Parts<'a> {
Parts {
c: Cur::new(self.b, self.at),
left: self.n,
}
}
}
#[derive(Clone, Copy)]
pub struct Parts<'a> {
c: Cur<'a>,
left: u32,
}
impl<'a> Parts<'a> {
fn step(&mut self) -> Result<PartView<'a>, Malformed> {
let t = self.c.u8().ok_or(Malformed)?;
match t & tag::KIND_MASK {
tag::TEXT if t == tag::TEXT => {
Ok(PartView::Text(StrRef(self.c.varint().ok_or(Malformed)?)))
}
tag::EXPRESSION if t & !tag::EXPRESSION_BITS == 0 => {
expr(&mut self.c, t).map(PartView::Expression)
}
k @ (tag::OPEN | tag::STANDALONE | tag::CLOSE) if t & !tag::MARKUP_BITS == 0 => {
let kind = match k {
tag::OPEN => MarkupKind::Open,
tag::STANDALONE => MarkupKind::Standalone,
_ => MarkupKind::Close,
};
let name = StrRef(self.c.varint().ok_or(Malformed)?);
let options = if t & tag::OPTIONS != 0 {
options(&mut self.c)?
} else {
OptionsView::NONE
};
Ok(PartView::Markup(MarkupView {
kind,
name,
options,
}))
}
_ => Err(Malformed),
}
}
}
impl<'a> Iterator for Parts<'a> {
type Item = Result<PartView<'a>, Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
self.left -= 1;
let r = self.step();
if r.is_err() {
self.left = 0;
}
Some(r)
}
}
#[derive(Clone, Copy)]
pub struct SelectView<'a> {
b: &'a [u8],
sel_at: usize,
nsel: u32,
var_at: usize,
nvar: u32,
}
impl<'a> SelectView<'a> {
pub fn selectors(&self) -> Selectors<'a> {
Selectors {
c: Cur::new(self.b, self.sel_at),
left: self.nsel,
}
}
pub fn variants(&self) -> Variants<'a> {
Variants {
c: Cur::new(self.b, self.var_at),
left: self.nvar,
}
}
}
#[derive(Clone, Copy)]
pub struct Selectors<'a> {
c: Cur<'a>,
left: u32,
}
impl Selectors<'_> {
pub fn len(&self) -> u32 {
self.left
}
pub fn is_empty(&self) -> bool {
self.left == 0
}
}
impl Iterator for Selectors<'_> {
type Item = Result<VarRef, Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
self.left -= 1;
let r = self.c.varint().map(VarRef::from_raw);
if r.is_none() {
self.left = 0;
}
Some(r.ok_or(Malformed))
}
}
#[derive(Clone, Copy)]
pub struct VariantView<'a> {
keys: Keys<'a>,
pattern: PatternView<'a>,
}
impl<'a> VariantView<'a> {
pub fn keys(&self) -> Keys<'a> {
self.keys
}
pub fn pattern(&self) -> PatternView<'a> {
self.pattern
}
}
#[derive(Clone, Copy)]
pub struct Variants<'a> {
c: Cur<'a>,
left: u32,
}
impl<'a> Variants<'a> {
pub fn len(&self) -> u32 {
self.left
}
pub fn is_empty(&self) -> bool {
self.left == 0
}
fn step(&mut self) -> Option<VariantView<'a>> {
let nkeys = self.c.varint()?;
let keys_at = self.c.pos();
for _ in 0..nkeys {
self.c.varint()?;
}
let plen = self.c.len()?;
let mut p = Cur::new(self.c.take(plen)?, 0);
let pattern = pattern(&mut p).ok()?;
Some(VariantView {
keys: Keys {
c: Cur::new(self.c.bytes(), keys_at),
left: nkeys,
},
pattern,
})
}
}
impl<'a> Iterator for Variants<'a> {
type Item = Result<VariantView<'a>, Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
self.left -= 1;
let r = self.step();
if r.is_none() {
self.left = 0;
}
Some(r.ok_or(Malformed))
}
}
#[derive(Clone, Copy)]
pub struct Keys<'a> {
c: Cur<'a>,
left: u32,
}
impl Keys<'_> {
pub fn len(&self) -> u32 {
self.left
}
pub fn is_empty(&self) -> bool {
self.left == 0
}
}
impl Iterator for Keys<'_> {
type Item = Result<KeyView, Malformed>;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
self.left -= 1;
let r = self.c.varint().map(|k| match k.checked_sub(1) {
None => KeyView::CatchAll,
Some(r) => KeyView::Literal(StrRef(r)),
});
if r.is_none() {
self.left = 0;
}
Some(r.ok_or(Malformed))
}
}