1#![no_std]
20#![deny(missing_docs)]
21
22use core::iter::{DoubleEndedIterator, ExactSizeIterator};
23use core::mem::size_of;
24use core::ops::{Bound, RangeBounds};
25
26pub trait BitRangeRead<U> {
28 fn range_read_le<R: RangeBounds<usize>>(self, range: R) -> U;
32 fn range_read_be<R: RangeBounds<usize>>(self, range: R) -> U;
34}
35
36pub trait BitRangeWrite<U> {
38 fn range_write_le<R: RangeBounds<usize>>(self, range: R, value: U);
42 fn range_write_be<R: RangeBounds<usize>>(self, range: R, value: U);
44}
45
46macro_rules! impl_bit_range_slice {
70 ($($numeric:ty,)*) => {$(
71 impl BitRangeRead<$numeric> for &'_ [u8] {
72 #[cfg_attr(feature = "enable-inline", inline)]
73 #[cfg_attr(feature = "never-inline", inline(never))]
74 fn range_read_le<R: RangeBounds<usize>>(self, range: R) -> $numeric {
75 let res: u64 = bit_range_read_le_iter_impl(self.iter(), range);
76 res as $numeric
77 }
78
79 #[cfg_attr(feature = "enable-inline", inline)]
80 #[cfg_attr(feature = "never-inline", inline(never))]
81 fn range_read_be<R: RangeBounds<usize>>(self, range: R) -> $numeric {
82 let res: u64 = bit_range_read_le_iter_impl(self.iter().rev(), range);
83 res as $numeric
84 }
85 })*
86 }
87}
88
89impl_bit_range_slice!(u8, u16, u32, u64,);
91
92macro_rules! impl_bit_range_write_slice {
93 ($($numeric:ty,)*) => {$(
94 impl BitRangeWrite<$numeric> for &'_ mut [u8] {
95 #[cfg_attr(feature = "enable-inline", inline)]
96 #[cfg_attr(feature = "never-inline", inline(never))]
97 fn range_write_le<R: RangeBounds<usize>>(self, range: R, value: $numeric) {
98 write_le_compound(self, value as u64, range);
99 }
100
101 #[cfg_attr(feature = "enable-inline", inline)]
102 #[cfg_attr(feature = "never-inline", inline(never))]
103 fn range_write_be<R: RangeBounds<usize>>(self, range: R, value: $numeric) {
104 write_be_compound(self, value as u64, range);
105 }
106 })*
107 }
108}
109
110impl_bit_range_write_slice!(u8, u16, i32, u32, u64,);
112
113#[inline(always)]
115fn setup_iter<'a, R>(input_len: usize, range: R) -> (usize, usize, usize, usize)
116where
117 R: RangeBounds<usize>,
118{
119 let start_bit = match range.start_bound() {
120 Bound::Included(start) => *start,
121 Bound::Excluded(start) => *start + 1,
122 Bound::Unbounded => 0,
123 };
124 let end_bit = match range.end_bound() {
125 Bound::Included(end) => *end,
126 Bound::Excluded(end) => *end - 1,
127 Bound::Unbounded => size_of::<u64>() * 8 - 1,
128 };
129 let total_bits = end_bit - start_bit + 1;
130 let start_byte = start_bit / 8;
131 let end_byte = (end_bit / 8).min(input_len);
132
133 let start_bit = start_bit - start_byte * 8;
134
135 (start_bit, total_bits, start_byte, end_byte)
136}
137
138
139#[cfg_attr(feature = "enable-inline", inline)]
140#[cfg_attr(feature = "never-inline", inline(never))]
141fn bit_range_read_le_iter_impl<'a, I, R>(input: I, range: R) -> u64
142where
143 I: Iterator<Item = &'a u8> + DoubleEndedIterator + ExactSizeIterator,
144 R: RangeBounds<usize>,
145{
146 let (start_bit, total_bits, start_byte, end_byte) = setup_iter(input.len(), range);
147
148 let iter = input.skip(start_byte).take(end_byte + 1);
149
150 let mask = (1 << total_bits) - 1;
152 let mut output = read_u128_le(iter);
153 output >>= start_bit;
154 output &= mask;
155
156 output as u64
157}
158
159#[cfg_attr(feature = "enable-inline", inline)]
160#[cfg_attr(feature = "never-inline", inline(never))]
161fn write_le_compound<R>(output: &mut [u8], val: u64, range: R)
162where
163 R: RangeBounds<usize>,
164{
165 let (start_bit, total_bits, start_byte, end_byte) = setup_iter(output.len(), range);
166 let iter = output.iter().skip(start_byte).take(end_byte + 1);
167
168 let mut work_value = read_u128_le(iter);
170
171 let mask = ((1 << total_bits) - 1) << start_bit;
172 let val = ((val as u128) << start_bit) & mask;
173
174 work_value &= !mask;
176 work_value |= val;
177
178 let iter = output.iter_mut().skip(start_byte).take(end_byte + 1);
179
180 write_value_le(iter, work_value);
182}
183
184#[cfg_attr(feature = "enable-inline", inline)]
185#[cfg_attr(feature = "never-inline", inline(never))]
186fn write_be_compound<R>(output: &mut [u8], val: u64, range: R)
187where
188 R: RangeBounds<usize>,
189{
190 let (start_bit, total_bits, start_byte, end_byte) = setup_iter(output.len(), range);
191 let iter = output.iter().rev().skip(start_byte).take(end_byte + 1);
192
193 let mut work_value = read_u128_le(iter);
195
196 let mask = ((1 << total_bits) - 1) << start_bit;
197 let val = ((val as u128) << start_bit) & mask;
198
199 work_value &= !mask;
201 work_value |= val;
202
203 let iter = output.iter_mut().rev().skip(start_byte).take(end_byte + 1);
204
205 write_value_le(iter, work_value);
207}
208
209#[inline(always)]
210fn read_u128_le<'a, I>(input: I) -> u128
211where
212 I: Iterator<Item = &'a u8> + DoubleEndedIterator,
213{
214 input.rev().fold(0, |acc, x| (acc << 8) | *x as u128)
215}
216
217#[inline(always)]
218fn write_value_le<'a, O>(output: O, value: u128)
219where
220 O: Iterator<Item = &'a mut u8> + DoubleEndedIterator,
221{
222 let val_as_bytes = &value.to_be_bytes();
223 val_as_bytes
224 .iter()
225 .rev()
226 .zip(output)
227 .for_each(|(i, o)| *o = *i);
228}
229
230#[cfg(test)]
231#[macro_use]
232extern crate std;
233
234#[cfg(test)]
235mod tests;