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 = 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. 88 const 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 93 pub 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 "#; 183