devela/data/codec/bit/wrapper/
primitives.rs

1// devela::data::codec::bit::ops::wrapper::primitives
2//
3//! Implements `Bitwise` for the integer primitives.
4//
5
6#[cfg(_bit··)]
7use crate::{
8    iif, Bitwise,
9    MismatchedBounds::{self, DataOverflow, IndexOutOfBounds, MismatchedIndices},
10};
11
12macro_rules! impl_bits_wrapper {
13    () => {
14        impl_bits_wrapper![
15            i8:"_bit_i8", i16:"_bit_i16", i32:"_bit_i32",
16            i64:"_bit_i64", i128:"_bit_i128", isize:"_bit_isize",
17            u8:"_bit_u8", u16:"_bit_u16", u32:"_bit_u32",
18            u64:"_bit_u64", u128:"_bit_u128", usize:"_bit_usize"
19        ];
20    };
21    ( $( $t:ty : $cap:literal ),+ ) => { $( impl_bits_wrapper![@$t : $cap]; )+ };
22
23    // `$t`: the primitive type
24    // $cap: the capability feature that enables the given implementation. E.g "_bit_u8".
25    (@$t:ty : $cap:literal) => {
26        /* impl traits */
27
28        #[doc = concat!["# Implementation for `", stringify!($t), "`."]]
29        #[cfg(feature = $cap )]
30        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
31        impl Bitwise::<$t> {
32            /* constants */
33
34            /// The size in bits.
35            pub const BITS: u32 = <$t>::BITS;
36
37            /* new mask */
38
39            /// Returns a new bitmask of 1s from the `[start..=end]` range.
40            ///
41            /// Sets the rest of the bits to 0.
42            ///
43            /// # Panics
44            /// Panics if `start >= BITS || end >= BITS || start > end`.
45            #[doc = include_str!("../benches/mask_range.md")]
46            #[must_use]
47            pub const fn mask_range(start: u32, end: u32) -> Self {
48                debug_assert![start <= end];
49                // a mask with all bits set, from 0 to end:
50                let mask_end = iif![end == <$t>::BITS -1; !0; (1 << (end + 1)) - 1];
51                // a mask with all bits set from 0 to start - 1:
52                let mask_start = iif![start == 0; 0; (1 << start) - 1];
53                Self(mask_end - mask_start)
54            }
55            /// Returns a new bitmask of ones from the `[start..=end]` checked range.
56            ///
57            /// Sets the rest of the bits to 0.
58            ///
59            /// # Errors
60            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
61            /// [`MismatchedIndices`] if `start > end`.
62            #[doc = include_str!("../benches/mask_checked_range.md")]
63            pub const fn mask_checked_range(start: u32, end: u32)
64                -> Result<Self, MismatchedBounds> {
65                if start >= <$t>::BITS {
66                    Err(IndexOutOfBounds(Some(start as usize)))
67                } else if end >= <$t>::BITS {
68                    Err(IndexOutOfBounds(Some(end as usize)))
69                } else if start > end {
70                    Err(MismatchedIndices)
71                } else {
72                    // create a mask with all bits set, from 0 to end:
73                    let mask_end = iif![end == <$t>::BITS -1; !0; (1 << (end + 1)) - 1];
74                    // create a mask with all bits set from 0 to start - 1:
75                    let mask_start = iif![start == 0; 0; (1 << start) - 1];
76                    Ok(Self(mask_end - mask_start))
77                }
78            }
79
80            /* get */
81
82            /// Gets the bits in `self` from the `[start..=end]` range.
83            ///
84            /// Sets the rest of the bits to 0.
85            /// # Panics
86            /// Panics if `start >= BITS || end >= BITS || start > end`.
87            #[must_use]
88            pub const fn get_range(self, start: u32, end: u32) -> Self {
89                Self(self.0 & Self::mask_range(start, end).0)
90            }
91
92            /// Gets the bits in `self` from the `[start..=end]` checked range.
93            ///
94            /// Sets the rest of the bits to 0.
95            /// # Errors
96            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
97            /// [`MismatchedIndices`] if `start > end`.
98            pub const fn get_checked_range(self, start: u32, end: u32)
99                -> Result<Self, MismatchedBounds> {
100                match Self::mask_checked_range(start, end) {
101                    Ok(mask) => Ok(Self(self.0 & mask.0)),
102                    Err(e) => Err(e),
103                }
104            }
105
106            /* get value */
107
108            /// Gets the value of the bits in `self` from the `[start..=end]` range.
109            ///
110            /// Sets the rest of the bits to 0.
111            ///
112            /// The bits in the specified range are shifted rightwards so that the least
113            /// significant bit (LSB) aligns with the units place, forming the integer value.
114            /// # Panics
115            /// Panics if `start >= BITS || end >= BITS || start > end`.
116            #[must_use]
117            pub const fn get_value_range(self, start: u32, end: u32) -> Self {
118                Self((self.0 & Self::mask_range(start, end).0) >> start)
119            }
120
121            /// Gets the value of the bits in `self` from the `[start..=end]` checked range.
122            ///
123            /// Sets the rest of the bits to 0.
124            ///
125            /// The bits in the specified range are shifted rightwards so that the least
126            /// significant bit (LSB) aligns with the units place, forming the integer value.
127            /// # Errors
128            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
129            /// [`MismatchedIndices`] if `start > end`.
130            pub const fn get_value_checked_range(self, start: u32, end: u32)
131                -> Result<Self, MismatchedBounds> {
132                match Self::mask_checked_range(start, end) {
133                    Ok(mask) => Ok(Self((self.0 & mask.0) >> start)),
134                    Err(e) => Err(e),
135                }
136            }
137
138            /* set */
139
140            /// Sets the bits in `self` to 1, from the `[start..=end]` range.
141            ///
142            /// Leaves the rest of the bits unchanged.
143            /// # Panics
144            /// Panics if `start >= BITS || end >= BITS || start > end`.
145            #[must_use]
146            pub const fn set_range(self, start: u32, end: u32) -> Self {
147                Self(self.0 | Self::mask_range(start, end).0)
148            }
149
150            /// Sets the bits in `self` to 1, from the `[start..=end]` checked range.
151            ///
152            /// Leaves the rest of the bits unchanged.
153            /// # Errors
154            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS`
155            /// and [`MismatchedIndices`] if `start > end`.
156            pub const fn set_checked_range(self, start: u32, end: u32)
157                -> Result<Self, MismatchedBounds> {
158                match Self::mask_checked_range(start, end) {
159                    Ok(mask) => Ok(Self(self.0 | mask.0)),
160                    Err(e) => Err(e),
161                }
162            }
163
164            /* set value */
165
166            /// Sets the given `value` into the bits from the `[start..=end]` range.
167            ///
168            /// Leaves the rest of the bits unchanged.
169            ///
170            /// The value is first masked to fit the size of the range, and then
171            /// it is inserted into the specified bit range of `self`, replacing
172            /// the existing bits in that range. The rest of the bits in `self` remain unchanged.
173            /// # Panics
174            /// Panics if `start >= BITS || end >= BITS || start > end`.
175            #[must_use]
176            pub const fn set_value_range(self, value: $t, start: u32, end: u32) -> Self {
177                let mask = Self::mask_range(start, end).0;
178                let value_shifted = (value << start) & mask;
179                Self((self.0 & !mask) | value_shifted)
180            }
181
182            /// Sets the given `value` into the bits from the `[start..=end]` checked range.
183            ///
184            /// Leaves the rest of the bits unchanged.
185            /// # Errors
186            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS`
187            /// and [`MismatchedIndices`] if `start > end`.
188            pub const fn set_value_checked_range(self, value: $t, start: u32, end: u32)
189                -> Result<Self, MismatchedBounds> {
190                match Self::mask_checked_range(start, end) {
191                    Ok(mask) => {
192                        let value_shifted = (value << start) & mask.0;
193                        Ok(Self((self.0 & !mask.0) | value_shifted))
194                    },
195                    Err(e) => Err(e),
196                }
197            }
198
199            /// Sets the given checked `value` into the bits from the `[start..=end]` checked range.
200            ///
201            /// Leaves the rest of the bits unchanged.
202            /// # Errors
203            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS`,
204            /// [`MismatchedIndices`] if `start > end`, and
205            /// [`DataOverflow`] if `value` does not fit within the specified bit range.
206            pub const fn set_checked_value_checked_range(self, value: $t, start: u32, end: u32)
207                -> Result<Self, MismatchedBounds> {
208                match Self::mask_checked_range(start, end) {
209                    Ok(mask) => {
210                        iif![value >= (1 << (end - start));
211                            return Err(DataOverflow(Some(value as usize)))];
212                        let value_shifted = (value << start) & mask.0;
213                        Ok(Self((self.0 & !mask.0) | value_shifted))
214                    },
215                    Err(e) => Err(e),
216                }
217            }
218
219            /* unset */
220
221            /// Unsets the bits in `self` to 0, from the `[start..=end]` range.
222            ///
223            /// Leaves the rest of the bits unchanged.
224            /// # Panics
225            /// Panics if `start >= BITS || end >= BITS || start > end`.
226            #[must_use]
227            pub const fn unset_range(self, start: u32, end: u32) -> Self {
228                Self(self.0 & !Self::mask_range(start, end).0)
229            }
230
231            /// Unsets the bits in `self` to 0, from the `[start..=end]` checked range.
232            ///
233            /// Leaves the rest of the bits unchanged.
234            /// # Errors
235            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
236            /// [`MismatchedIndices`] if `start > end`.
237            pub const fn unset_checked_range(self, start: u32, end: u32)
238                -> Result<Self, MismatchedBounds> {
239                match Self::mask_checked_range(start, end) {
240                    Ok(mask) => Ok(Self(self.0 & !mask.0)),
241                    Err(e) => Err(e),
242                }
243            }
244
245            /* flip */
246
247            /// Flips the bits in `self` from the `[start..=end]` range.
248            ///
249            /// Leaves the rest of the bits unchanged.
250            /// # Panics
251            /// Panics if `start >= BITS || end >= BITS || start > end`.
252            #[must_use]
253            pub const fn flip_range(self, start: u32, end: u32) -> Self {
254                Self(self.0 ^ Self::mask_range(start, end).0)
255            }
256
257            /// Flips the bits in `self` from the `[start..=end]` checked range.
258            ///
259            /// Leaves the rest of the bits unchanged.
260            /// # Errors
261            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
262            /// [`MismatchedIndices`] if `start > end`.
263            pub const fn flip_checked_range(self, start: u32, end: u32)
264                -> Result<Self, MismatchedBounds> {
265                match Self::mask_checked_range(start, end) {
266                    Ok(mask) => Ok(Self(self.0 ^ mask.0)),
267                    Err(e) => Err(e),
268                }
269            }
270
271            /* reverse */
272
273            /// Reverses the order of the bits in `self` from the `[start..=end]` range.
274            ///
275            /// Leaves the rest of the bits unchanged.
276            /// # Panics
277            /// Panics if `start >= BITS || end >= BITS || start > end`.
278            #[must_use]
279            pub const fn reverse_range(self, start: u32, end: u32) -> Self {
280                debug_assert![start <= end];
281                // If the entire range of bits is selected, simply reverse all bits
282                let range_bits = end - start + 1;
283                iif![range_bits == Self::BITS; return Self(self.0.reverse_bits())];
284                // Create the mask for the range and reverse its bits
285                let mask = (((1 as $t) << range_bits) - 1) << start;
286                let bits_to_rev = (self.0 & mask) >> start;
287                let rev = bits_to_rev.reverse_bits();
288                // Shift the reversed bits back to their original position
289                let rev_shifted = (rev >> (Self::BITS - range_bits)) << start;
290                // Combine with the original number, preserving bits outside the range
291                Self((self.0 & !mask) | rev_shifted)
292            }
293
294            /// Reverses the order of the bits in `self` from the `[start..=end]` checked range.
295            ///
296            /// Leaves the rest of the bits unchanged.
297            /// # Errors
298            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
299            /// [`MismatchedIndices`] if `start > end`.
300            pub const fn reverse_checked_range(self, start: u32, end: u32)
301                -> Result<Self, MismatchedBounds> {
302                if start >= Self::BITS {
303                    Err(IndexOutOfBounds(Some(start as usize)))
304                } else if end >= <$t>::BITS {
305                    Err(IndexOutOfBounds(Some(end as usize)))
306                } else if start > end {
307                    Err(MismatchedIndices)
308                } else {
309                    // If the entire range of bits is selected, simply reverse all bits
310                    let range_bits = end - start + 1;
311                    iif![range_bits == Self::BITS; return Ok(Self(self.0.reverse_bits()))];
312                    // Create the mask for the range and reverse its bits
313                    let mask = (((1 as $t) << range_bits) - 1) << start;
314                    let bits_to_rev = (self.0 & mask) >> start;
315                    let rev = bits_to_rev.reverse_bits();
316                    // Shift the reversed bits back to their original position
317                    let rev_shifted = (rev >> (Self::BITS - range_bits)) << start;
318                    // Combine with the original number, preserving bits outside the range
319                    Ok(Self((self.0 & !mask) | rev_shifted))
320                }
321            }
322
323            /* count */
324
325            /// Counts the number of 1s in `self` from the `[start..=end]` range.
326            /// # Panics
327            /// Panics if `start >= BITS || end >= BITS || start > end`.
328            #[must_use]
329            pub const fn count_ones_range(self, start: u32, end: u32) -> u32 {
330                let masked_bits = self.0 & Self::mask_range(start, end).0;
331                masked_bits.count_ones()
332            }
333            /// Counts the number of 1s in `self` from the `[start..=end]` checked range.
334            /// # Errors
335            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
336            /// [`MismatchedIndices`] if `start > end`.
337            pub const fn count_ones_checked_range(self, start: u32, end: u32)
338                -> Result<u32, MismatchedBounds> {
339                match Self::mask_checked_range(start, end) {
340                    Ok(mask) => Ok((self.0 & mask.0).count_ones()),
341                    Err(e) => Err(e),
342                }
343            }
344
345            /// Counts the number of 0s in `self` from the `[start..=end]` range.
346            /// # Panics
347            /// Panics if `start >= BITS || end >= BITS || start > end`.
348            #[must_use]
349            pub const fn count_zeros_range(self, start: u32, end: u32) -> u32 {
350                let mask = Self::mask_range(start, end).0;
351                let masked_bits = self.0 & mask;
352                (!masked_bits & mask).count_ones()
353            }
354
355            /// Counts the number of 0s in `self` from the `[start..=end]` checked range.
356            /// # Errors
357            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
358            /// [`MismatchedIndices`] if `start > end`.
359            pub const fn count_zeros_checked_range(self, start: u32, end: u32)
360                -> Result<u32, MismatchedBounds> {
361                match Self::mask_checked_range(start, end) {
362                    Ok(mask) => {
363                        let masked_bits = self.0 & mask.0;
364                        Ok((!masked_bits & mask.0).count_ones())
365                    },
366                    Err(e) => Err(e),
367                }
368            }
369
370            /* find first */
371
372            /// Finds the index of the first 1 in `self` from the `[start..=end]` range.
373            ///
374            /// Returns `None` if there are no bits set.
375            ///
376            /// The index is relative to the entire sequence of `self`, not to the given `start`.
377            /// # Panics
378            /// Panics if `start >= BITS || end >= BITS || start > end`.
379            #[must_use]
380            pub const fn find_first_one_range(self, start: u32, end: u32) -> Option<u32> {
381                let masked_bits = self.0 & Self::mask_range(start, end).0;
382                let mut idx = start;
383                while idx <= end {
384                    iif![(masked_bits & (1 << idx)) != 0; return Some(idx)];
385                    idx += 1;
386                }
387                None
388            }
389
390            /// Finds the index of the first 1 in `self` from the `[start..=end]` checked range.
391            ///
392            /// Returns `None` if there are no bits set.
393            ///
394            /// The index is relative to the entire sequence of `self`, not to the given `start`.
395            /// # Errors
396            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
397            /// [`MismatchedIndices`] if `start > end`.
398            pub const fn find_first_one_checked_range(self, start: u32, end: u32)
399                -> Result<Option<u32>, MismatchedBounds> {
400                match Self::mask_checked_range(start, end) {
401                    Ok(mask) => {
402                        let masked_bits = self.0 & mask.0;
403                        let mut idx = start;
404                        while idx <= end {
405                            iif![(masked_bits & (1 << idx)) != 0; return Ok(Some(idx))];
406                            idx += 1;
407                        }
408                        Ok(None)
409                    },
410                    Err(e) => Err(e),
411                }
412            }
413
414            /// Finds the index of the first 0 in `self` from the `[start..=end]` range.
415            ///
416            /// Returns `None` if there are no bits unset.
417            ///
418            /// The index is relative to the entire sequence of `self`, not to the given `start`.
419            /// # Panics
420            /// Panics if `start >= BITS || end >= BITS || start > end`.
421            #[must_use]
422            pub const fn find_first_zero_range(self, start: u32, end: u32)
423                -> Option<u32> {
424                let masked_bits = !(self.0 & Self::mask_range(start, end).0);
425                let mut idx = start;
426                while idx <= end {
427                    iif![(masked_bits & (1 << idx)) != 0; return Some(idx)];
428                    idx += 1;
429                }
430                None
431            }
432
433            /// Finds the index of the first 0 in `self` from the `[start..=end]` checked range.
434            ///
435            /// Returns `None` if there are no bits unset.
436            ///
437            /// The index is relative to the entire sequence of `self`, not to the given `start`.
438            /// # Errors
439            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
440            /// [`MismatchedIndices`] if `start > end`.
441            pub const fn find_first_zero_checked_range(self, start: u32, end: u32)
442                -> Result<Option<u32>, MismatchedBounds> {
443                match Self::mask_checked_range(start, end) {
444                    Ok(mask) => {
445                        let masked_bits = !(self.0 & mask.0);
446                        let mut idx = start;
447                        while idx <= end {
448                            iif![(masked_bits & (1 << idx)) != 0; return Ok(Some(idx))];
449                            idx += 1;
450                        }
451                        Ok(None)
452                    },
453                    Err(e) => Err(e),
454                }
455            }
456
457            /* find last */
458
459            /// Finds the index of the last 1 in `self` from the `[start..=end]` range.
460            ///
461            /// Returns `None` if there are no bits set.
462            ///
463            /// The index is relative to the entire sequence of `self`, not to the given `start`.
464            /// # Panics
465            /// Panics if `start >= BITS || end >= BITS || start > end`.
466            #[must_use]
467            pub const fn find_last_one_range(self, start: u32, end: u32) -> Option<u32> {
468                let masked_bits = self.0 & Self::mask_range(start, end).0;
469                let mut idx = end;
470                loop {
471                    iif![(masked_bits & (1 << idx)) != 0; return Some(idx)];
472                    iif![idx == start; break];
473                    idx -= 1;
474                }
475                None
476            }
477
478            /// Finds the index of the last 1 in `self` from the `[start..=end]` checked range.
479            ///
480            /// Returns `None` if there are no bits set.
481            ///
482            /// The index is relative to the entire sequence of `self`, not to the given `start`.
483            /// # Errors
484            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
485            /// [`MismatchedIndices`] if `start > end`.
486            pub const fn find_last_one_checked_range(self, start: u32, end: u32)
487                -> Result<Option<u32>, MismatchedBounds> {
488                match Self::mask_checked_range(start, end) {
489                    Ok(mask) => {
490                        let masked_bits = self.0 & mask.0;
491                        let mut idx = end;
492                        loop {
493                            iif![(masked_bits & (1 << idx)) != 0; return Ok(Some(idx))];
494                            iif![idx == start; break];
495                            idx -= 1;
496                        }
497                        Ok(None)
498                    },
499                    Err(e) => Err(e),
500                }
501            }
502
503            /// Finds the index of the last 0 in `self` from the `[start..=end]` range.
504            ///
505            /// Returns `None` if there are no bits set.
506            ///
507            /// The index is relative to the entire sequence of `self`, not to the given `start`.
508            /// # Panics
509            /// Panics if `start >= BITS || end >= BITS || start > end`.
510            #[must_use]
511            pub const fn find_last_zero_range(self, start: u32, end: u32) -> Option<u32> {
512                let masked_bits = !(self.0 & Self::mask_range(start, end).0);
513                let mut idx = end;
514                loop {
515                    iif![(masked_bits & (1 << idx)) != 0; return Some(idx)];
516                    iif![idx == start; break];
517                    idx -= 1;
518                }
519                None
520            }
521
522            /// Finds the index of the last 0 in `self` from the `[start..=end]` checked range.
523            ///
524            /// Returns `None` if there are no bits set.
525            ///
526            /// The index is relative to the entire sequence of `self`, not to the given `start`.
527            /// # Errors
528            /// Returns [`IndexOutOfBounds`] if `start >= BITS || end >= BITS` and
529            /// [`MismatchedIndices`] if `start > end`.
530            pub const fn find_last_zero_checked_range(self, start: u32, end: u32)
531                -> Result<Option<u32>, MismatchedBounds> {
532                match Self::mask_checked_range(start, end) {
533                    Ok(mask) => {
534                        let masked_bits = !(self.0 & mask.0);
535                        let mut idx = end;
536                        loop {
537                            iif![(masked_bits & (1 << idx)) != 0; return Ok(Some(idx))];
538                            iif![idx == start; break];
539                            idx -= 1;
540                        }
541                        Ok(None)
542                    },
543                    Err(e) => Err(e),
544                }
545            }
546        }
547    };
548}
549impl_bits_wrapper![];