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