wasmtime_component_util/
lib.rs

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)]
5pub enum DiscriminantSize {
6    /// 8-bit discriminant
7    Size1,
8    /// 16-bit discriminant
9    Size2,
10    /// 32-bit discriminant
11    Size4,
12}
13
14impl 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
38impl 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
45impl 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
57pub 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
68impl 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.
88const 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
93pub 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"#;