1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use alloc::vec::Vec;
/// Pack signed 8-bit integer values into a sequence of unsigned 32-bit integers.
pub fn pack_i8s_to_u32s(values: Vec<i8>) -> Vec<u32> {
// Shift and combine groups of four 8-bit values into a u32.
// Same as doing this:
// let result = (d_u8 & 0xFF) << 24 | (c_u8 & 0xFF) << 16 | (b_u8 & 0xFF) << 8 | (a_u8 & 0xFF);
#[cfg(target_endian = "big")]
{
values
.chunks(4)
.map(|x| {
x.iter()
.enumerate()
.fold(0u32, |acc, (i, x)| acc | (*x as u32 & 0xFF) << (i * 8))
})
.collect()
}
// The order of bytes in little endian matches the above description, we just need to
// handle padding when the number of values is not a factor of 4
#[cfg(target_endian = "little")]
{
let mut values = values;
let remainder = values.len() % 4;
if remainder != 0 {
// Pad with zeros
values.extend(core::iter::repeat_n(0, 4 - remainder));
}
let len = values.len() / 4;
let capacity = values.capacity() / 4;
// Pre-forget the old vec and re-interpret as u32
let mut values = core::mem::ManuallyDrop::new(values);
let ptr = values.as_mut_ptr() as *mut u32;
unsafe { Vec::from_raw_parts(ptr, len, capacity) }
}
}
/// Unpack 32-bit unsigned integer values into a sequence of signed 8-bit integers.
pub fn unpack_u32s_to_i8s(values: Vec<u32>, numel: usize) -> Vec<i8> {
#[cfg(target_endian = "big")]
{
values
.into_iter()
.enumerate()
.flat_map(|(i, packed)| {
// A single u32 could contain less than four 8-bit values...
let n = core::cmp::min(4, numel - i * 4);
// Extract each 8-bit segment from u32 and cast back to i8
// Same as doing this (when 4 values are fully packed):
// let a = (packed & 0xFF) as i8;
// let b = ((packed >> 8) & 0xFF) as i8;
// let c = ((packed >> 16) & 0xFF) as i8;
// let d = ((packed >> 24) & 0xFF) as i8;
(0..n).map(move |i| (packed >> (i * 8) & 0xFF) as i8)
})
.collect()
}
// The order of bytes in little endian matches the above description, we just need to
// handle padding when the number of elements is not a factor of 4
#[cfg(target_endian = "little")]
{
let len = values.len() * 4;
let capacity = values.capacity() * 4;
// Pre-forget the old vec and re-interpret as u32
let mut values = core::mem::ManuallyDrop::new(values);
let ptr = values.as_mut_ptr() as *mut i8;
let mut vec = unsafe { Vec::from_raw_parts(ptr, len, capacity) };
let padding = len - numel;
if padding > 0 {
vec.truncate(len - padding);
}
vec
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn should_pack_i8s_to_u32() {
let packed = pack_i8s_to_u32s(vec![-128, 2, -3, 127]);
assert_eq!(packed, vec![2147287680]);
}
#[test]
fn should_pack_i8s_to_u32_padded() {
let packed = pack_i8s_to_u32s(vec![-128, 2, -3, 127, 55]);
let packed_padded = pack_i8s_to_u32s(vec![-128, 2, -3, 127, 55, 0, 0, 0]);
assert_eq!(packed, vec![2147287680, 55]);
assert_eq!(packed, packed_padded);
}
#[test]
fn should_unpack_u32s_to_i8s() {
let unpacked = unpack_u32s_to_i8s(vec![2147287680u32], 4);
assert_eq!(unpacked, vec![-128, 2, -3, 127]);
}
#[test]
fn should_unpack_u32s_to_i8s_padded() {
let unpacked = unpack_u32s_to_i8s(vec![55u32], 1);
assert_eq!(unpacked, vec![55]);
}
}