Skip to main content

nox_spirv/
stream.rs

1//! SPIR-V streams.
2
3use crate::{
4    core::*,
5    ParseResult, ParseError,
6    op,
7};
8
9/// An iterator over [`instructions`][1] in SPIR-V code.
10///
11/// [1]: InstructionStream
12#[derive(Clone, Copy)]
13pub struct Stream<'a> {
14    spirv: &'a [u32],
15    version: u32,
16    bound: u32,
17    pos: usize,
18}
19
20impl<'a> Stream<'a> {
21
22    /// Creates a new stream from SPIR-V words.
23    #[inline(always)]
24    pub fn new(spirv: &'a [u32]) -> ParseResult<Self> {
25        if spirv.len() < 5 { return Err(ParseError::EndOfStream) }
26        let magic_number = spirv[0];
27        if magic_number != 0x07230203 {
28            return Err(ParseError::InvalidMagicNumber(magic_number))
29        }
30        Ok(Self {
31            spirv: &spirv[5..],
32            version: spirv[1],
33            bound: spirv[3],
34            pos: 0,
35        })
36    }
37
38    /// Resets the stream's position to zero.
39    #[inline(always)]
40    pub fn reset(&mut self) {
41        self.pos = 0;
42    }
43
44    /// The version of the SPIR-V passed to this stream.
45    #[inline(always)]
46    pub fn version(&self) -> u32 {
47        self.version
48    }
49
50    /// The bound, where all ids are guaranteed to satisfy 0 < id < bound.
51    #[inline(always)]
52    pub fn bound(&self) -> u32 {
53        self.bound
54    }
55}
56
57/// Represents a single SPIR-V instruction stream.
58#[derive(Clone, Copy)]
59pub struct InstructionStream<'a> {
60    op: op::Code,
61    words: &'a [u32],
62    pos: usize,
63}
64
65impl<'a> InstructionStream<'a> {
66
67    /// Returns the instruction's [`Code`][1].
68    ///
69    /// [1]: op::Code
70    #[inline]
71    pub fn code(&self) -> op::Code {
72        self.op
73    }
74
75    /// Returns whether the end of the stream has been reached.
76    #[inline]
77    pub fn is_eos(&self) -> bool {
78        self.pos == self.words.len()
79    }
80
81    /// Resets the stream's position to one.
82    #[inline]
83    pub fn reset(&mut self) {
84        self.pos = 1;
85    }
86
87    /// Advances the streams position without reading.
88    #[inline]
89    pub fn advance(&mut self, count: u32) -> ParseResult<()> {
90        let end = self.pos + count as usize;
91        if end > self.words.len() { Err(ParseError::EndOfStream) }
92        else {
93            self.pos = end;
94            Ok(())
95        }
96    }
97    
98    /// Reads a word from the stream.
99    #[inline]
100    pub fn read(&mut self) -> ParseResult<u32> {
101        let len = self.words.len();
102        let pos = self.pos;
103        if pos == len { Err(ParseError::EndOfStream) }
104        else {
105            self.pos += 1;
106            Ok(self.words[pos])
107        }
108    }
109
110    /// Reads multiple words from the stream.
111    ///
112    /// Pass [`None`] for `count` to read until the end of the stream.
113    #[inline]
114    pub fn read_words(&mut self, count: Option<u32>) -> ParseResult<&'a [u32]> {
115        let len = self.words.len();
116        let pos = self.pos;
117        if pos == len { Ok(Default::default()) }
118        else {
119            let end = count
120                .map(|count| pos + count as usize)
121                .unwrap_or(len);
122            if end > len { Err(ParseError::EndOfStream) }
123            else {
124                self.pos = end;
125                Ok(&self.words[pos..end])
126            }
127        }
128    }
129
130    /// Reads a [`string`][1].
131    ///
132    /// [1]: CompilerStr
133    #[inline]
134    pub fn read_string(&mut self) -> ParseResult<CompilerStr<'a>> {
135        let pos = self.pos;
136        if pos == self.words.len() { Err(ParseError::EndOfStream) }
137        else {
138            let str = CompilerStr::new(&self.words[pos..]);
139            let div = str.len() / 4;
140            let mul = 4 * div;
141            self.pos = pos + div + if str.len() - mul != 0 { 1 } else { 0 };
142            Ok(str)
143        }
144    }
145}
146
147impl<'a> Iterator for Stream<'a> {
148
149    type Item = InstructionStream<'a>;
150
151    #[inline]
152    fn next(&mut self) -> Option<Self::Item> {
153        let len = self.spirv.len();
154        let pos = self.pos;
155        if pos == len { None }
156        else {
157            let word = self.spirv[pos];
158            let (count, op_code) = (word >> 16, word & 0xFFFF);
159            let end = pos + count as usize;
160            if end > len { None }
161            else {
162                let instruction = InstructionStream {
163                    op: op::Code(op_code as u16),
164                    words: &self.spirv[pos..end],
165                    pos: 1,
166                };
167                self.pos = end;
168                Some(instruction)
169            }
170        }
171    }
172}