Skip to main content

wasefire_interpreter/
syntax.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use alloc::vec::Vec;
16use core::ops::Deref;
17
18use num_enum::{TryFromPrimitive, UnsafeFromPrimitive};
19
20use crate::error::*;
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
23#[repr(u8)]
24pub enum NumType {
25    I32 = 0x7f,
26    I64 = 0x7e,
27    F32 = 0x7d,
28    F64 = 0x7c,
29}
30
31#[allow(dead_code)] // TODO: Remove when we support them.
32#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
33#[repr(u8)]
34pub enum VecType {
35    V128 = 0x7b,
36}
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
39#[repr(u8)]
40pub enum RefType {
41    FuncRef = 0x70,
42    ExternRef = 0x6f,
43}
44
45impl RefType {
46    pub(crate) fn is_unsupported(byte: u8) -> Option<Unsupported> {
47        match byte {
48            0x6f | 0x70 => None,
49            // TODO: We might want to support 0x63 and 0x64 but then we need can't represent RefType
50            // and ValType with u8.
51            0x63 | 0x64 | 0x69 ..= 0x74 => Some(if_debug!(Unsupported::HeapType)),
52            _ => None,
53        }
54    }
55}
56
57#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
58#[repr(u8)]
59pub enum ValType {
60    I32 = 0x7f,
61    I64 = 0x7e,
62    F32 = 0x7d,
63    F64 = 0x7c,
64    V128 = 0x7b,
65    FuncRef = 0x70,
66    ExternRef = 0x6f,
67}
68
69impl ValType {
70    pub(crate) fn is_unsupported(byte: u8) -> Option<Unsupported> {
71        RefType::is_unsupported(byte)
72    }
73}
74
75#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
76pub struct ResultType<'m>(&'m [ValType]);
77
78#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
79pub struct FuncType<'m> {
80    pub params: ResultType<'m>,
81    pub results: ResultType<'m>,
82}
83
84#[derive(Debug, Copy, Clone, PartialEq, Eq)]
85pub struct Limits {
86    pub min: u32,
87    pub max: u32,
88}
89
90pub const TABLE_MAX: u32 = u32::MAX;
91pub const MEM_MAX: u32 = 0x10000;
92
93impl Limits {
94    pub fn valid(&self, k: u32) -> bool {
95        self.min <= self.max && self.max <= k
96    }
97
98    pub fn matches(self, other: Limits) -> bool {
99        self.min >= other.min && self.max <= other.max
100    }
101}
102
103pub type MemType = Limits;
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq)]
106pub struct TableType {
107    pub limits: Limits,
108    pub item: RefType,
109}
110
111#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
112#[repr(u8)]
113pub enum Mut {
114    Const = 0,
115    Var = 1,
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, Eq)]
119pub struct GlobalType {
120    pub mutable: Mut,
121    pub value: ValType,
122}
123
124#[derive(Debug, Copy, Clone, PartialEq, Eq)]
125pub enum Sx {
126    U,
127    S,
128}
129
130#[derive(Debug, Copy, Clone, PartialEq, Eq)]
131pub enum Nx {
132    N32,
133    N64,
134}
135
136#[derive(Debug, Copy, Clone, PartialEq, Eq)]
137pub enum Bx {
138    N32B8,
139    N32B16,
140    N64B8,
141    N64B16,
142    N64B32,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub enum ITestOp {
147    Eqz,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum IRelOp {
152    Eq,
153    Ne,
154    Lt(Sx),
155    Gt(Sx),
156    Le(Sx),
157    Ge(Sx),
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum IUnOp {
162    Clz,
163    Ctz,
164    PopCnt,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum IBinOp {
169    Add,
170    Sub,
171    Mul,
172    Div(Sx),
173    Rem(Sx),
174    And,
175    Or,
176    Xor,
177    Shl,
178    Shr(Sx),
179    Rotl,
180    Rotr,
181}
182
183#[cfg(feature = "float-types")]
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub enum FRelOp {
186    Eq,
187    Ne,
188    Lt,
189    Gt,
190    Le,
191    Ge,
192}
193
194#[cfg(feature = "float-types")]
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum FUnOp {
197    Abs,
198    Neg,
199    Ceil,
200    Floor,
201    Trunc,
202    Nearest,
203    Sqrt,
204}
205
206#[cfg(feature = "float-types")]
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub enum FBinOp {
209    Add,
210    Sub,
211    Mul,
212    Div,
213    Min,
214    Max,
215    CopySign,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub enum CvtOp {
220    Wrap,
221    Extend(Sx),
222    #[cfg(feature = "float-types")]
223    Trunc(Nx, Nx, Sx),
224    #[cfg(feature = "float-types")]
225    TruncSat(Nx, Nx, Sx),
226    #[cfg(feature = "float-types")]
227    Convert(Nx, Nx, Sx),
228    #[cfg(feature = "float-types")]
229    Demote,
230    #[cfg(feature = "float-types")]
231    Promote,
232    #[cfg(feature = "float-types")]
233    IReinterpret(Nx),
234    #[cfg(feature = "float-types")]
235    FReinterpret(Nx),
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
239pub enum Instr<'m> {
240    Unreachable,
241    Nop,
242    Block(BlockType),
243    Loop(BlockType),
244    If(BlockType),
245    Else,
246    End,
247    Br(LabelIdx),
248    BrIf(LabelIdx),
249    BrTable(Vec<LabelIdx>, LabelIdx),
250    Return,
251    Call(FuncIdx),
252    CallIndirect(TableIdx, TypeIdx), // parsing order differs
253    Drop,
254    Select(Option<ResultType<'m>>),
255    LocalGet(LocalIdx),
256    LocalSet(LocalIdx),
257    LocalTee(LocalIdx),
258    GlobalGet(GlobalIdx),
259    GlobalSet(GlobalIdx),
260    TableGet(TableIdx),
261    TableSet(TableIdx),
262    ILoad(Nx, MemArg),
263    #[cfg(feature = "float-types")]
264    FLoad(Nx, MemArg),
265    ILoad_(Bx, Sx, MemArg),
266    IStore(Nx, MemArg),
267    #[cfg(feature = "float-types")]
268    FStore(Nx, MemArg),
269    IStore_(Bx, MemArg),
270    MemorySize,
271    MemoryGrow,
272    I32Const(u32),
273    I64Const(u64),
274    #[cfg(feature = "float-types")]
275    F32Const(u32),
276    #[cfg(feature = "float-types")]
277    F64Const(u64),
278    ITestOp(Nx, ITestOp),
279    IRelOp(Nx, IRelOp),
280    #[cfg(feature = "float-types")]
281    FRelOp(Nx, FRelOp),
282    IUnOp(Nx, IUnOp),
283    #[cfg(feature = "float-types")]
284    FUnOp(Nx, FUnOp),
285    IBinOp(Nx, IBinOp),
286    #[cfg(feature = "float-types")]
287    FBinOp(Nx, FBinOp),
288    CvtOp(CvtOp),
289    IExtend(Bx),
290    RefNull(RefType),
291    RefIsNull,
292    RefFunc(FuncIdx),
293    MemoryInit(DataIdx),
294    DataDrop(DataIdx),
295    MemoryCopy,
296    MemoryFill,
297    TableInit(TableIdx, ElemIdx), // parsing order differs
298    ElemDrop(ElemIdx),
299    TableCopy(TableIdx, TableIdx),
300    TableGrow(TableIdx),
301    TableSize(TableIdx),
302    TableFill(TableIdx),
303}
304
305pub type TypeIdx = u32;
306pub type FuncIdx = u32;
307pub type TableIdx = u32;
308pub type MemIdx = u32;
309pub type GlobalIdx = u32;
310pub type ElemIdx = u32;
311pub type DataIdx = u32;
312pub type LocalIdx = u32;
313pub type LabelIdx = u32;
314
315#[derive(Debug, Clone, PartialEq, Eq)]
316pub enum ExportDesc {
317    Func(FuncIdx),
318    Table(TableIdx),
319    Mem(MemIdx),
320    Global(GlobalIdx),
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
324pub enum ImportDesc {
325    Func(TypeIdx),
326    Table(TableType),
327    Mem(MemType),
328    Global(GlobalType),
329}
330
331#[derive(Debug, Clone)]
332pub struct Import<'m> {
333    pub module: &'m str,
334    pub name: &'m str,
335    pub desc: ImportDesc,
336}
337
338#[derive(Debug, Clone, PartialEq, Eq)]
339pub enum ExternType<'m> {
340    Func(FuncType<'m>),
341    Table(TableType),
342    Mem(MemType),
343    Global(GlobalType),
344}
345
346impl<'m> ExternType<'m> {
347    pub fn matches(&self, other: &ExternType<'m>) -> bool {
348        match (self, other) {
349            (ExternType::Func(x), ExternType::Func(y)) => x == y,
350            (
351                ExternType::Table(TableType { limits: t, item: x }),
352                ExternType::Table(TableType { limits: s, item: y }),
353            ) => t.matches(*s) && x == y,
354            (ExternType::Mem(t), ExternType::Mem(s)) => t.matches(*s),
355            (ExternType::Global(x), ExternType::Global(y)) => x == y,
356            _ => false,
357        }
358    }
359}
360
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub enum BlockType {
363    None,
364    Type(ValType),
365    Index(TypeIdx),
366}
367
368#[derive(Debug, Copy, Clone, PartialEq, Eq)]
369pub struct MemArg {
370    pub align: u32,
371    pub offset: u32,
372}
373
374#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, UnsafeFromPrimitive)]
375#[repr(u8)]
376pub enum SectionId {
377    Custom = 0,
378    Type = 1,
379    Import = 2,
380    Function = 3,
381    Table = 4,
382    Memory = 5,
383    Global = 6,
384    Export = 7,
385    Start = 8,
386    Element = 9,
387    Code = 10,
388    Data = 11,
389    DataCount = 12,
390    Tag = 13,
391}
392
393impl Deref for ResultType<'_> {
394    type Target = [ValType];
395
396    fn deref(&self) -> &Self::Target {
397        self.0
398    }
399}
400
401impl<'m> From<&'m [ValType]> for ResultType<'m> {
402    fn from(xs: &'m [ValType]) -> Self {
403        Self(xs)
404    }
405}
406
407impl From<ValType> for ResultType<'_> {
408    fn from(x: ValType) -> Self {
409        macro_rules! make {
410            ($($x:ident),*) => {{
411                static VAL_TYPES: &[ValType] = &[$(ValType::$x),*];
412                make!([][0][$($x)*])
413            }};
414            ([$($output:tt)*][$i:expr][]) => {
415                Self(match x { $($output)* })
416            };
417            ([$($output:tt)*][$i:expr][$x:ident $($input:ident)*]) => {
418                make!([$($output)* ValType::$x => core::slice::from_ref(&VAL_TYPES[$i]),]
419                      [$i + 1][$($input)*])
420            };
421        }
422        make!(I32, I64, F32, F64, V128, FuncRef, ExternRef)
423    }
424}
425
426impl From<()> for ResultType<'_> {
427    fn from(_: ()) -> Self {
428        Self(&[])
429    }
430}
431
432impl NumType {
433    pub fn i(n: Nx) -> Self {
434        match n {
435            Nx::N32 => NumType::I32,
436            Nx::N64 => NumType::I64,
437        }
438    }
439
440    #[cfg(feature = "float-types")]
441    pub fn f(n: Nx) -> Self {
442        match n {
443            Nx::N32 => NumType::F32,
444            Nx::N64 => NumType::F64,
445        }
446    }
447}
448
449impl From<Nx> for usize {
450    fn from(x: Nx) -> Self {
451        match x {
452            Nx::N32 => 32,
453            Nx::N64 => 64,
454        }
455    }
456}
457
458impl From<Bx> for Nx {
459    fn from(x: Bx) -> Self {
460        match x {
461            Bx::N32B8 | Bx::N32B16 => Nx::N32,
462            Bx::N64B8 | Bx::N64B16 | Bx::N64B32 => Nx::N64,
463        }
464    }
465}
466
467impl From<Bx> for usize {
468    fn from(x: Bx) -> Self {
469        match x {
470            Bx::N32B8 | Bx::N64B8 => 8,
471            Bx::N32B16 | Bx::N64B16 => 16,
472            Bx::N64B32 => 32,
473        }
474    }
475}
476
477impl CvtOp {
478    pub fn dst(&self) -> NumType {
479        match *self {
480            CvtOp::Wrap => NumType::I32,
481            CvtOp::Extend(_) => NumType::I64,
482            #[cfg(feature = "float-types")]
483            CvtOp::Trunc(n, _, _) => NumType::i(n),
484            #[cfg(feature = "float-types")]
485            CvtOp::TruncSat(n, _, _) => NumType::i(n),
486            #[cfg(feature = "float-types")]
487            CvtOp::Convert(n, _, _) => NumType::f(n),
488            #[cfg(feature = "float-types")]
489            CvtOp::Demote => NumType::F32,
490            #[cfg(feature = "float-types")]
491            CvtOp::Promote => NumType::F64,
492            #[cfg(feature = "float-types")]
493            CvtOp::IReinterpret(n) => NumType::i(n),
494            #[cfg(feature = "float-types")]
495            CvtOp::FReinterpret(n) => NumType::f(n),
496        }
497    }
498
499    pub fn src(&self) -> NumType {
500        match *self {
501            CvtOp::Wrap => NumType::I64,
502            CvtOp::Extend(_) => NumType::I32,
503            #[cfg(feature = "float-types")]
504            CvtOp::Trunc(_, m, _) => NumType::f(m),
505            #[cfg(feature = "float-types")]
506            CvtOp::TruncSat(_, m, _) => NumType::f(m),
507            #[cfg(feature = "float-types")]
508            CvtOp::Convert(_, m, _) => NumType::i(m),
509            #[cfg(feature = "float-types")]
510            CvtOp::Demote => NumType::F64,
511            #[cfg(feature = "float-types")]
512            CvtOp::Promote => NumType::F32,
513            #[cfg(feature = "float-types")]
514            CvtOp::IReinterpret(n) => NumType::f(n),
515            #[cfg(feature = "float-types")]
516            CvtOp::FReinterpret(n) => NumType::i(n),
517        }
518    }
519}
520
521macro_rules! impl_op {
522    ($n:ident, $u:ident, $i:ident, $f:ident) => {
523        impl ITestOp {
524            pub fn $n(&self, x: $u) -> bool {
525                match self {
526                    ITestOp::Eqz => x == 0,
527                }
528            }
529        }
530
531        impl IRelOp {
532            pub fn $n(&self, x: $u, y: $u) -> bool {
533                match self {
534                    IRelOp::Eq => x == y,
535                    IRelOp::Ne => x != y,
536                    IRelOp::Lt(Sx::U) => x < y,
537                    IRelOp::Lt(Sx::S) => (x as $i) < (y as $i),
538                    IRelOp::Gt(Sx::U) => x > y,
539                    IRelOp::Gt(Sx::S) => (x as $i) > (y as $i),
540                    IRelOp::Le(Sx::U) => x <= y,
541                    IRelOp::Le(Sx::S) => (x as $i) <= (y as $i),
542                    IRelOp::Ge(Sx::U) => x >= y,
543                    IRelOp::Ge(Sx::S) => (x as $i) >= (y as $i),
544                }
545            }
546        }
547
548        impl IUnOp {
549            pub fn $n(&self, x: $u) -> Option<$u> {
550                Some(match self {
551                    IUnOp::Clz => x.leading_zeros(),
552                    IUnOp::Ctz => x.trailing_zeros(),
553                    IUnOp::PopCnt => x.count_ones(),
554                } as $u)
555            }
556        }
557
558        impl IBinOp {
559            pub fn $n(&self, x: $u, y: $u) -> Option<$u> {
560                match self {
561                    IBinOp::Add => Some(x.wrapping_add(y)),
562                    IBinOp::Sub => Some(x.wrapping_sub(y)),
563                    IBinOp::Mul => Some(x.wrapping_mul(y)),
564                    IBinOp::Div(Sx::U) => x.checked_div(y),
565                    IBinOp::Div(Sx::S) => (x as $i).checked_div(y as $i).map(|z| z as $u),
566                    IBinOp::Rem(Sx::U) => x.checked_rem(y),
567                    IBinOp::Rem(Sx::S) if y == 0 => None,
568                    IBinOp::Rem(Sx::S) => Some((x as $i).wrapping_rem(y as $i) as $u),
569                    IBinOp::And => Some(x & y),
570                    IBinOp::Or => Some(x | y),
571                    IBinOp::Xor => Some(x ^ y),
572                    IBinOp::Shl => Some(x.wrapping_shl(y as u32)),
573                    IBinOp::Shr(Sx::U) => Some(x.wrapping_shr(y as u32)),
574                    IBinOp::Shr(Sx::S) => Some((x as $i).wrapping_shr(y as u32) as $u),
575                    IBinOp::Rotl => Some(x.rotate_left(y as u32)),
576                    IBinOp::Rotr => Some(x.rotate_right(y as u32)),
577                }
578            }
579        }
580
581        #[cfg(feature = "float-types")]
582        impl FRelOp {
583            pub fn $n(&self, x: $u, y: $u) -> bool {
584                let x = $f::from_bits(x);
585                let y = $f::from_bits(y);
586                match self {
587                    FRelOp::Eq => x == y,
588                    FRelOp::Ne => x != y,
589                    FRelOp::Lt => x < y,
590                    FRelOp::Gt => x > y,
591                    FRelOp::Le => x <= y,
592                    FRelOp::Ge => x >= y,
593                }
594            }
595        }
596
597        #[cfg(feature = "float-types")]
598        impl FUnOp {
599            pub fn $n(&self, x: $u) -> $u {
600                let x = $f::from_bits(x);
601                let z = match self {
602                    FUnOp::Abs => float::$f::abs(x),
603                    FUnOp::Neg => -x,
604                    FUnOp::Ceil => float::$f::ceil(x),
605                    FUnOp::Floor => float::$f::floor(x),
606                    FUnOp::Trunc => float::$f::trunc(x),
607                    FUnOp::Nearest => {
608                        let round = float::$f::round(x);
609                        if float::$f::abs(x - round) == 0.5 {
610                            match round % 2. {
611                                r if r == 1. => float::$f::floor(x),
612                                r if r == -1. => float::$f::ceil(x),
613                                r if r == 0. => round,
614                                _ => unreachable!(),
615                            }
616                        } else {
617                            round
618                        }
619                    }
620                    FUnOp::Sqrt => float::$f::sqrt(x),
621                };
622                z.to_bits()
623            }
624        }
625
626        #[cfg(feature = "float-types")]
627        impl FBinOp {
628            pub fn $n(&self, x_: $u, y_: $u) -> $u {
629                let x = $f::from_bits(x_);
630                let y = $f::from_bits(y_);
631                let z = match self {
632                    FBinOp::Add => x + y,
633                    FBinOp::Sub => x - y,
634                    FBinOp::Mul => x * y,
635                    FBinOp::Div => x / y,
636                    FBinOp::Min => x.minimum(y),
637                    FBinOp::Max => x.maximum(y),
638                    FBinOp::CopySign => {
639                        const M: $u = 1 << ($u::BITS - 1);
640                        return (x_ & !M) | (y_ & M);
641                    }
642                };
643                z.to_bits()
644            }
645        }
646    };
647}
648impl_op!(n32, u32, i32, f32);
649impl_op!(n64, u64, i64, f64);
650
651#[cfg(feature = "float-types")]
652#[allow(non_upper_case_globals)]
653mod float {
654    pub mod f32 {
655        pub const abs: fn(f32) -> f32 = libm::fabsf;
656        pub const ceil: fn(f32) -> f32 = libm::ceilf;
657        pub const floor: fn(f32) -> f32 = libm::floorf;
658        pub const round: fn(f32) -> f32 = libm::roundf;
659        pub const sqrt: fn(f32) -> f32 = libm::sqrtf;
660        pub const trunc: fn(f32) -> f32 = libm::truncf;
661    }
662
663    pub mod f64 {
664        pub const abs: fn(f64) -> f64 = libm::fabs;
665        pub const ceil: fn(f64) -> f64 = libm::ceil;
666        pub const floor: fn(f64) -> f64 = libm::floor;
667        pub const round: fn(f64) -> f64 = libm::round;
668        pub const sqrt: fn(f64) -> f64 = libm::sqrt;
669        pub const trunc: fn(f64) -> f64 = libm::trunc;
670    }
671}
672
673impl From<u8> for Sx {
674    fn from(x: u8) -> Self {
675        match x {
676            0 => Sx::S,
677            1 => Sx::U,
678            _ => unreachable!(),
679        }
680    }
681}
682
683impl From<u8> for Nx {
684    fn from(x: u8) -> Self {
685        match x {
686            0 => Nx::N32,
687            1 => Nx::N64,
688            _ => unreachable!(),
689        }
690    }
691}
692
693impl From<u8> for Bx {
694    fn from(x: u8) -> Self {
695        match x {
696            0 => Bx::N32B8,
697            1 => Bx::N32B16,
698            2 => Bx::N64B8,
699            3 => Bx::N64B16,
700            4 => Bx::N64B32,
701            _ => unreachable!(),
702        }
703    }
704}
705
706impl From<u8> for IRelOp {
707    fn from(x: u8) -> Self {
708        match x {
709            0 => IRelOp::Eq,
710            1 => IRelOp::Ne,
711            x @ 2 ..= 3 => IRelOp::Lt((x - 2).into()),
712            x @ 4 ..= 5 => IRelOp::Gt((x - 4).into()),
713            x @ 6 ..= 7 => IRelOp::Le((x - 6).into()),
714            x @ 8 ..= 9 => IRelOp::Ge((x - 8).into()),
715            _ => unreachable!(),
716        }
717    }
718}
719
720impl From<u8> for IUnOp {
721    fn from(x: u8) -> Self {
722        match x {
723            0 => IUnOp::Clz,
724            1 => IUnOp::Ctz,
725            2 => IUnOp::PopCnt,
726            _ => unreachable!(),
727        }
728    }
729}
730
731impl From<u8> for IBinOp {
732    fn from(x: u8) -> Self {
733        match x {
734            0 => IBinOp::Add,
735            1 => IBinOp::Sub,
736            2 => IBinOp::Mul,
737            x @ 3 ..= 4 => IBinOp::Div((x - 3).into()),
738            x @ 5 ..= 6 => IBinOp::Rem((x - 5).into()),
739            7 => IBinOp::And,
740            8 => IBinOp::Or,
741            9 => IBinOp::Xor,
742            10 => IBinOp::Shl,
743            x @ 11 ..= 12 => IBinOp::Shr((x - 11).into()),
744            13 => IBinOp::Rotl,
745            14 => IBinOp::Rotr,
746            _ => unreachable!(),
747        }
748    }
749}
750
751#[cfg(feature = "float-types")]
752impl From<u8> for FRelOp {
753    fn from(x: u8) -> Self {
754        match x {
755            0 => FRelOp::Eq,
756            1 => FRelOp::Ne,
757            2 => FRelOp::Lt,
758            3 => FRelOp::Gt,
759            4 => FRelOp::Le,
760            5 => FRelOp::Ge,
761            _ => unreachable!(),
762        }
763    }
764}
765
766#[cfg(feature = "float-types")]
767impl From<u8> for FUnOp {
768    fn from(x: u8) -> Self {
769        match x {
770            0 => FUnOp::Abs,
771            1 => FUnOp::Neg,
772            2 => FUnOp::Ceil,
773            3 => FUnOp::Floor,
774            4 => FUnOp::Trunc,
775            5 => FUnOp::Nearest,
776            6 => FUnOp::Sqrt,
777            _ => unreachable!(),
778        }
779    }
780}
781
782#[cfg(feature = "float-types")]
783impl From<u8> for FBinOp {
784    fn from(x: u8) -> Self {
785        match x {
786            0 => FBinOp::Add,
787            1 => FBinOp::Sub,
788            2 => FBinOp::Mul,
789            3 => FBinOp::Div,
790            4 => FBinOp::Min,
791            5 => FBinOp::Max,
792            6 => FBinOp::CopySign,
793            _ => unreachable!(),
794        }
795    }
796}
797
798impl SectionId {
799    pub fn order(self) -> u8 {
800        // DataCount is actually between Element and Code.
801        match self as u8 {
802            x @ 0 ..= 5 => x,
803            13 => 6,
804            x @ 6 ..= 9 => x + 1,
805            12 => 11,
806            x @ 10 ..= 11 => x + 2,
807            _ => unreachable!(),
808        }
809    }
810}
811
812pub trait TryFromByte: Sized {
813    fn try_from_byte(byte: u8) -> Option<Self>;
814}
815
816pub trait UnsafeFromByte {
817    // Safety: The byte must be a valid representation.
818    unsafe fn from_byte_unchecked(byte: u8) -> Self;
819}
820
821impl TryFromByte for bool {
822    fn try_from_byte(byte: u8) -> Option<Self> {
823        Some(match byte {
824            0 => false,
825            1 => true,
826            _ => return None,
827        })
828    }
829}
830
831impl UnsafeFromByte for bool {
832    unsafe fn from_byte_unchecked(byte: u8) -> Self {
833        unsafe { Self::try_from_byte(byte).unwrap_unchecked() }
834    }
835}
836
837macro_rules! impl_from_byte {
838    ($name:ident) => {
839        impl UnsafeFromByte for $name {
840            unsafe fn from_byte_unchecked(byte: u8) -> Self {
841                unsafe { Self::unchecked_transmute_from(byte) }
842            }
843        }
844        impl TryFromByte for $name {
845            fn try_from_byte(byte: u8) -> Option<Self> {
846                Self::try_from_primitive(byte).ok()
847            }
848        }
849    };
850}
851impl_from_byte!(RefType);
852impl_from_byte!(ValType);
853impl_from_byte!(Mut);
854impl_from_byte!(SectionId);