renderling/
bits.rs

1//! Helpers for bitwise operations.
2
3use core::ops::RangeInclusive;
4
5use crabslab::{Id, Slab};
6
7/// Statically define a shift/mask range as a literal range of bits.
8pub const fn bits(range: RangeInclusive<u32>) -> (u32, u32) {
9    let mut start = *range.start();
10    let end = *range.end();
11    let mut mask = 0;
12    while start <= end {
13        mask = (mask << 1) | 1;
14        start += 1;
15    }
16    (*range.start(), mask)
17}
18
19/// Insert the value of the bits defined by the shift/mask range.
20pub fn insert(bits: &mut u32, (shift, mask): (u32, u32), value: u32) {
21    // rotate right
22    if shift >= 1 {
23        *bits = (*bits >> shift) | (*bits << (32 - shift));
24    }
25    // unset
26    *bits &= !mask;
27    // set
28    *bits |= value & mask;
29    // unrotate (rotate left)
30    if shift >= 1 {
31        *bits = (*bits << shift) | (*bits >> (32 - shift));
32    }
33}
34
35/// Extract the value of the bits defined by the shift/mask range.
36pub fn extract(bits: u32, (shift, mask): (u32, u32)) -> u32 {
37    (bits >> shift) & mask
38}
39
40/// The shift/mask range for the first 8 bits of a u32.
41pub const U8_0_BITS: (u32, u32) = bits(0..=7);
42/// The shift/mask range for the second 8 bits of a u32.
43pub const U8_1_BITS: (u32, u32) = bits(8..=15);
44/// The shift/mask range for the third 8 bits of a u32.
45pub const U8_2_BITS: (u32, u32) = bits(16..=23);
46/// The shift/mask range for the fourth 8 bits of a u32.
47pub const U8_3_BITS: (u32, u32) = bits(24..=31);
48
49/// The shift/mask range for the first 16 bits of a u32.
50pub const U16_0_BITS: (u32, u32) = bits(0..=15);
51/// The shift/mask range for the second 16 bits of a u32.
52pub const U16_1_BITS: (u32, u32) = bits(16..=31);
53
54/// Extract 8 bits of the u32 at the given index in the slab.
55///
56/// Returns the extracted value, the index of the next component and the index
57/// of the next u32 in the slab.
58pub fn extract_u8(
59    // index of the u32 in the slab
60    u32_index: usize,
61    // eg 0 for the first 8 bits, 1 for the second 8 bits, etc
62    byte_offset: usize,
63    // slab of u32s
64    slab: &[u32],
65) -> (u32, usize, usize) {
66    const SHIFT_MASKS: [((u32, u32), usize); 4] = [
67        (U8_0_BITS, 0),
68        (U8_1_BITS, 0),
69        (U8_2_BITS, 0),
70        (U8_3_BITS, 1),
71    ];
72    let byte_mod = byte_offset % 4;
73    let (shift_mask, index_inc) = SHIFT_MASKS[byte_mod];
74    let u32_value = slab.read(Id::from(u32_index));
75    let value = extract(u32_value, shift_mask);
76    (value, u32_index + index_inc, byte_mod + 1)
77}
78
79/// Extract 8 bits of the u32 at the given index in the slab.
80///
81/// Returns the extracted value, the index of the next component and the index
82/// of the next u32 in the slab.
83pub fn extract_i8(
84    // index of the u32 in the slab
85    u32_index: usize,
86    // eg 0 for the first 8 bits, 1 for the second 8 bits, etc
87    byte_offset: usize,
88    // slab of u32s
89    slab: &[u32],
90) -> (i32, usize, usize) {
91    let (value, u32_index, n) = extract_u8(u32_index, byte_offset, slab);
92    let value: i32 = (value as i32 & 0xFF) - ((value as i32 & 0x80) << 1);
93    (value, u32_index, n)
94}
95
96/// Extract 16 bits of the u32 at the given index in the slab.
97pub fn extract_u16(
98    // index of the u32 in the slab
99    u32_index: usize,
100    // eg 0 for the first 16 bits, 2 for the second 16 bits, etc
101    byte_offset: usize,
102    // slab of u32s
103    slab: &[u32],
104) -> (u32, usize, usize) {
105    // NOTE: This should only have two entries, but we'll still handle the case
106    // where the extraction is not aligned to a u32 boundary by reading as if it
107    // were, and then re-aligning.
108    const SHIFT_MASKS: [((u32, u32), usize, usize); 4] = [
109        (U16_0_BITS, 2, 0),
110        (U16_0_BITS, 2, 0),
111        (U16_1_BITS, 0, 1),
112        (U16_1_BITS, 0, 1),
113    ];
114    let byte_mod = byte_offset % 4;
115    crate::println!("byte_mod: {byte_mod}");
116    let (shift_mask, next_byte_offset, index_inc) = SHIFT_MASKS[byte_mod];
117    let u32_value = slab.read(Id::from(u32_index));
118    crate::println!("u32: {:032b}", u32_value);
119    let value = extract(u32_value, shift_mask);
120    crate::println!("u16: {:016b}", value);
121    crate::println!("u32: {:?}", u32_value);
122    (value, u32_index + index_inc, next_byte_offset)
123}
124
125/// Extract 16 bits of the u32 at the given index in the slab.
126pub fn extract_i16(
127    // index of the u32 in the slab
128    u32_index: usize,
129    // eg 0 for the first 16 bits, 1 for the second 16 bits, etc
130    byte_offset: usize,
131    // slab of u32s
132    slab: &[u32],
133) -> (i32, usize, usize) {
134    let (value, u32_index, n) = extract_u16(u32_index, byte_offset, slab);
135    let value: i32 = (value as i32 & 0xFFFF) - ((value as i32 & 0x8000) << 1);
136    (value, u32_index, n)
137}
138
139/// Extract 32 bits of the u32 at the given index in the slab.
140pub fn extract_u32(
141    // index of the u32 in the slab
142    u32_index: usize,
143    // ignored and always passed back as `0`
144    _byte_offset: usize,
145    // slab of u32s
146    slab: &[u32],
147) -> (u32, usize, usize) {
148    (slab.read(Id::from(u32_index)), u32_index + 1, 0)
149}
150
151/// Extract 32 bits of the u32 at the given index in the slab.
152pub fn extract_i32(
153    // index of the u32 in the slab
154    u32_index: usize,
155    // ignored and always passed back as `0`
156    _byte_offset: usize,
157    // slab of u32s
158    slab: &[u32],
159) -> (i32, usize, usize) {
160    let (value, _, _) = extract_u32(u32_index, 0, slab);
161    (value as i32, u32_index + 1, 0)
162}
163
164/// Extract 32 bits of the u32 at the given index in the slab.
165pub fn extract_f32(
166    // index of the u32 in the slab
167    u32_index: usize,
168    // ignored and always passed back as `0`
169    _byte_offset: usize,
170    // slab of u32s
171    slab: &[u32],
172) -> (f32, usize, usize) {
173    let (value, _, _) = extract_u32(u32_index, 0, slab);
174    (f32::from_bits(value), u32_index + 1, 0)
175}
176
177#[cfg(test)]
178mod test {
179    use super::*;
180
181    #[test]
182    fn bits_sanity() {
183        let mut store = 0;
184        assert_eq!(
185            "00000000000000000000000000000000",
186            &format!("{:032b}", store)
187        );
188        insert(&mut store, bits(0..=7), u8::MAX as u32);
189        assert_eq!(
190            "00000000000000000000000011111111",
191            &format!("{:032b}", store)
192        );
193        store = 0;
194        insert(&mut store, bits(8..=15), u8::MAX as u32);
195        assert_eq!(
196            "00000000000000001111111100000000",
197            &format!("{:032b}", store)
198        );
199    }
200
201    #[test]
202    fn bits_u8_sanity() {
203        let mut bits = 0;
204        println!("bits: {:032b}", bits);
205        super::insert(&mut bits, super::U8_0_BITS, 6u8 as u32);
206        println!("bits: {:032b}", bits);
207        assert_eq!(super::extract(bits, super::U8_0_BITS), 6);
208        super::insert(&mut bits, super::U8_1_BITS, 5u8 as u32);
209        println!("bits: {:032b}", bits);
210        assert_eq!(super::extract(bits, super::U8_0_BITS), 6);
211        assert_eq!(super::extract(bits, super::U8_1_BITS), 5);
212        super::insert(&mut bits, super::U8_2_BITS, 4u8 as u32);
213        println!("bits: {:032b}", bits);
214        assert_eq!(super::extract(bits, super::U8_0_BITS), 6);
215        assert_eq!(super::extract(bits, super::U8_1_BITS), 5);
216        assert_eq!(super::extract(bits, super::U8_2_BITS), 4);
217        super::insert(&mut bits, super::U8_3_BITS, 3u8 as u32);
218        println!("bits: {:032b}", bits);
219        assert_eq!(super::extract(bits, super::U8_0_BITS), 6);
220        assert_eq!(super::extract(bits, super::U8_1_BITS), 5);
221        assert_eq!(super::extract(bits, super::U8_2_BITS), 4);
222        assert_eq!(super::extract(bits, super::U8_3_BITS), 3);
223    }
224
225    #[test]
226    fn extract_u8_sanity() {
227        let u8_slab = [0u8, 1u8, 2u8, 3u8, 4u8, 5u8, 0u8, 0u8];
228        let u32_slab: &[u32] = bytemuck::cast_slice(&u8_slab);
229        let index = 0;
230        let n = 0;
231        let (a, index, n) = extract_u8(index, n, u32_slab);
232        let (b, index, n) = extract_u8(index, n, u32_slab);
233        let (c, index, n) = extract_u8(index, n, u32_slab);
234        let (d, index, n) = extract_u8(index, n, u32_slab);
235        let (e, index, n) = extract_u8(index, n, u32_slab);
236        let (f, _, _) = extract_u8(index, n, u32_slab);
237        assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]);
238    }
239
240    #[test]
241    fn extract_i8_sanity() {
242        let i8_slab = [0i8, -1i8, -2i8, -3i8, 4i8, 5i8, 0i8, 0i8];
243        let u32_slab: &[u32] = bytemuck::cast_slice(&i8_slab);
244        let index = 0;
245        let n = 0;
246        let (a, index, n) = extract_i8(index, n, u32_slab);
247        let (b, index, n) = extract_i8(index, n, u32_slab);
248        let (c, index, n) = extract_i8(index, n, u32_slab);
249        let (d, index, n) = extract_i8(index, n, u32_slab);
250        let (e, index, n) = extract_i8(index, n, u32_slab);
251        let (f, _, _) = extract_i8(index, n, u32_slab);
252        assert_eq!([0, -1, -2, -3, 4, 5], [a, b, c, d, e, f]);
253    }
254
255    #[test]
256    fn extract_u16_sanity() {
257        let u16_slab = [0u16, 1u16, 2u16, 3u16, 4u16, 5u16];
258        let u32_slab: &[u32] = bytemuck::cast_slice(&u16_slab);
259        let index = 0;
260        let n = 0;
261        let (a, index, n) = extract_u16(index, n, u32_slab);
262        let (b, index, n) = extract_u16(index, n, u32_slab);
263        let (c, index, n) = extract_u16(index, n, u32_slab);
264        let (d, index, n) = extract_u16(index, n, u32_slab);
265        let (e, index, n) = extract_u16(index, n, u32_slab);
266        let (f, _, _) = extract_u16(index, n, u32_slab);
267        assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]);
268    }
269
270    #[test]
271    fn extract_i16_sanity() {
272        let i16_slab = [0i16, -1i16, -2i16, -3i16, 4i16, 5i16, -12345i16, 0i16];
273        let u32_slab: &[u32] = bytemuck::cast_slice(&i16_slab);
274        let index = 0;
275        let n = 0;
276        let (a, index, n) = extract_i16(index, n, u32_slab);
277        let (b, index, n) = extract_i16(index, n, u32_slab);
278        let (c, index, n) = extract_i16(index, n, u32_slab);
279        let (d, index, n) = extract_i16(index, n, u32_slab);
280        let (e, index, n) = extract_i16(index, n, u32_slab);
281        let (f, index, n) = extract_i16(index, n, u32_slab);
282        let (g, _, _) = extract_i16(index, n, u32_slab);
283        assert_eq!([0, -1, -2, -3, 4, 5, -12345], [a, b, c, d, e, f, g]);
284    }
285
286    #[test]
287    fn extract_u32_sanity() {
288        let u32_slab = [0u32, 1u32, 2u32, 3u32, 4u32, 5u32];
289        let u32_slab: &[u32] = bytemuck::cast_slice(&u32_slab);
290        let index = 0;
291        let n = 0;
292        let (a, index, n) = extract_u32(index, n, u32_slab);
293        let (b, index, n) = extract_u32(index, n, u32_slab);
294        let (c, index, n) = extract_u32(index, n, u32_slab);
295        let (d, index, n) = extract_u32(index, n, u32_slab);
296        let (e, index, n) = extract_u32(index, n, u32_slab);
297        let (f, _, _) = extract_u32(index, n, u32_slab);
298        assert_eq!([0, 1, 2, 3, 4, 5], [a, b, c, d, e, f]);
299    }
300
301    #[test]
302    fn extract_i32_sanity() {
303        let i32_slab = [0i32, -1i32, -2i32, -3i32, 4i32, 5i32, -12345i32];
304        let u32_slab: &[u32] = bytemuck::cast_slice(&i32_slab);
305        let index = 0;
306        let n = 0;
307        let (a, index, n) = extract_i32(index, n, u32_slab);
308        let (b, index, n) = extract_i32(index, n, u32_slab);
309        let (c, index, n) = extract_i32(index, n, u32_slab);
310        let (d, index, n) = extract_i32(index, n, u32_slab);
311        let (e, index, n) = extract_i32(index, n, u32_slab);
312        let (f, index, n) = extract_i32(index, n, u32_slab);
313        let (g, _, _) = extract_i32(index, n, u32_slab);
314        assert_eq!([0, -1, -2, -3, 4, 5, -12345], [a, b, c, d, e, f, g]);
315    }
316
317    #[test]
318    fn extract_f32_sanity() {
319        let f32_slab = [
320            0.0f32,
321            -1.0f32,
322            -2.0f32,
323            -3.0f32,
324            4.0f32,
325            5.0f32,
326            -12345.0f32,
327        ];
328        let u32_slab: &[u32] = bytemuck::cast_slice(&f32_slab);
329        let index = 0;
330        let n = 0;
331        let (a, index, n) = extract_f32(index, n, u32_slab);
332        let (b, index, n) = extract_f32(index, n, u32_slab);
333        let (c, index, n) = extract_f32(index, n, u32_slab);
334        let (d, index, n) = extract_f32(index, n, u32_slab);
335        let (e, index, n) = extract_f32(index, n, u32_slab);
336        let (f, index, n) = extract_f32(index, n, u32_slab);
337        let (g, _, _) = extract_f32(index, n, u32_slab);
338        assert_eq!(
339            [0f32, -1f32, -2f32, -3f32, 4f32, 5f32, -12345f32],
340            [a, b, c, d, e, f, g]
341        );
342    }
343}