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