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 = 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
88pub 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"#;