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