1use super::TrapCode;
4use core::fmt;
5use core::str::FromStr;
6
7#[cfg(feature = "enable-serde")]
8use serde_derive::{Deserialize, Serialize};
9
10#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
12pub enum Endianness {
13 Little,
15 Big,
17}
18
19#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
22#[repr(u8)]
23#[allow(missing_docs)]
24#[rustfmt::skip]
25pub enum AliasRegion {
26 Heap = 0b01,
28 Table = 0b10,
29 Vmctx = 0b11,
30}
31
32impl AliasRegion {
33 const fn from_bits(bits: u8) -> Option<Self> {
34 match bits {
35 0b00 => None,
36 0b01 => Some(Self::Heap),
37 0b10 => Some(Self::Table),
38 0b11 => Some(Self::Vmctx),
39 _ => panic!("invalid alias region bits"),
40 }
41 }
42
43 const fn to_bits(region: Option<Self>) -> u8 {
44 match region {
45 None => 0b00,
46 Some(r) => r as u8,
47 }
48 }
49}
50
51#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
62#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
63pub struct MemFlags {
64 bits: u16,
82}
83
84const BIT_ALIGNED: u16 = 1 << 0;
87
88const BIT_READONLY: u16 = 1 << 1;
92
93const BIT_LITTLE_ENDIAN: u16 = 1 << 2;
95
96const BIT_BIG_ENDIAN: u16 = 1 << 3;
98
99const BIT_CHECKED: u16 = 1 << 4;
104
105const MASK_ALIAS_REGION: u16 = 0b11 << ALIAS_REGION_OFFSET;
108const ALIAS_REGION_OFFSET: u16 = 5;
109
110const MASK_TRAP_CODE: u16 = 0b1111 << TRAP_CODE_OFFSET;
112const TRAP_CODE_OFFSET: u16 = 7;
113
114impl MemFlags {
115 pub const fn new() -> Self {
117 Self { bits: 0 }
118 }
119
120 pub const fn trusted() -> Self {
123 Self::new().with_notrap().with_aligned()
124 }
125
126 const fn read_bit(self, bit: u16) -> bool {
128 self.bits & bit != 0
129 }
130
131 const fn with_bit(mut self, bit: u16) -> Self {
133 self.bits |= bit;
134 self
135 }
136
137 pub const fn alias_region(self) -> Option<AliasRegion> {
139 AliasRegion::from_bits(((self.bits & MASK_ALIAS_REGION) >> ALIAS_REGION_OFFSET) as u8)
140 }
141
142 pub const fn with_alias_region(mut self, region: Option<AliasRegion>) -> Self {
144 let bits = AliasRegion::to_bits(region);
145 self.bits &= !MASK_ALIAS_REGION;
146 self.bits |= (bits as u16) << ALIAS_REGION_OFFSET;
147 self
148 }
149
150 pub fn set_alias_region(&mut self, region: Option<AliasRegion>) {
152 *self = self.with_alias_region(region);
153 }
154
155 pub fn set_by_name(&mut self, name: &str) -> Result<bool, &'static str> {
165 *self = match name {
166 "notrap" => self.with_trap_code(None),
167 "aligned" => self.with_aligned(),
168 "readonly" => self.with_readonly(),
169 "little" => {
170 if self.read_bit(BIT_BIG_ENDIAN) {
171 return Err("cannot set both big and little endian bits");
172 }
173 self.with_endianness(Endianness::Little)
174 }
175 "big" => {
176 if self.read_bit(BIT_LITTLE_ENDIAN) {
177 return Err("cannot set both big and little endian bits");
178 }
179 self.with_endianness(Endianness::Big)
180 }
181 "heap" => {
182 if self.alias_region().is_some() {
183 return Err("cannot set more than one alias region");
184 }
185 self.with_alias_region(Some(AliasRegion::Heap))
186 }
187 "table" => {
188 if self.alias_region().is_some() {
189 return Err("cannot set more than one alias region");
190 }
191 self.with_alias_region(Some(AliasRegion::Table))
192 }
193 "vmctx" => {
194 if self.alias_region().is_some() {
195 return Err("cannot set more than one alias region");
196 }
197 self.with_alias_region(Some(AliasRegion::Vmctx))
198 }
199 "checked" => self.with_checked(),
200
201 other => match TrapCode::from_str(other) {
202 Ok(TrapCode::User(_)) => return Err("cannot set user trap code on mem flags"),
203 Ok(code) => self.with_trap_code(Some(code)),
204 Err(()) => return Ok(false),
205 },
206 };
207 Ok(true)
208 }
209
210 pub const fn endianness(self, native_endianness: Endianness) -> Endianness {
216 if self.read_bit(BIT_LITTLE_ENDIAN) {
217 Endianness::Little
218 } else if self.read_bit(BIT_BIG_ENDIAN) {
219 Endianness::Big
220 } else {
221 native_endianness
222 }
223 }
224
225 pub fn set_endianness(&mut self, endianness: Endianness) {
227 *self = self.with_endianness(endianness);
228 }
229
230 pub const fn with_endianness(self, endianness: Endianness) -> Self {
232 let res = match endianness {
233 Endianness::Little => self.with_bit(BIT_LITTLE_ENDIAN),
234 Endianness::Big => self.with_bit(BIT_BIG_ENDIAN),
235 };
236 assert!(!(res.read_bit(BIT_LITTLE_ENDIAN) && res.read_bit(BIT_BIG_ENDIAN)));
237 res
238 }
239
240 pub const fn notrap(self) -> bool {
251 self.trap_code().is_none()
252 }
253
254 pub fn set_notrap(&mut self) {
256 *self = self.with_notrap();
257 }
258
259 pub const fn with_notrap(self) -> Self {
262 self.with_trap_code(None)
263 }
264
265 pub const fn aligned(self) -> bool {
271 self.read_bit(BIT_ALIGNED)
272 }
273
274 pub fn set_aligned(&mut self) {
276 *self = self.with_aligned();
277 }
278
279 pub const fn with_aligned(self) -> Self {
281 self.with_bit(BIT_ALIGNED)
282 }
283
284 pub const fn readonly(self) -> bool {
290 self.read_bit(BIT_READONLY)
291 }
292
293 pub fn set_readonly(&mut self) {
295 *self = self.with_readonly();
296 }
297
298 pub const fn with_readonly(self) -> Self {
300 self.with_bit(BIT_READONLY)
301 }
302
303 pub const fn checked(self) -> bool {
315 self.read_bit(BIT_CHECKED)
316 }
317
318 pub fn set_checked(&mut self) {
320 *self = self.with_checked();
321 }
322
323 pub const fn with_checked(self) -> Self {
325 self.with_bit(BIT_CHECKED)
326 }
327
328 pub const fn trap_code(self) -> Option<TrapCode> {
332 match (self.bits & MASK_TRAP_CODE) >> TRAP_CODE_OFFSET {
338 0b0000 => Some(TrapCode::HeapOutOfBounds),
339 0b0001 => Some(TrapCode::StackOverflow),
340 0b0010 => Some(TrapCode::HeapMisaligned),
341 0b0011 => Some(TrapCode::TableOutOfBounds),
342 0b0100 => Some(TrapCode::IndirectCallToNull),
343 0b0101 => Some(TrapCode::BadSignature),
344 0b0110 => Some(TrapCode::IntegerOverflow),
345 0b0111 => Some(TrapCode::IntegerDivisionByZero),
346 0b1000 => Some(TrapCode::BadConversionToInteger),
347 0b1001 => Some(TrapCode::UnreachableCodeReached),
348 0b1010 => Some(TrapCode::Interrupt),
349 0b1011 => Some(TrapCode::NullReference),
350 0b1100 => Some(TrapCode::NullI31Ref),
351 0b1111 => None,
354 _ => unreachable!(),
355 }
356 }
357
358 pub const fn with_trap_code(mut self, code: Option<TrapCode>) -> Self {
366 let bits = match code {
367 Some(TrapCode::HeapOutOfBounds) => 0b0000,
368 Some(TrapCode::StackOverflow) => 0b0001,
369 Some(TrapCode::HeapMisaligned) => 0b0010,
370 Some(TrapCode::TableOutOfBounds) => 0b0011,
371 Some(TrapCode::IndirectCallToNull) => 0b0100,
372 Some(TrapCode::BadSignature) => 0b0101,
373 Some(TrapCode::IntegerOverflow) => 0b0110,
374 Some(TrapCode::IntegerDivisionByZero) => 0b0111,
375 Some(TrapCode::BadConversionToInteger) => 0b1000,
376 Some(TrapCode::UnreachableCodeReached) => 0b1001,
377 Some(TrapCode::Interrupt) => 0b1010,
378 Some(TrapCode::NullReference) => 0b1011,
379 Some(TrapCode::NullI31Ref) => 0b1100,
380 None => 0b1111,
381
382 Some(TrapCode::User(_)) => panic!("cannot set user trap code in mem flags"),
383 };
384 self.bits &= !MASK_TRAP_CODE;
385 self.bits |= bits << TRAP_CODE_OFFSET;
386 self
387 }
388}
389
390impl fmt::Display for MemFlags {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 match self.trap_code() {
393 None => write!(f, " notrap")?,
394 Some(TrapCode::HeapOutOfBounds) => {}
397 Some(t) => write!(f, " {t}")?,
398 }
399 if self.aligned() {
400 write!(f, " aligned")?;
401 }
402 if self.readonly() {
403 write!(f, " readonly")?;
404 }
405 if self.read_bit(BIT_BIG_ENDIAN) {
406 write!(f, " big")?;
407 }
408 if self.read_bit(BIT_LITTLE_ENDIAN) {
409 write!(f, " little")?;
410 }
411 if self.checked() {
412 write!(f, " checked")?;
413 }
414 match self.alias_region() {
415 None => {}
416 Some(AliasRegion::Heap) => write!(f, " heap")?,
417 Some(AliasRegion::Table) => write!(f, " table")?,
418 Some(AliasRegion::Vmctx) => write!(f, " vmctx")?,
419 }
420 Ok(())
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn roundtrip_traps() {
430 for trap in TrapCode::non_user_traps().iter().copied() {
431 let flags = MemFlags::new().with_trap_code(Some(trap));
432 assert_eq!(flags.trap_code(), Some(trap));
433 }
434 let flags = MemFlags::new().with_trap_code(None);
435 assert_eq!(flags.trap_code(), None);
436 }
437
438 #[test]
439 fn cannot_set_big_and_little() {
440 let mut big = MemFlags::new().with_endianness(Endianness::Big);
441 assert!(big.set_by_name("little").is_err());
442
443 let mut little = MemFlags::new().with_endianness(Endianness::Little);
444 assert!(little.set_by_name("big").is_err());
445 }
446
447 #[test]
448 fn only_one_region() {
449 let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Heap));
450 assert!(big.set_by_name("table").is_err());
451 assert!(big.set_by_name("vmctx").is_err());
452
453 let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Table));
454 assert!(big.set_by_name("heap").is_err());
455 assert!(big.set_by_name("vmctx").is_err());
456
457 let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Vmctx));
458 assert!(big.set_by_name("heap").is_err());
459 assert!(big.set_by_name("table").is_err());
460 }
461}