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(usize),
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             FlagsSize::Size4Plus(ceiling_divide(count, 32))
77         }
78     }
79 }
80 
81 /// Divide `n` by `d`, rounding up in the case of a non-zero remainder.
82 const fn ceiling_divide(n: usize, d: usize) -> usize {
83     (n + d - 1) / d
84 }
85 
86 /// A simple bump allocator which can be used with modules
87 pub const REALLOC_AND_FREE: &str = r#"
88     (global $last (mut i32) (i32.const 8))
89     (func $realloc (export "realloc")
90         (param $old_ptr i32)
91         (param $old_size i32)
92         (param $align i32)
93         (param $new_size i32)
94         (result i32)
95 
96         (local $ret i32)
97 
98         ;; Test if the old pointer is non-null
99         local.get $old_ptr
100         if
101             ;; If the old size is bigger than the new size then
102             ;; this is a shrink and transparently allow it
103             local.get $old_size
104             local.get $new_size
105             i32.gt_u
106             if
107                 local.get $old_ptr
108                 return
109             end
110 
111             ;; otherwise fall through to allocate a new chunk which will later
112             ;; copy data over
113         end
114 
115         ;; align up `$last`
116         (global.set $last
117             (i32.and
118                 (i32.add
119                     (global.get $last)
120                     (i32.add
121                         (local.get $align)
122                         (i32.const -1)))
123                 (i32.xor
124                     (i32.add
125                         (local.get $align)
126                         (i32.const -1))
127                     (i32.const -1))))
128 
129         ;; save the current value of `$last` as the return value
130         global.get $last
131         local.set $ret
132 
133         ;; bump our pointer
134         (global.set $last
135             (i32.add
136                 (global.get $last)
137                 (local.get $new_size)))
138 
139         ;; while `memory.size` is less than `$last`, grow memory
140         ;; by one page
141         (loop $loop
142             (if
143                 (i32.lt_u
144                     (i32.mul (memory.size) (i32.const 65536))
145                     (global.get $last))
146                 (then
147                     i32.const 1
148                     memory.grow
149                     ;; test to make sure growth succeeded
150                     i32.const -1
151                     i32.eq
152                     if unreachable end
153 
154                     br $loop)))
155 
156 
157         ;; ensure anything necessary is set to valid data by spraying a bit
158         ;; pattern that is invalid
159         local.get $ret
160         i32.const 0xde
161         local.get $new_size
162         memory.fill
163 
164         ;; If the old pointer is present then that means this was a reallocation
165         ;; of an existing chunk which means the existing data must be copied.
166         local.get $old_ptr
167         if
168             local.get $ret          ;; destination
169             local.get $old_ptr      ;; source
170             local.get $old_size     ;; size
171             memory.copy
172         end
173 
174         local.get $ret
175     )
176 "#;
177