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