Skip to main content

vyre_libs/parsing/c/parse/vast/
ref_decode_err.rs

1//! Explicit CPU oracle VAST construction and decode errors.
2//!
3//! Production VAST construction must use `c11_build_vast_nodes`. The byte
4//! decode helpers remain shared by oracle-only classify/typedef/expression
5//! fixtures so malformed parity inputs fail with actionable diagnostics.
6
7#![allow(missing_docs)] // Internal oracle helpers are documented at the owning module boundary.
8use crate::parsing::c::lex::tokens::*;
9
10use super::expr_shape::*;
11use super::ref_expr_shape::*;
12use super::*;
13
14#[cfg(any(test, feature = "cpu-parity"))]
15pub fn reference_c11_build_vast_nodes(
16    tok_types: &[u32],
17    tok_starts: &[u32],
18    tok_lens: &[u32],
19) -> Vec<u8> {
20    let n = tok_types.len().min(tok_starts.len()).min(tok_lens.len());
21    let mut parent = vec![SENTINEL; n];
22    let mut first_child = vec![SENTINEL; n];
23    let mut next_sibling = vec![SENTINEL; n];
24    let mut previous_sibling = vec![SENTINEL; n];
25    let mut stack: Vec<u32> = Vec::new();
26    let mut last_child: Vec<Option<u32>> = vec![None; n];
27    let mut root_last: Option<u32> = None;
28
29    for i in 0..n {
30        let parent_idx = stack.last().copied().unwrap_or(SENTINEL);
31        parent[i] = parent_idx;
32
33        if let Some(previous) = if parent_idx == SENTINEL {
34            root_last
35        } else {
36            last_child[parent_idx as usize]
37        } {
38            previous_sibling[i] = previous;
39            next_sibling[previous as usize] = i as u32;
40        } else if parent_idx != SENTINEL {
41            first_child[parent_idx as usize] = i as u32;
42        }
43
44        if parent_idx == SENTINEL {
45            root_last = Some(i as u32);
46        } else {
47            last_child[parent_idx as usize] = Some(i as u32);
48        }
49
50        match tok_types[i] {
51            TOK_LPAREN | TOK_LBRACE | TOK_LBRACKET => stack.push(i as u32),
52            TOK_RPAREN => pop_matching(&mut stack, tok_types, TOK_LPAREN),
53            TOK_RBRACE => pop_matching(&mut stack, tok_types, TOK_LBRACE),
54            TOK_RBRACKET => pop_matching(&mut stack, tok_types, TOK_LBRACKET),
55            _ => {}
56        }
57    }
58
59    let mut rows = Vec::with_capacity(n.saturating_mul(VAST_NODE_STRIDE_U32 as usize));
60    for i in 0..n {
61        rows.extend_from_slice(&[
62            tok_types[i],
63            parent[i],
64            first_child[i],
65            next_sibling[i],
66            previous_sibling[i],
67            tok_starts[i],
68            tok_lens[i],
69            0,
70            0,
71            0,
72        ]);
73    }
74    u32_words_to_bytes(&rows)
75}
76
77/// Malformed byte input for C VAST CPU oracle decoding.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum CReferenceDecodeError {
80    /// Input byte length is not a whole number of `u32` words.
81    MisalignedBytes {
82        /// Actual byte length.
83        len: usize,
84    },
85    /// Input word count is not a whole number of VAST rows.
86    PartialVastRow {
87        /// Actual decoded word count.
88        words: usize,
89        /// Required row stride.
90        stride: usize,
91    },
92    /// Two VAST streams that must describe the same node set have
93    /// different row counts.
94    MismatchedVastRows {
95        /// Row count in the raw VAST stream.
96        raw_rows: usize,
97        /// Row count in the typed VAST stream.
98        typed_rows: usize,
99    },
100}
101
102impl std::fmt::Display for CReferenceDecodeError {
103    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self {
105            Self::MisalignedBytes { len } => write!(
106                formatter,
107                "C VAST byte input has {len} bytes, which is not 4-byte aligned. Fix: pass complete u32 rows to the C VAST reference oracle."
108            ),
109            Self::PartialVastRow { words, stride } => write!(
110                formatter,
111                "C VAST word input has {words} words, which is not a multiple of row stride {stride}. Fix: pass complete C VAST rows to the reference oracle."
112            ),
113            Self::MismatchedVastRows {
114                raw_rows,
115                typed_rows,
116            } => write!(
117                formatter,
118                "C VAST reference oracle received {raw_rows} raw rows but {typed_rows} typed rows. Fix: pass matching raw and typed VAST streams from the same translation unit."
119            ),
120        }
121    }
122}
123
124impl std::error::Error for CReferenceDecodeError {}
125
126fn try_u32_words_from_bytes(bytes: &[u8]) -> Result<Vec<u32>, CReferenceDecodeError> {
127    if bytes.len() % 4 != 0 {
128        return Err(CReferenceDecodeError::MisalignedBytes { len: bytes.len() });
129    }
130    Ok(vyre_primitives::wire::decode_u32_le_bytes_all(bytes))
131}
132
133pub(super) fn try_vast_words_from_bytes(bytes: &[u8]) -> Result<Vec<u32>, CReferenceDecodeError> {
134    let words = try_u32_words_from_bytes(bytes)?;
135    if words.len() % VAST_NODE_STRIDE_U32 as usize != 0 {
136        return Err(CReferenceDecodeError::PartialVastRow {
137            words: words.len(),
138            stride: VAST_NODE_STRIDE_U32 as usize,
139        });
140    }
141    Ok(words)
142}