1 /// Represents the possible sizes in bytes of the discriminant of a variant type in the component model
2 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3 pub enum DiscriminantSize {
4     /// 8-bit discriminant
5     Size1,
6     /// 16-bit discriminant
7     Size2,
8     /// 32-bit discriminant
9     Size4,
10 }
11 
12 impl DiscriminantSize {
13     /// Calculate the size of discriminant needed to represent a variant with the specified number of cases.
14     pub const fn from_count(count: usize) -> Option<Self> {
15         if count <= 0xFF {
16             Some(Self::Size1)
17         } else if count <= 0xFFFF {
18             Some(Self::Size2)
19         } else if count <= 0xFFFF_FFFF {
20             Some(Self::Size4)
21         } else {
22             None
23         }
24     }
25 
26     /// Returns the size, in bytes, of this discriminant
27     pub const fn byte_size(&self) -> u32 {
28         match self {
29             DiscriminantSize::Size1 => 1,
30             DiscriminantSize::Size2 => 2,
31             DiscriminantSize::Size4 => 4,
32         }
33     }
34 }
35 
36 impl From<DiscriminantSize> for u32 {
37     /// Size of the discriminant as a `u32`
38     fn from(size: DiscriminantSize) -> u32 {
39         size.byte_size()
40     }
41 }
42 
43 impl From<DiscriminantSize> for usize {
44     /// Size of the discriminant as a `usize`
45     fn from(size: DiscriminantSize) -> usize {
46         match size {
47             DiscriminantSize::Size1 => 1,
48             DiscriminantSize::Size2 => 2,
49             DiscriminantSize::Size4 => 4,
50         }
51     }
52 }
53 
54 /// Represents the number of bytes required to store a flags value in the component model
55 pub enum FlagsSize {
56     /// There are no flags
57     Size0,
58     /// Flags can fit in a u8
59     Size1,
60     /// Flags can fit in a u16
61     Size2,
62     /// Flags can fit in a specified number of u32 fields
63     Size4Plus(u8),
64 }
65 
66 impl FlagsSize {
67     /// Calculate the size needed to represent a value with the specified number of flags.
68     pub const fn from_count(count: usize) -> FlagsSize {
69         if count == 0 {
70             FlagsSize::Size0
71         } else if count <= 8 {
72             FlagsSize::Size1
73         } else if count <= 16 {
74             FlagsSize::Size2
75         } else {
76             let amt = ceiling_divide(count, 32);
77             if amt > (u8::MAX as usize) {
78                 panic!("too many flags");
79             }
80             FlagsSize::Size4Plus(amt as u8)
81         }
82     }
83 }
84 
85 /// Divide `n` by `d`, rounding up in the case of a non-zero remainder.
86 const fn ceiling_divide(n: usize, d: usize) -> usize {
87     (n + d - 1) / d
88 }
89 
90 /// A simple bump allocator which can be used with modules
91 pub const REALLOC_AND_FREE: &str = r#"
92     (global $last (mut i32) (i32.const 8))
93     (func $realloc (export "realloc")
94         (param $old_ptr i32)
95         (param $old_size i32)
96         (param $align i32)
97         (param $new_size i32)
98         (result i32)
99 
100         (local $ret i32)
101 
102         ;; Test if the old pointer is non-null
103         local.get $old_ptr
104         if
105             ;; If the old size is bigger than the new size then
106             ;; this is a shrink and transparently allow it
107             local.get $old_size
108             local.get $new_size
109             i32.gt_u
110             if
111                 local.get $old_ptr
112                 return
113             end
114 
115             ;; otherwise fall through to allocate a new chunk which will later
116             ;; copy data over
117         end
118 
119         ;; align up `$last`
120         (global.set $last
121             (i32.and
122                 (i32.add
123                     (global.get $last)
124                     (i32.add
125                         (local.get $align)
126                         (i32.const -1)))
127                 (i32.xor
128                     (i32.add
129                         (local.get $align)
130                         (i32.const -1))
131                     (i32.const -1))))
132 
133         ;; save the current value of `$last` as the return value
134         global.get $last
135         local.set $ret
136 
137         ;; bump our pointer
138         (global.set $last
139             (i32.add
140                 (global.get $last)
141                 (local.get $new_size)))
142 
143         ;; while `memory.size` is less than `$last`, grow memory
144         ;; by one page
145         (loop $loop
146             (if
147                 (i32.lt_u
148                     (i32.mul (memory.size) (i32.const 65536))
149                     (global.get $last))
150                 (then
151                     i32.const 1
152                     memory.grow
153                     ;; test to make sure growth succeeded
154                     i32.const -1
155                     i32.eq
156                     if unreachable end
157 
158                     br $loop)))
159 
160 
161         ;; ensure anything necessary is set to valid data by spraying a bit
162         ;; pattern that is invalid
163         local.get $ret
164         i32.const 0xde
165         local.get $new_size
166         memory.fill
167 
168         ;; If the old pointer is present then that means this was a reallocation
169         ;; of an existing chunk which means the existing data must be copied.
170         local.get $old_ptr
171         if
172             local.get $ret          ;; destination
173             local.get $old_ptr      ;; source
174             local.get $old_size     ;; size
175             memory.copy
176         end
177 
178         local.get $ret
179     )
180 "#;
181