1#![warn(missing_docs)]
2
3use core::{
4 fmt::{Display, self},
5 error::Error,
6 slice,
7};
8
9use crate::{
10 stream::*,
11 op,
12 core::*,
13};
14
15pub const VERSION_1_0: u32 = 0x00010000;
17pub const VERSION_1_1: u32 = 0x00010100;
19pub const VERSION_1_2: u32 = 0x00010200;
21pub const VERSION_1_3: u32 = 0x00010300;
23pub const VERSION_1_4: u32 = 0x00010400;
25pub const VERSION_1_5: u32 = 0x00010500;
27pub const VERSION_1_6: u32 = 0x00010600;
29
30#[derive(Debug)]
34pub enum ParseError {
35 EndOfStream,
37 InvalidMagicNumber(u32),
39 InvalidType {
41 expected: &'static str,
43 found: op::Code,
47 },
48 InvalidIdResult(op::IdRef),
50 ExpectedContextResultType,
54 InvalidIntLiteralWidth(u32),
56 InvalidFloatLiteralWidth(u32),
58 IdOutOfBounds {
60 bound: u32,
62 id: op::IdResult,
64 },
65 UnknownVariant {
67 kind: &'static str,
69 value: u32,
71 },
72 InvalidPairWordCount(u32),
74}
75
76#[derive(Clone)]
77struct ResultStorage<'a> {
78 data: Vec<Option<InstructionStream<'a>>>,
79}
80
81impl<'a> ResultStorage<'a>
82{
83
84 #[inline]
85 fn new(bound: u32) -> Self {
86 Self {
87 data: vec![None; bound as usize],
88 }
89 }
90
91 #[inline]
92 fn insert(&mut self,
93 id: op::IdResult,
94 mut stream: InstructionStream<'a>
95 ) -> ParseResult<()>
96 {
97 stream.reset();
98 let idx = id.0 as usize;
99 if idx >= self.data.len() {
100 return Err(ParseError::IdOutOfBounds {
101 bound: self.data.len() as u32,
102 id
103 })
104 }
105 self.data[idx] = Some(stream);
106 Ok(())
107 }
108
109 #[inline]
110 fn get(&self, id: op::IdRef) -> Option<InstructionStream<'a>> {
111 self.data.get(id.0 as usize)
112 .and_then(|&value| value)
113 }
114}
115
116#[derive(Clone)]
118pub struct Module<'a> {
119 stream: Stream<'a>,
120 results: ResultStorage<'a>,
121}
122
123pub type ParseResult<T> = Result<T, ParseError>;
125
126impl<'a> Module<'a> {
127
128 #[inline]
130 pub fn new(spirv: &'a [u32]) -> ParseResult<Self> {
131 let stream = Stream::new(spirv)?;
132 Ok(Self {
133 results: ResultStorage::new(stream.bound()),
134 stream,
135 })
136 }
137
138 #[inline]
140 pub fn version(&self) -> u32 {
141 self.stream.version()
142 }
143
144 #[inline]
146 pub fn parse_next(&mut self) -> ParseResult<bool> {
147 let Some(mut instruction) = self.stream
148 .next()
149 else {
150 return Ok(false)
151 };
152 let inst_info = op::INST_INFOS[instruction.code().0 as usize];
153 if inst_info.has_id_result {
154 if inst_info.has_id_result_type {
155 let _ = instruction.read()?;
156 }
157 let id = op::IdResult(instruction.read()?);
158 self.results.insert(
159 id,
160 instruction,
161 )?;
162 }
163 Ok(true)
164 }
165
166 #[inline]
168 pub fn parse_full(&mut self) -> ParseResult<()> {
169 while self.parse_next()? {}
170 Ok(())
171 }
172
173 #[inline]
177 pub fn all_instructions(&self) -> impl Iterator<Item = InstructionStream<'a>> {
178 let mut stream = self.stream;
179 stream.reset();
180 stream
181 }
182
183 #[inline]
187 pub fn remaining_instructions(&self) -> impl Iterator<Item = InstructionStream<'a>> {
188 self.stream
189 }
190
191 #[inline]
195 pub fn results(&self) -> impl Iterator<Item = (op::IdRef, InstructionStream<'a>)> {
196 self.results.data
197 .iter()
198 .enumerate()
199 .filter_map(|(i, result)| {
200 result
201 .map(|result| (op::IdRef::from_word(i as u32), result))
202 })
203 }
204
205 #[inline]
207 pub fn get_result(&self, id: op::IdRef) -> Option<InstructionStream<'a>> {
208 self.results.get(id)
209 }
210}
211
212#[derive(Default, Clone, Copy)]
214pub struct ParseContext {
215 pub result_type: Option<op::IdResultType>,
219}
220
221pub trait WordExt<'a>: Word {
223
224 #[inline]
226 fn parse_one(
227 stream: &mut InstructionStream<'a>,
228 ) -> ParseResult<Self> {
229 Ok(Self::from_word(stream.read()?))
230 }
231
232 #[inline]
234 fn parse_optional(
235 stream: &mut InstructionStream<'a>,
236 ) -> ParseResult<Option<Self>> {
237 if stream.is_eos() {
238 Ok(None)
239 } else {
240 Ok(Some(Self::from_word(stream.read()?)))
241 }
242 }
243
244 #[inline]
246 fn parse_eos(
247 stream: &mut InstructionStream<'a>,
248 ) -> ParseResult<&'a [Self]> {
249 let words = stream.read_words(None)?;
250 Ok(unsafe {
251 slice::from_raw_parts(
252 words.as_ptr().cast(),
253 words.len(),
254 )
255 })
256 }
257}
258
259impl<'a, T: Word> WordExt<'a> for T {}
260
261impl<'a> CompilerStr<'a> {
262
263 #[inline]
265 pub fn parse_one(
266 stream: &mut InstructionStream<'a>,
267 ) -> ParseResult<Self> {
268 stream.read_string()
269 }
270
271 #[inline]
273 pub fn parse_optional(
274 stream: &mut InstructionStream<'a>,
275 ) -> ParseResult<Option<Self>> {
276 if stream.is_eos() {
277 Ok(None)
278 } else {
279 Ok(Some(stream.read_string()?))
280 }
281 }
282}
283
284impl Display for ParseError {
285
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match self {
288 Self::EndOfStream => write!(f, "end of stream"),
289 Self::InvalidMagicNumber(num) => write!(f, "invalid magic number {num}, expected 0x07230203"),
290 Self::InvalidType { expected, found } => write!(f,
291 "invalid type; expected {expected}, found {found}",
292 ),
293 Self::InvalidIdResult(id) => write!(f,
294 "invalid id result {id}",
295 ),
296 Self::ExpectedContextResultType => write!(f,
297 "expected context result type, found None",
298 ),
299 Self::InvalidIntLiteralWidth(width) => write!(f,
300 "invalid int literal width {width}",
301 ),
302 Self::InvalidFloatLiteralWidth(width) => write!(f,
303 "invalid float literal width {width}",
304 ),
305 Self::IdOutOfBounds { bound, id } => write!(f,
306 "Id {id} is out of the set bound {bound}",
307 ),
308 Self::UnknownVariant { kind, value } => write!(f,
309 "unknown {kind} variant {value}",
310 ),
311 Self::InvalidPairWordCount(count) => write!(f,
312 "invalid pair word count {count}: count is not a multiple of 2"
313 ),
314 }
315 }
316}
317
318impl Error for ParseError {}