Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::error::Error;
9use crate::{Header, Node};
10
11#[derive(Clone)]
12pub struct Parser<'dt> {
13 stream: Stream<'dt>,
14 pub strings: StringsBlock<'dt>,
15 pub structs: StructsBlock<'dt>,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[repr(transparent)]
20pub struct BigEndianU32(pub(crate) u32);
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23#[repr(transparent)]
24pub struct BigEndianToken(pub(crate) BigEndianU32);
25
26pub(crate) struct Stream<'dt>(&'dt [u32]);
27
28#[derive(Debug, Clone, Copy)]
29#[repr(transparent)]
30pub struct StringsBlock<'dt>(pub(crate) &'dt [u8]);
31
32#[derive(Debug, Clone, Copy)]
33#[repr(transparent)]
34pub struct StructsBlock<'dt>(pub(crate) &'dt [u32]);
35
36impl<'dt> Parser<'dt> {
37 pub fn new(data: &'dt [u32], strings: StringsBlock<'dt>, structs: StructsBlock<'dt>) -> Self {
38 Self {
39 stream: Stream::new(data),
40 strings,
41 structs,
42 }
43 }
44
45 pub fn data(&self) -> &'dt [u32] {
46 self.stream.0
47 }
48
49 pub fn byte_data(&self) -> &'dt [u8] {
50 // SAFETY: it is always valid to cast a `u32` to 4 `u8`s
51 unsafe {
52 core::slice::from_raw_parts(
53 self.stream.0.as_ptr().cast::<u8>(),
54 self.stream.0.len() * 4,
55 )
56 }
57 }
58
59 pub fn advance_token(&mut self) -> Result<BigEndianToken, Error> {
60 loop {
61 match BigEndianToken(
62 self.stream
63 .advance()
64 .map(BigEndianU32)
65 .ok_or(Error::UnexpectedEof)?,
66 ) {
67 BigEndianToken::NOP => continue,
68 token @ (BigEndianToken::BEGIN_NODE
69 | BigEndianToken::END_NODE
70 | BigEndianToken::PROP
71 | BigEndianToken::END) => break Ok(token),
72 t => break Err(Error::InvalidToken(t)),
73 }
74 }
75 }
76
77 pub(crate) fn peek_token(&self) -> Result<BigEndianToken, Error> {
78 self.clone().advance_token()
79 }
80
81 pub fn advance_u32(&mut self) -> Result<BigEndianU32, Error> {
82 self.stream
83 .advance()
84 .map(BigEndianU32)
85 .ok_or(Error::UnexpectedEof)
86 }
87
88 pub fn advance_cstr(&mut self) -> Result<&'dt core::ffi::CStr, Error> {
89 // SAFETY: It is safe to reinterpret the stream data to a smaller integer size
90 let bytes = unsafe {
91 core::slice::from_raw_parts(
92 self.stream.0.as_ptr().cast::<u8>(),
93 self.stream.0.len() * 4,
94 )
95 };
96 let cstr = core::ffi::CStr::from_bytes_until_nul(bytes)?;
97
98 // Round up to the next multiple of 4, if necessary
99 let skip = ((cstr.to_bytes_with_nul().len() + 3) & !3) / 4;
100 self.stream.skip_many(skip);
101
102 Ok(cstr)
103 }
104
105 pub fn advance_aligned(&mut self, n: usize) {
106 // Round up to the next multiple of 4, if necessary
107 let skip = ((n + 3) & !3) / 4;
108 self.stream.skip_many(skip);
109 }
110
111 pub fn parse_header(&mut self) -> Result<Header, Error> {
112 let magic = self.advance_u32()?.to_ne();
113 let total_size = self.advance_u32()?.to_ne();
114 let struct_offset = self.advance_u32()?.to_ne();
115 let strings_offset = self.advance_u32()?.to_ne();
116 let memory_reserve_map_offset = self.advance_u32()?.to_ne();
117 let version = self.advance_u32()?.to_ne();
118 let last_compatible_version = self.advance_u32()?.to_ne();
119 let boot_cpuid = self.advance_u32()?.to_ne();
120 let strings_size = self.advance_u32()?.to_ne();
121 let structs_size = self.advance_u32()?.to_ne();
122
123 Ok(Header {
124 magic,
125 total_size,
126 structs_offset: struct_offset,
127 strings_offset,
128 memory_reserve_map_offset,
129 version,
130 last_compatible_version,
131 boot_cpuid,
132 strings_size,
133 structs_size,
134 })
135 }
136
137 pub fn parse_root(&mut self) -> Result<Node<'dt>, Error> {
138 match self.advance_token()? {
139 BigEndianToken::BEGIN_NODE => {}
140 t => return Err(Error::UnexpectedToken(t)),
141 }
142
143 let byte_data = self.byte_data();
144 match byte_data
145 .get(byte_data.len() - 4..)
146 .map(<[u8; 4]>::try_from)
147 {
148 Some(Ok(data @ [_, _, _, _])) => {
149 match BigEndianToken(BigEndianU32(u32::from_ne_bytes(data))) {
150 BigEndianToken::END => {}
151 t => return Err(Error::UnexpectedToken(t)),
152 }
153 }
154 _ => return Err(Error::UnexpectedEof),
155 }
156
157 // advance past this nodes name
158 let name = self.advance_cstr()?;
159
160 let starting_data = self.data();
161
162 Ok(Node {
163 name,
164 raw: &starting_data[..starting_data.len() - 1],
165 strings: self.strings,
166 structs: self.structs,
167 })
168 }
169
170 pub fn parse_raw_property(&mut self) -> Result<(usize, &'dt [u8]), Error> {
171 match self.advance_token()? {
172 BigEndianToken::PROP => {
173 // Properties are in the format: <data len> <name offset> <data...>
174 let len = usize::try_from(self.advance_u32()?.to_ne())?;
175 let name_offset = usize::try_from(self.advance_u32()?.to_ne())?;
176 let data = self.byte_data().get(..len).ok_or(Error::UnexpectedEof)?;
177
178 self.advance_aligned(data.len());
179
180 Ok((name_offset, data))
181 }
182 t => Err(Error::UnexpectedToken(t)),
183 }
184 }
185}
186
187impl BigEndianU32 {
188 pub const fn from_ne(n: u32) -> Self {
189 Self(n.to_be())
190 }
191
192 pub const fn to_ne(self) -> u32 {
193 u32::from_be(self.0)
194 }
195}
196
197impl BigEndianToken {
198 pub const BEGIN_NODE: Self = Self(BigEndianU32::from_ne(1));
199 pub const END_NODE: Self = Self(BigEndianU32::from_ne(2));
200 pub const PROP: Self = Self(BigEndianU32::from_ne(3));
201 pub const NOP: Self = Self(BigEndianU32::from_ne(4));
202 pub const END: Self = Self(BigEndianU32::from_ne(9));
203}
204
205impl<'a> Stream<'a> {
206 #[inline(always)]
207 pub(crate) fn new(data: &'a [u32]) -> Self {
208 Self(data)
209 }
210
211 #[inline(always)]
212 pub(crate) fn advance(&mut self) -> Option<u32> {
213 let ret = *self.0.first()?;
214 self.0 = self.0.get(1..)?;
215 Some(ret)
216 }
217
218 pub(crate) fn skip_many(&mut self, n: usize) {
219 self.0 = self.0.get(n..).unwrap_or_default();
220 }
221}
222
223impl Clone for Stream<'_> {
224 fn clone(&self) -> Self {
225 Self(self.0)
226 }
227}
228
229impl<'a> StringsBlock<'a> {
230 pub fn offset_at(self, offset: usize) -> Result<&'a str, Error> {
231 core::ffi::CStr::from_bytes_until_nul(self.0.get(offset..).ok_or(Error::UnexpectedEof)?)?
232 .to_str()
233 .map_err(Into::into)
234 }
235}