Skip to content

Commit 453a15d

Browse files
committed
🎨 Const transformation
Const mutable references are not stable yet, thus we fall back on moving the buffers in and out directly. The downside is additinal copies, even for optimized code.
1 parent 411f980 commit 453a15d

File tree

1 file changed

+147
-100
lines changed

1 file changed

+147
-100
lines changed

src/lib.rs

Lines changed: 147 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -15,120 +15,167 @@ pub use bitfield_struct_derive::bitfield;
1515
///
1616
/// FIXME: Use mutable reference as soon as `const_mut_refs` is stable
1717
#[inline(always)]
18-
pub fn bit_copy(dst: &mut [u8], dst_off: usize, src: &[u8], src_off: usize, len: usize) {
18+
pub const fn bit_copy<const D: usize>(
19+
mut dst: [u8; D],
20+
dst_off: usize,
21+
src: &[u8],
22+
src_off: usize,
23+
len: usize,
24+
) -> [u8; D] {
1925
debug_assert!(len > 0);
2026
debug_assert!(dst.len() * 8 >= dst_off + len);
2127
debug_assert!(src.len() * 8 >= src_off + len);
2228

23-
// normalize input
24-
let dst = &mut dst[dst_off / 8..];
25-
let src = &src[src_off / 8..];
26-
let dst_off = dst_off % 8;
27-
let src_off = src_off % 8;
28-
29-
if len < (8 - dst_off) {
29+
if len == 1 {
30+
let dst_i = dst_off / 8;
31+
dst[dst_i] = single_bit(dst[dst_i], dst_off % 8, src, src_off);
32+
dst
33+
} else if len < (8 - (dst_off % 8)) {
3034
// edge case if there are less then one byte to be copied
31-
single_byte(&mut dst[0], dst_off, src, src_off, len);
32-
} else if dst_off == src_off {
33-
copy_aligned(dst, src, dst_off, len);
35+
let dst_i = dst_off / 8;
36+
dst[dst_i] = single_byte(dst[dst_i], dst_off % 8, src, src_off, len);
37+
dst
38+
} else if dst_off % 8 == src_off % 8 {
39+
copy_aligned(dst, dst_off / 8, src, src_off / 8, dst_off % 8, len)
40+
} else {
41+
copy_unaligned(dst, dst_off, src, src_off, len)
42+
}
43+
}
44+
45+
#[inline(always)]
46+
pub const fn is_bit_set(src: &[u8], i: usize) -> bool {
47+
debug_assert!(i < src.len() * 8);
48+
(src[i / 8] >> (i % 8)) & 1 != 0
49+
}
50+
51+
#[inline(always)]
52+
const fn single_bit(dst: u8, dst_off: usize, src: &[u8], src_off: usize) -> u8 {
53+
debug_assert!(dst_off < 8);
54+
if is_bit_set(src, src_off) {
55+
dst | (1 << dst_off)
3456
} else {
35-
copy_unaligned(dst, dst_off, src, src_off, len);
57+
dst & !(1 << dst_off)
3658
}
3759
}
3860

3961
#[inline(always)]
40-
fn single_byte(dst: &mut u8, dst_off: usize, src: &[u8], src_off: usize, len: usize) {
62+
const fn single_byte(dst: u8, dst_off: usize, src: &[u8], src_off: usize, len: usize) -> u8 {
63+
debug_assert!(dst_off < 8);
64+
65+
let src_i = src_off / 8;
66+
let src_off = src_off % 8;
67+
4168
let mask = (u8::MAX >> (8 - len)) << dst_off;
42-
*dst &= !mask;
43-
*dst |= ((src[0] >> src_off) << dst_off) & mask;
69+
let mut dst = dst & !mask;
70+
dst |= ((src[src_i] >> src_off) << dst_off) & mask;
4471

4572
// exceeding a single byte of the src buffer
4673
if len + src_off > 8 {
47-
*dst |= (src[1] << (8 - src_off + dst_off)) & mask;
74+
dst |= (src[src_i + 1] << (8 - src_off + dst_off)) & mask;
4875
}
76+
dst
4977
}
5078

5179
#[inline(always)]
52-
fn copy_unaligned(
53-
mut dst: &mut [u8],
54-
dst_off: usize,
55-
mut src: &[u8],
80+
const fn copy_unaligned<const D: usize>(
81+
mut dst: [u8; D],
82+
mut dst_off: usize,
83+
src: &[u8],
5684
mut src_off: usize,
5785
mut len: usize,
58-
) {
59-
debug_assert!(0 < dst_off && dst_off < 8 && 0 < src_off && src_off < 8);
86+
) -> [u8; D] {
87+
debug_assert!(src_off % 8 != 0 && dst_off % 8 != 0);
6088
debug_assert!(dst.len() * 8 >= dst_off + len);
6189
debug_assert!(src.len() * 8 >= src_off + len);
6290

91+
let mut dst_i = dst_off / 8;
92+
dst_off %= 8;
93+
let mut src_i = src_off / 8;
94+
src_off %= 8;
95+
6396
// copy dst prefix -> byte-align dst
6497
if dst_off > 0 {
6598
let prefix = 8 - dst_off;
6699
let mask = u8::MAX << dst_off;
67-
dst[0] &= !mask;
68-
dst[0] |= (src[0] >> src_off) << dst_off;
100+
dst[dst_i] &= !mask;
101+
dst[dst_i] |= (src[src_i] >> src_off) << dst_off;
69102

70103
// exceeding a single byte of the src buffer
71104
src_off += prefix;
72105
if let Some(reminder) = src_off.checked_sub(8) {
73-
src = &src[1..];
106+
src_i += 1;
74107
if reminder > 0 {
75-
dst[0] |= src[0] << (dst_off + reminder)
108+
dst[dst_i] |= src[src_i] << (dst_off + reminder)
76109
}
77110
src_off = reminder
78111
}
79-
dst = &mut dst[1..];
112+
dst_i += 1;
80113
len -= prefix;
81114
}
82115

83116
// copy middle
84-
for i in 0..len / 8 {
85-
dst[i] = (src[i] >> src_off) | (src[i + 1] << (8 - src_off));
117+
let mut i = 0;
118+
while i < len / 8 {
119+
dst[dst_i + i] = (src[src_i + i] >> src_off) | (src[src_i + i + 1] << (8 - src_off));
120+
i += 1;
86121
}
87122

88123
// suffix
89124
let suffix = len % 8;
90125
if suffix > 0 {
91126
let last = len / 8;
92127
let mask = u8::MAX >> (8 - suffix);
93-
dst[last] &= !mask;
94-
dst[last] |= src[last] >> src_off;
128+
dst[dst_i + last] &= !mask;
129+
dst[dst_i + last] |= src[src_i + last] >> src_off;
95130

96131
// exceeding a single byte of the src buffer
97132
if suffix + src_off > 8 {
98-
dst[last] |= (src[last + 1] << (8 - src_off)) & mask;
133+
dst[dst_i + last] |= (src[src_i + last + 1] << (8 - src_off)) & mask;
99134
}
100135
}
136+
dst
101137
}
102138
#[inline(always)]
103-
fn copy_aligned(mut dst: &mut [u8], mut src: &[u8], off: usize, mut len: usize) {
104-
debug_assert!(0 < off && off < 8);
105-
debug_assert!(dst.len() * 8 >= off + len);
106-
debug_assert!(src.len() * 8 >= off + len);
139+
const fn copy_aligned<const D: usize>(
140+
mut dst: [u8; D],
141+
mut dst_i: usize,
142+
src: &[u8],
143+
mut src_i: usize,
144+
off: usize,
145+
mut len: usize,
146+
) -> [u8; D] {
147+
debug_assert!(off < 8);
148+
debug_assert!(dst.len() * 8 >= dst_i * 8 + len);
149+
debug_assert!(src.len() * 8 >= src_i * 8 + len);
107150

108151
// copy prefix -> byte-align dst
109152
if off > 0 {
110-
let prefix = 8 - off;
111-
let mask = u8::MAX << off;
112-
dst[0] &= !mask;
113-
dst[0] |= src[0] & mask;
153+
let prefix = 8 - (off % 8);
154+
let mask = u8::MAX << (off % 8);
155+
dst[dst_i] &= !mask;
156+
dst[dst_i] |= src[src_i] & mask;
114157

115-
src = &src[1..];
116-
dst = &mut dst[1..];
158+
src_i += 1;
159+
dst_i += 1;
117160
len -= prefix;
118161
}
119162

120163
// copy middle
121-
let bytes = len / 8;
122-
dst[..bytes].copy_from_slice(&src[..bytes]);
164+
let mut i = 0;
165+
while i < len / 8 {
166+
dst[dst_i + i] = src[src_i + i];
167+
i += 1;
168+
}
123169

124170
// copy suffix
125171
let suffix = len % 8;
126172
if suffix > 0 {
127173
let last = len / 8;
128174
let mask = u8::MAX >> (8 - suffix);
129-
dst[last] &= !mask;
130-
dst[last] |= src[last];
175+
dst[dst_i + last] &= !mask;
176+
dst[dst_i + last] |= src[src_i + last];
131177
}
178+
dst
132179
}
133180

134181
#[cfg(test)]
@@ -140,90 +187,90 @@ mod test {
140187
#[test]
141188
fn copy_bits_single_bit() {
142189
// single byte
143-
let src = &[0b00100000];
144-
let dst = &mut [0b10111111];
145-
super::bit_copy(dst, 6, src, 5, 1);
146-
assert_eq!(dst, &[0b11111111]);
190+
let src = [0b00100000];
191+
let dst = [0b10111111];
192+
let dst = super::bit_copy(dst, 6, &src, 5, 1);
193+
assert_eq!(dst, [0b11111111]);
147194
// reversed
148-
let src = &[!0b00100000];
149-
let dst = &mut [!0b10111111];
150-
super::bit_copy(dst, 6, src, 5, 1);
151-
assert_eq!(dst, &[!0b11111111]);
195+
let src = [!0b00100000];
196+
let dst = [!0b10111111];
197+
let dst = super::bit_copy(dst, 6, &src, 5, 1);
198+
assert_eq!(dst, [!0b11111111]);
152199
}
153200

154201
#[test]
155202
fn copy_bits_single_byte() {
156203
// single byte
157-
let src = &[0b00111000];
158-
let dst = &mut [0b10001111];
159-
super::bit_copy(dst, 4, src, 3, 3);
160-
assert_eq!(dst, &[0b11111111]);
204+
let src = [0b00111000];
205+
let dst = [0b10001111];
206+
let dst = super::bit_copy(dst, 4, &src, 3, 3);
207+
assert_eq!(dst, [0b11111111]);
161208
// reversed
162-
let src = &[!0b00111000];
163-
let dst = &mut [!0b10001111];
164-
super::bit_copy(dst, 4, src, 3, 3);
165-
assert_eq!(dst, &[!0b11111111]);
209+
let src = [!0b00111000];
210+
let dst = [!0b10001111];
211+
let dst = super::bit_copy(dst, 4, &src, 3, 3);
212+
assert_eq!(dst, [!0b11111111]);
166213
}
167214

168215
#[test]
169216
fn copy_bits_unaligned() {
170217
// two to single byte
171-
let src = &[0b00000000, 0b11000000, 0b00000111, 0b00000000];
172-
let dst = &mut [0b00000000, 0b00000000, 0b00000000, 0b00000000];
173-
super::bit_copy(dst, 17, src, 14, 5);
174-
assert_eq!(dst, &[0b00000000, 0b00000000, 0b00111110, 0b0000000]);
218+
let src = [0b00000000, 0b11000000, 0b00000111, 0b00000000];
219+
let dst = [0b00000000, 0b00000000, 0b00000000, 0b00000000];
220+
let dst = super::bit_copy(dst, 17, &src, 14, 5);
221+
assert_eq!(dst, [0b00000000, 0b00000000, 0b00111110, 0b0000000]);
175222
// reversed
176-
let src = &[!0b00000000, !0b11000000, !0b00000111, !0b00000000];
177-
let dst = &mut [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
178-
super::bit_copy(dst, 17, src, 14, 5);
179-
assert_eq!(dst, &[!0b00000000, !0b00000000, !0b00111110, !0b0000000]);
223+
let src = [!0b00000000, !0b11000000, !0b00000111, !0b00000000];
224+
let dst = [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
225+
let dst = super::bit_copy(dst, 17, &src, 14, 5);
226+
assert_eq!(dst, [!0b00000000, !0b00000000, !0b00111110, !0b0000000]);
180227

181228
// over two bytes
182-
let src = &[0b00000000, 0b11000000, 0b00000111, 0b00000000];
183-
let dst = &mut [0b00000000, 0b00000000, 0b00000000, 0b00000000];
184-
super::bit_copy(dst, 23, src, 14, 5);
185-
assert_eq!(dst, &[0b00000000, 0b00000000, 0b10000000, 0b00001111]);
229+
let src = [0b00000000, 0b11000000, 0b00000111, 0b00000000];
230+
let dst = [0b00000000, 0b00000000, 0b00000000, 0b00000000];
231+
let dst = super::bit_copy(dst, 23, &src, 14, 5);
232+
assert_eq!(dst, [0b00000000, 0b00000000, 0b10000000, 0b00001111]);
186233
// reversed
187-
let src = &[!0b00000000, !0b11000000, !0b00000111, !0b00000000];
188-
let dst = &mut [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
189-
super::bit_copy(dst, 23, src, 14, 5);
190-
assert_eq!(dst, &[!0b00000000, !0b00000000, !0b10000000, !0b00001111]);
234+
let src = [!0b00000000, !0b11000000, !0b00000111, !0b00000000];
235+
let dst = [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
236+
let dst = super::bit_copy(dst, 23, &src, 14, 5);
237+
assert_eq!(dst, [!0b00000000, !0b00000000, !0b10000000, !0b00001111]);
191238

192239
// over three bytes
193-
let src = &[0b11000000, 0b11111111, 0b00000111, 0b00000000];
194-
let dst = &mut [0b00000000, 0b00000000, 0b00000000, 0b00000000];
195-
super::bit_copy(dst, 15, src, 6, 13);
196-
assert_eq!(dst, &[0b00000000, 0b10000000, 0b11111111, 0b00001111]);
240+
let src = [0b11000000, 0b11111111, 0b00000111, 0b00000000];
241+
let dst = [0b00000000, 0b00000000, 0b00000000, 0b00000000];
242+
let dst = super::bit_copy(dst, 15, &src, 6, 13);
243+
assert_eq!(dst, [0b00000000, 0b10000000, 0b11111111, 0b00001111]);
197244
// reversed
198-
let src = &[!0b11000000, !0b11111111, !0b00000111, !0b00000000];
199-
let dst = &mut [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
200-
super::bit_copy(dst, 15, src, 6, 13);
201-
assert_eq!(dst, &[!0b00000000, !0b10000000, !0b11111111, !0b00001111]);
245+
let src = [!0b11000000, !0b11111111, !0b00000111, !0b00000000];
246+
let dst = [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
247+
let dst = super::bit_copy(dst, 15, &src, 6, 13);
248+
assert_eq!(dst, [!0b00000000, !0b10000000, !0b11111111, !0b00001111]);
202249
}
203250

204251
#[test]
205252
fn copy_bits_aligned() {
206253
// over two bytes
207-
let src = &[0b00000000, 0b11000000, 0b00000111, 0b00000000];
208-
let dst = &mut [0b00000000, 0b00000000, 0b00000000, 0b00000000];
209-
super::bit_copy(dst, 14, src, 14, 5);
254+
let src = [0b00000000, 0b11000000, 0b00000111, 0b00000000];
255+
let dst = [0b00000000, 0b00000000, 0b00000000, 0b00000000];
256+
let dst = super::bit_copy(dst, 14, &src, 14, 5);
210257
assert_eq!(dst, src);
211258
// reversed
212-
let src = &[!0b00000000, !0b11000000, !0b00000111, !0b00000000];
213-
let dst = &mut [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
214-
super::bit_copy(dst, 14, src, 14, 5);
259+
let src = [!0b00000000, !0b11000000, !0b00000111, !0b00000000];
260+
let dst = [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
261+
let dst = super::bit_copy(dst, 14, &src, 14, 5);
215262
assert_eq!(dst, src);
216263

217264
// over three bytes
218-
let src = &[0b11000000, 0b11100111, 0b00000111, 0b00000000];
219-
let dst = &mut [0b00000000, 0b00000000, 0b00000000, 0b00000000];
220-
super::bit_copy(dst, 14, src, 6, 13);
221-
assert_eq!(dst, &[0b00000000, 0b11000000, 0b11100111, 0b00000111]);
265+
let src = [0b11000000, 0b11100111, 0b00000111, 0b00000000];
266+
let dst = [0b00000000, 0b00000000, 0b00000000, 0b00000000];
267+
let dst = super::bit_copy(dst, 14, &src, 6, 13);
268+
assert_eq!(dst, [0b00000000, 0b11000000, 0b11100111, 0b00000111]);
222269
// reversed
223-
let src = &[!0b11000000, !0b11100111, !0b00000111, !0b00000000];
224-
let dst = &mut [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
225-
super::bit_copy(dst, 14, src, 6, 13);
226-
assert_eq!(dst, &[!0b00000000, !0b11000000, !0b11100111, !0b00000111]);
270+
let src = [!0b11000000, !0b11100111, !0b00000111, !0b00000000];
271+
let dst = [!0b00000000, !0b00000000, !0b00000000, !0b00000000];
272+
let dst = super::bit_copy(dst, 14, &src, 6, 13);
273+
assert_eq!(dst, [!0b00000000, !0b11000000, !0b11100111, !0b00000111]);
227274
}
228275

229276
#[test]

0 commit comments

Comments
 (0)