Skip to main content

fbl/
bits.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use core::mem::size_of;
8
9pub trait BitsSource: Copy {
10    fn into_u128(self) -> u128;
11}
12
13macro_rules! impl_bits_source {
14    ($($t:ty),*) => {
15        $(
16            impl BitsSource for $t {
17                #[inline(always)]
18                fn into_u128(self) -> u128 {
19                    self as u128
20                }
21            }
22        )*
23    };
24}
25
26impl_bits_source!(u8, u16, u32, u64, u128, usize);
27
28pub trait BitsTarget: Copy {
29    fn from_u128(val: u128) -> Self;
30}
31
32macro_rules! impl_bits_target {
33    ($($t:ty),*) => {
34        $(
35            impl BitsTarget for $t {
36                #[inline(always)]
37                fn from_u128(val: u128) -> Self {
38                    val as $t
39                }
40            }
41        )*
42    };
43}
44
45impl_bits_target!(u8, u16, u32, u64, u128, usize);
46
47impl BitsTarget for bool {
48    #[inline(always)]
49    fn from_u128(val: u128) -> Self {
50        val != 0
51    }
52}
53
54/// Extracts the bit range `[HIGH_BIT:LOW_BIT]` (inclusive) from a numerical `input`.
55#[inline(always)]
56pub fn extract_bits<const HIGH_BIT: usize, const LOW_BIT: usize, R: BitsTarget, S: BitsSource>(
57    input: S,
58) -> R {
59    const {
60        assert!(HIGH_BIT >= LOW_BIT, "High bit must be greater or equal to low bit.");
61        assert!(HIGH_BIT < size_of::<S>() * 8, "Source value ends before high bit");
62        assert!(
63            (HIGH_BIT + 1 - LOW_BIT) <= size_of::<R>() * 8,
64            "Return type is not large enough to hold requested bits."
65        );
66    }
67    let bit_count = HIGH_BIT + 1 - LOW_BIT;
68    let mask = if bit_count == 128 { u128::MAX } else { (1u128 << bit_count) - 1 };
69    R::from_u128((input.into_u128() >> LOW_BIT) & mask)
70}
71
72#[inline(always)]
73pub fn extract_bit<const BIT: usize, R: BitsTarget, S: BitsSource>(input: S) -> R {
74    extract_bits::<BIT, BIT, R, S>(input)
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_extract_bits() {
83        let val: u32 = 0xabcd_1234;
84        assert_eq!(extract_bits::<3, 0, u8, _>(val), 0x4);
85        assert_eq!(extract_bits::<7, 4, u8, _>(val), 0x3);
86        assert_eq!(extract_bits::<11, 8, u8, _>(val), 0x2);
87        assert_eq!(extract_bits::<31, 24, u8, _>(val), 0xab);
88        assert!(extract_bit::<2, bool, _>(val));
89        assert!(!extract_bit::<0, bool, _>(val));
90    }
91}