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