wasmtime_internal_component_util/
lib.rs

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)]
12pub enum DiscriminantSize {
13    /// 8-bit discriminant
14    Size1,
15    /// 16-bit discriminant
16    Size2,
17    /// 32-bit discriminant
18    Size4,
19}
20
21impl DiscriminantSize {
22    /// Calculate the size of discriminant needed to represent a variant with the specified number of cases.
23    pub const fn from_count(count: usize) -> Option<Self> {
24        if count <= 0xFF {
25            Some(Self::Size1)
26        } else if count <= 0xFFFF {
27            Some(Self::Size2)
28        } else if count <= 0xFFFF_FFFF {
29            Some(Self::Size4)
30        } else {
31            None
32        }
33    }
34
35    /// Returns the size, in bytes, of this discriminant
36    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
45impl From<DiscriminantSize> for u32 {
46    /// Size of the discriminant as a `u32`
47    fn from(size: DiscriminantSize) -> u32 {
48        size.byte_size()
49    }
50}
51
52impl From<DiscriminantSize> for usize {
53    /// Size of the discriminant as a `usize`
54    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
64pub 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
75impl FlagsSize {
76    /// Calculate the size needed to represent a value with the specified number of flags.
77    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
95pub 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"#;