├── .github ├── FUNDING.yml └── workflows │ ├── pages.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── rustfmt.toml └── src ├── bit_iter_high.rs ├── bit_iter_low.rs ├── bit_split.rs ├── get_bit.rs ├── get_region.rs ├── get_value.rs ├── lib.rs ├── region_mask.rs ├── replicate_bits.rs ├── u8x2_.rs ├── with_bit.rs ├── with_region.rs └── with_value.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Lokathor] 4 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Github Pages 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout Repository 15 | uses: actions/checkout@v2 16 | with: 17 | # github-pages-deploy-action requires we set this 18 | persist-credentials: false 19 | 20 | - name: Install Rust 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: nightly 24 | profile: minimal 25 | default: true 26 | 27 | - name: Install rust-src component 28 | run: rustup component add rust-src 29 | 30 | - name: Build The Docs 31 | run: cargo doc 32 | 33 | - name: Deploy 🚀 34 | uses: JamesIves/github-pages-deploy-action@v4.3.3 35 | with: 36 | branch: gh-pages # The branch the action should deploy to. 37 | folder: target/doc # The folder the action should deploy. 38 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: {} 5 | pull_request: {} 6 | 7 | env: 8 | RUST_BACKTRACE: 1 9 | 10 | jobs: 11 | test: 12 | name: Test Rust ${{ matrix.rust }} on ${{ matrix.os }} 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | include: 17 | # versions (all on linux-x86_64) 18 | - { rust: 1.57.0, os: ubuntu-latest } 19 | - { rust: stable, os: ubuntu-latest } 20 | - { rust: beta, os: ubuntu-latest } 21 | - { rust: nightly, os: ubuntu-latest } 22 | steps: 23 | - uses: hecrj/setup-rust-action@v1 24 | with: 25 | rust-version: ${{ matrix.rust }} 26 | - uses: actions/checkout@v2 27 | - run: cargo check 28 | - run: cargo check --features="track_caller" 29 | - run: cargo test --all-features 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | /Cargo.lock 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitfrob" 3 | description = "A sibling crate for `bytemuck`, this is where forbid(unsafe_code) utilities live." 4 | version = "1.3.2" 5 | edition = "2021" 6 | repository = "https://github.com/Lokathor/bitfrob" 7 | license = "Zlib OR Apache-2.0 OR MIT" 8 | categories = ["no-std"] 9 | keywords = ["bits", "bit", "frob", "bytemuck"] 10 | 11 | [features] 12 | # adds `track_caller` attribute on fns that assert things. 13 | # I'm told this can mess up inlining and stuff, so it's optional. 14 | track_caller = [] 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [docs.rs Documentation](https://docs.rs/bitfrob) 2 | 3 | # bitfrob 4 | Helps you frob those bits 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | fn_args_layout = "Compressed" 3 | max_width = 80 4 | tab_spaces = 2 5 | use_field_init_shorthand = true 6 | use_small_heuristics = "Max" 7 | 8 | # Unstable 9 | format_code_in_doc_comments = true 10 | imports_granularity = "Crate" 11 | wrap_comments = true 12 | -------------------------------------------------------------------------------- /src/bit_iter_high.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_bit_iter_high { 2 | ($name:ident, $elem:ty, $region_mask_fn:ident) => { 3 | /// Iterator for groups of bits in an integer (low to high). 4 | /// 5 | /// The number of bits per iteration does *not* have to be an even divisor. 6 | /// If it's not, the final output will simply have a partial number of 7 | /// significant bits. 8 | #[derive(Debug, Clone)] 9 | #[allow(missing_copy_implementations)] 10 | pub struct $name { 11 | bits: $elem, 12 | bits_per_iter: u32, 13 | bits_remaining: i32, 14 | } 15 | impl $name { 16 | /// Constructs a new iterator for `bits_per_iter` at a time out of the 17 | /// `bits` value. 18 | /// 19 | /// If `bits_per_iter` doesn't divide evenly into the number of bits in 20 | /// the type then the final output of the iterator will be the partial 21 | /// chunk of bits aligned to the *high* end of the bit span. 22 | /// 23 | /// ## Panics 24 | /// * `bits_per_iter` must be greater than 0. 25 | /// * `bits_per_iter` must be less than or equal to the number of bits in 26 | /// the type. 27 | #[inline] 28 | #[must_use] 29 | #[cfg_attr(feature = "track_caller", track_caller)] 30 | pub const fn from_count_and_bits( 31 | bits_per_iter: u32, bits: $elem, 32 | ) -> Self { 33 | assert!(bits_per_iter > 0); 34 | assert!(bits_per_iter <= <$elem>::BITS); 35 | Self { bits, bits_per_iter, bits_remaining: <$elem>::BITS as i32 } 36 | } 37 | } 38 | impl Iterator for $name { 39 | type Item = $elem; 40 | #[inline] 41 | fn next(&mut self) -> Option<$elem> { 42 | if self.bits_remaining < 1 { 43 | None 44 | } else { 45 | let out: $elem = self.bits >> (<$elem>::BITS - self.bits_per_iter); 46 | self.bits = self.bits.wrapping_shl(self.bits_per_iter); 47 | self.bits_remaining -= self.bits_per_iter as i32; 48 | Some(out) 49 | } 50 | } 51 | } 52 | }; 53 | } 54 | 55 | impl_bit_iter_high!(U8BitIterHigh, u8, u8_region_mask); 56 | impl_bit_iter_high!(U16BitIterHigh, u16, u16_region_mask); 57 | impl_bit_iter_high!(U32BitIterHigh, u32, u32_region_mask); 58 | impl_bit_iter_high!(U64BitIterHigh, u64, u64_region_mask); 59 | impl_bit_iter_high!(U128BitIterHigh, u128, u128_region_mask); 60 | 61 | #[test] 62 | fn test_U8BitIterHigh() { 63 | let mut iter = U8BitIterHigh::from_count_and_bits(1, 0b1011_0111_u8); 64 | assert_eq!(iter.next(), Some(0b1_u8)); 65 | assert_eq!(iter.next(), Some(0b0_u8)); 66 | assert_eq!(iter.next(), Some(0b1_u8)); 67 | assert_eq!(iter.next(), Some(0b1_u8)); 68 | assert_eq!(iter.next(), Some(0b0_u8)); 69 | assert_eq!(iter.next(), Some(0b1_u8)); 70 | assert_eq!(iter.next(), Some(0b1_u8)); 71 | assert_eq!(iter.next(), Some(0b1_u8)); 72 | assert_eq!(iter.next(), None); 73 | 74 | let mut iter = U8BitIterHigh::from_count_and_bits(2, 0b1011_0111_u8); 75 | assert_eq!(iter.next(), Some(0b10_u8)); 76 | assert_eq!(iter.next(), Some(0b11_u8)); 77 | assert_eq!(iter.next(), Some(0b01_u8)); 78 | assert_eq!(iter.next(), Some(0b11_u8)); 79 | assert_eq!(iter.next(), None); 80 | 81 | let mut iter = U8BitIterHigh::from_count_and_bits(3, 0b1011_0111_u8); 82 | assert_eq!(iter.next(), Some(0b101_u8)); 83 | assert_eq!(iter.next(), Some(0b101_u8)); 84 | assert_eq!(iter.next(), Some(0b110_u8)); 85 | assert_eq!(iter.next(), None); 86 | 87 | let mut iter = U8BitIterHigh::from_count_and_bits(8, 0b1011_0111_u8); 88 | assert_eq!(iter.next(), Some(0b1011_0111_u8)); 89 | assert_eq!(iter.next(), None); 90 | } 91 | -------------------------------------------------------------------------------- /src/bit_iter_low.rs: -------------------------------------------------------------------------------- 1 | use crate::region_mask::*; 2 | 3 | macro_rules! impl_bit_iter_low { 4 | ($name:ident, $elem:ty, $region_mask_fn:ident) => { 5 | /// Iterator for groups of bits in an integer (low to high). 6 | /// 7 | /// The number of bits per iteration does *not* have to be an even divisor. 8 | /// If it's not, the final output will simply have a partial number of 9 | /// significant bits. 10 | #[derive(Debug, Clone)] 11 | #[allow(missing_copy_implementations)] 12 | pub struct $name { 13 | bits: $elem, 14 | mask: $elem, 15 | bits_per_iter: u32, 16 | bits_remaining: i32, 17 | } 18 | impl $name { 19 | /// Constructs a new iterator for `bits_per_iter` at a time out of the 20 | /// `bits` value. 21 | /// 22 | /// If `bits_per_iter` doesn't divide evenly into the number of bits in 23 | /// the type then the final output of the iterator will be the partial 24 | /// chunk of bits aligned to the *low* end of the bit span. 25 | /// 26 | /// ## Panics 27 | /// * `bits_per_iter` must be greater than 0. 28 | /// * `bits_per_iter` must be less than or equal to the number of bits in 29 | /// the type. 30 | #[inline] 31 | #[must_use] 32 | #[cfg_attr(feature = "track_caller", track_caller)] 33 | pub const fn from_count_and_bits( 34 | bits_per_iter: u32, bits: $elem, 35 | ) -> Self { 36 | assert!(bits_per_iter > 0); 37 | assert!(bits_per_iter <= <$elem>::BITS); 38 | Self { 39 | bits, 40 | mask: $region_mask_fn(0, bits_per_iter - 1), 41 | bits_per_iter, 42 | bits_remaining: <$elem>::BITS as i32, 43 | } 44 | } 45 | } 46 | impl Iterator for $name { 47 | type Item = $elem; 48 | #[inline] 49 | fn next(&mut self) -> Option<$elem> { 50 | if self.bits_remaining < 1 { 51 | None 52 | } else { 53 | let out: $elem = self.bits & self.mask; 54 | self.bits = self.bits.wrapping_shr(self.bits_per_iter); 55 | self.bits_remaining -= self.bits_per_iter as i32; 56 | Some(out) 57 | } 58 | } 59 | } 60 | }; 61 | } 62 | 63 | impl_bit_iter_low!(U8BitIterLow, u8, u8_region_mask); 64 | impl_bit_iter_low!(U16BitIterLow, u16, u16_region_mask); 65 | impl_bit_iter_low!(U32BitIterLow, u32, u32_region_mask); 66 | impl_bit_iter_low!(U64BitIterLow, u64, u64_region_mask); 67 | impl_bit_iter_low!(U128BitIterLow, u128, u128_region_mask); 68 | 69 | #[test] 70 | fn test_U8BitIterLow() { 71 | let mut iter = U8BitIterLow::from_count_and_bits(1, 0b1011_0111_u8); 72 | assert_eq!(iter.next(), Some(0b1_u8)); 73 | assert_eq!(iter.next(), Some(0b1_u8)); 74 | assert_eq!(iter.next(), Some(0b1_u8)); 75 | assert_eq!(iter.next(), Some(0b0_u8)); 76 | assert_eq!(iter.next(), Some(0b1_u8)); 77 | assert_eq!(iter.next(), Some(0b1_u8)); 78 | assert_eq!(iter.next(), Some(0b0_u8)); 79 | assert_eq!(iter.next(), Some(0b1_u8)); 80 | assert_eq!(iter.next(), None); 81 | 82 | let mut iter = U8BitIterLow::from_count_and_bits(2, 0b1011_0111_u8); 83 | assert_eq!(iter.next(), Some(0b11_u8)); 84 | assert_eq!(iter.next(), Some(0b01_u8)); 85 | assert_eq!(iter.next(), Some(0b11_u8)); 86 | assert_eq!(iter.next(), Some(0b10_u8)); 87 | assert_eq!(iter.next(), None); 88 | 89 | let mut iter = U8BitIterLow::from_count_and_bits(3, 0b1011_0111_u8); 90 | assert_eq!(iter.next(), Some(0b111_u8)); 91 | assert_eq!(iter.next(), Some(0b110_u8)); 92 | assert_eq!(iter.next(), Some(0b010_u8)); 93 | assert_eq!(iter.next(), None); 94 | 95 | let mut iter = U8BitIterLow::from_count_and_bits(8, 0b1011_0111_u8); 96 | assert_eq!(iter.next(), Some(0b1011_0111_u8)); 97 | assert_eq!(iter.next(), None); 98 | } 99 | -------------------------------------------------------------------------------- /src/bit_split.rs: -------------------------------------------------------------------------------- 1 | /// Splits a byte into 1-bit chunks. 2 | /// 3 | /// Bit 0 of the input will be in the **first** element of the output. 4 | #[inline] 5 | #[must_use] 6 | pub const fn u8_bit_split_1x8(byte: u8) -> [u8; 8] { 7 | [ 8 | (byte >> 0) & 0b1, 9 | (byte >> 1) & 0b1, 10 | (byte >> 2) & 0b1, 11 | (byte >> 3) & 0b1, 12 | (byte >> 4) & 0b1, 13 | (byte >> 5) & 0b1, 14 | (byte >> 6) & 0b1, 15 | (byte >> 7) & 0b1, 16 | ] 17 | } 18 | 19 | /// Splits a byte into 2-bit chunks. 20 | /// 21 | /// Bit 0 of the input will be in the **first** element of the output. 22 | #[inline] 23 | #[must_use] 24 | pub const fn u8_bit_split_2x4(byte: u8) -> [u8; 4] { 25 | [ 26 | (byte >> 0) & 0b11, 27 | (byte >> 2) & 0b11, 28 | (byte >> 4) & 0b11, 29 | (byte >> 6) & 0b11, 30 | ] 31 | } 32 | 33 | /// Splits a byte into 4-bit chunks. 34 | /// 35 | /// Bit 0 of the input will be in the **first** element of the output. 36 | #[inline] 37 | #[must_use] 38 | pub const fn u8_bit_split_4x2(byte: u8) -> [u8; 2] { 39 | [(byte >> 0) & 0b1111, (byte >> 4) & 0b1111] 40 | } 41 | 42 | /// Splits a byte into 1-bit chunks (reversed). 43 | /// 44 | /// Bit 0 of the input will be in the **last** element of the output. 45 | #[inline] 46 | #[must_use] 47 | pub const fn u8_bit_split_1x8_rev(byte: u8) -> [u8; 8] { 48 | [ 49 | (byte >> 7) & 0b1, 50 | (byte >> 6) & 0b1, 51 | (byte >> 5) & 0b1, 52 | (byte >> 4) & 0b1, 53 | (byte >> 3) & 0b1, 54 | (byte >> 2) & 0b1, 55 | (byte >> 1) & 0b1, 56 | (byte >> 0) & 0b1, 57 | ] 58 | } 59 | 60 | /// Splits a byte into 2-bit chunks (reversed). 61 | /// 62 | /// Bit 0 of the input will be in the **last** element of the output. 63 | #[inline] 64 | #[must_use] 65 | pub const fn u8_bit_split_2x4_rev(byte: u8) -> [u8; 4] { 66 | [ 67 | (byte >> 6) & 0b11, 68 | (byte >> 4) & 0b11, 69 | (byte >> 2) & 0b11, 70 | (byte >> 0) & 0b11, 71 | ] 72 | } 73 | 74 | /// Splits a byte into 4-bit chunks (reversed). 75 | /// 76 | /// Bit 0 of the input will be in the **last** element of the output. 77 | #[inline] 78 | #[must_use] 79 | pub const fn u8_bit_split_4x2_rev(byte: u8) -> [u8; 2] { 80 | [(byte >> 4) & 0b1111, (byte >> 0) & 0b1111] 81 | } 82 | 83 | /// When used as a multiplier, scales a "1 bit" `u8` to spread the value across 84 | /// all 8 bits. 85 | pub const U8_SCALE_1_TO_8: u8 = 0b_11111111; 86 | 87 | /// When used as a multiplier, scales a "2 bit" `u8` to spread the value across 88 | /// all 8 bits. 89 | pub const U8_SCALE_2_TO_8: u8 = 0b_01010101; 90 | 91 | /// When used as a multiplier, scales a "4 bit" `u8` to spread the value across 92 | /// all 8 bits. 93 | pub const U8_SCALE_4_TO_8: u8 = 0b_00010001; 94 | 95 | #[test] 96 | #[allow(clippy::erasing_op)] 97 | fn test_bit_split() { 98 | let x: [u8; 8] = u8_bit_split_1x8(0b11001010); 99 | assert_eq!(x, [0, 1, 0, 1, 0, 0, 1, 1]); 100 | 101 | let x: [u8; 4] = u8_bit_split_2x4(0b11001010); 102 | assert_eq!(x, [0b10, 0b10, 0b00, 0b11]); 103 | 104 | let x: [u8; 2] = u8_bit_split_4x2(0b11001010); 105 | assert_eq!(x, [0b1010, 0b1100]); 106 | 107 | let x: [u8; 8] = u8_bit_split_1x8_rev(0b11001010); 108 | assert_eq!(x, [1, 1, 0, 0, 1, 0, 1, 0]); 109 | 110 | let x: [u8; 4] = u8_bit_split_2x4_rev(0b11001010); 111 | assert_eq!(x, [0b11, 0b00, 0b10, 0b10]); 112 | 113 | let x: [u8; 2] = u8_bit_split_4x2_rev(0b11001010); 114 | assert_eq!(x, [0b1100, 0b1010]); 115 | 116 | assert_eq!(U8_SCALE_1_TO_8 * 0, 0); 117 | assert_eq!(U8_SCALE_1_TO_8 * 1, u8::MAX); 118 | // 119 | assert_eq!(U8_SCALE_2_TO_8 * 0, 0); 120 | assert_eq!(U8_SCALE_2_TO_8 * 1, 0b01010101); 121 | assert_eq!(U8_SCALE_2_TO_8 * 2, 0b10101010); 122 | assert_eq!(U8_SCALE_2_TO_8 * 3, u8::MAX); 123 | // 124 | assert_eq!(U8_SCALE_4_TO_8 * 0x0, 0x00); 125 | assert_eq!(U8_SCALE_4_TO_8 * 0x1, 0x11); 126 | assert_eq!(U8_SCALE_4_TO_8 * 0x2, 0x22); 127 | assert_eq!(U8_SCALE_4_TO_8 * 0x3, 0x33); 128 | assert_eq!(U8_SCALE_4_TO_8 * 0x4, 0x44); 129 | assert_eq!(U8_SCALE_4_TO_8 * 0x5, 0x55); 130 | assert_eq!(U8_SCALE_4_TO_8 * 0x6, 0x66); 131 | assert_eq!(U8_SCALE_4_TO_8 * 0x7, 0x77); 132 | assert_eq!(U8_SCALE_4_TO_8 * 0x8, 0x88); 133 | assert_eq!(U8_SCALE_4_TO_8 * 0x9, 0x99); 134 | assert_eq!(U8_SCALE_4_TO_8 * 0xA, 0xAA); 135 | assert_eq!(U8_SCALE_4_TO_8 * 0xB, 0xBB); 136 | assert_eq!(U8_SCALE_4_TO_8 * 0xC, 0xCC); 137 | assert_eq!(U8_SCALE_4_TO_8 * 0xD, 0xDD); 138 | assert_eq!(U8_SCALE_4_TO_8 * 0xE, 0xEE); 139 | assert_eq!(U8_SCALE_4_TO_8 * 0xF, 0xFF); 140 | } 141 | -------------------------------------------------------------------------------- /src/get_bit.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_get_bit { 2 | ($fn_name:ident, $t:ty) => { 3 | /// Determines if the `b` bit is set in `u`. 4 | /// 5 | /// ## Panics 6 | /// * `b` can't exceed the number of bits in the type. 7 | /// 8 | /// ``` 9 | /// # use bitfrob::*; 10 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 0b0000_1110_", stringify!($t), "), false);")] 11 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 0b0000_1110_", stringify!($t), "), true);")] 12 | /// ``` 13 | #[inline] 14 | #[must_use] 15 | #[cfg_attr(feature = "track_caller", track_caller)] 16 | pub const fn $fn_name(b: u32, u: $t) -> bool { 17 | assert!(b < <$t>::BITS); 18 | let mask = 1 << b; 19 | (u & mask) != 0 20 | } 21 | }; 22 | } 23 | 24 | impl_get_bit!(u8_get_bit, u8); 25 | impl_get_bit!(u16_get_bit, u16); 26 | impl_get_bit!(u32_get_bit, u32); 27 | impl_get_bit!(u64_get_bit, u64); 28 | impl_get_bit!(u128_get_bit, u128); 29 | -------------------------------------------------------------------------------- /src/get_region.rs: -------------------------------------------------------------------------------- 1 | use crate::region_mask::*; 2 | 3 | macro_rules! impl_get_region { 4 | ($fn_name:ident, $t:ty, $mask_get_fn:ident) => { 5 | /// Get the `low` to `high` bit region of `u`. 6 | /// 7 | /// The `low` and `high` values form an *inclusive* bit range. 8 | /// 9 | /// ## Panics 10 | /// * `low` and `high` can't exceed the number of bits in the type. 11 | /// * `low` must be less than `high`. 12 | /// 13 | /// ``` 14 | /// # use bitfrob::*; 15 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 2, ", stringify!($t), "::MAX), 0b0000_0111_", stringify!($t),");")] 16 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 3, ", stringify!($t), "::MAX), 0b0000_1110_", stringify!($t),");")] 17 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 7, ", stringify!($t), "::MAX), 0b1111_0000_", stringify!($t),");")] 18 | /// ``` 19 | #[inline] 20 | #[must_use] 21 | #[cfg_attr(feature = "track_caller", track_caller)] 22 | pub const fn $fn_name(low: u32, high: u32, u: $t) -> $t { 23 | let mask = $mask_get_fn(low, high); 24 | (u & mask) 25 | } 26 | }; 27 | } 28 | 29 | impl_get_region!(u8_get_region, u8, u8_region_mask); 30 | impl_get_region!(u16_get_region, u16, u16_region_mask); 31 | impl_get_region!(u32_get_region, u32, u32_region_mask); 32 | impl_get_region!(u64_get_region, u64, u64_region_mask); 33 | impl_get_region!(u128_get_region, u128, u128_region_mask); 34 | -------------------------------------------------------------------------------- /src/get_value.rs: -------------------------------------------------------------------------------- 1 | use crate::get_region::*; 2 | 3 | macro_rules! impl_get_value { 4 | ($fn_name:ident, $t:ty, $region_get_fn:ident) => { 5 | /// Get the `low` to `high` bit region of `u`, down shifted by `low`. 6 | /// 7 | /// The `low` and `high` values form an *inclusive* bit range. 8 | /// 9 | /// The output is down shifted by `low` bits so that it will be based at 0. 10 | /// 11 | /// ## Panics 12 | /// * `low` and `high` can't exceed the number of bits in the type. 13 | /// * `low` must be less than `high`. 14 | /// 15 | /// ``` 16 | /// # use bitfrob::*; 17 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 2, ", stringify!($t), "::MAX), 0b0000_0111_", stringify!($t),");")] 18 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 3, ", stringify!($t), "::MAX), 0b0000_0111_", stringify!($t),");")] 19 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 7, ", stringify!($t), "::MAX), 0b0000_1111_", stringify!($t),");")] 20 | /// ``` 21 | #[inline] 22 | #[must_use] 23 | #[cfg_attr(feature = "track_caller", track_caller)] 24 | pub const fn $fn_name(low: u32, high: u32, u: $t) -> $t { 25 | $region_get_fn(0, high-low, u >> low) 26 | } 27 | }; 28 | } 29 | 30 | impl_get_value!(u8_get_value, u8, u8_get_region); 31 | impl_get_value!(u16_get_value, u16, u16_get_region); 32 | impl_get_value!(u32_get_value, u32, u32_get_region); 33 | impl_get_value!(u64_get_value, u64, u64_get_region); 34 | impl_get_value!(u128_get_value, u128, u128_get_region); 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![warn(missing_docs)] 3 | #![forbid(unsafe_code)] 4 | #![allow(clippy::identity_op)] 5 | #![cfg_attr(test, allow(bad_style))] 6 | #![warn(clippy::missing_panics_doc)] 7 | #![warn(clippy::must_use_candidate)] 8 | #![warn(missing_copy_implementations)] 9 | #![warn(clippy::missing_const_for_fn)] 10 | #![warn(missing_debug_implementations)] 11 | #![warn(clippy::missing_inline_in_public_items)] 12 | 13 | //! A crate to help with bit manipulation of integers. 14 | //! 15 | //! ## Features 16 | //! * `track_caller` adds the [`#[track_caller]`][ref-track_caller] attribute on 17 | //! all the functions that assert stuff. 18 | //! 19 | //! [ref-track_caller]: 20 | //! https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute 21 | 22 | mod bit_iter_high; 23 | mod bit_iter_low; 24 | mod bit_split; 25 | mod get_bit; 26 | mod get_region; 27 | mod get_value; 28 | mod region_mask; 29 | mod replicate_bits; 30 | mod u8x2_; 31 | mod with_bit; 32 | mod with_region; 33 | mod with_value; 34 | 35 | pub use self::{ 36 | bit_iter_high::*, bit_iter_low::*, bit_split::*, get_bit::*, get_region::*, 37 | get_value::*, region_mask::*, replicate_bits::*, u8x2_::*, with_bit::*, 38 | with_region::*, with_value::*, 39 | }; 40 | -------------------------------------------------------------------------------- /src/region_mask.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_region_mask { 2 | ($fn_name:ident, $t:ty) => { 3 | /// Generates a bit mask where all bits in the region are 1. 4 | /// 5 | /// The `low` and `high` values form an *inclusive* bit range where the mask 6 | /// bits are 1. 7 | /// 8 | /// This is largely a helper function for other functions in this crate, but 9 | /// you can use it yourself if you think it's useful. 10 | /// 11 | /// ## Panics 12 | /// * `low` and `high` must be less than the number of bits in the type. 13 | /// * `low` must be *less than or equal to* `high`. 14 | /// 15 | /// ``` 16 | /// # use bitfrob::*; 17 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 0), 0b0000_0001_", stringify!($t), ");")] 18 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 1), 0b0000_0011_", stringify!($t), ");")] 19 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 2), 0b0000_0111_", stringify!($t), ");")] 20 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 3), 0b0000_1110_", stringify!($t), ");")] 21 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 7), 0b1111_0000_", stringify!($t), ");")] 22 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 4), 0b0001_0000_", stringify!($t), ");")] 23 | /// ``` 24 | #[inline] 25 | #[must_use] 26 | #[cfg_attr(feature = "track_caller", track_caller)] 27 | pub const fn $fn_name(low: u32, high: u32) -> $t { 28 | assert!(low < <$t>::BITS); 29 | assert!(high < <$t>::BITS); 30 | assert!(low <= high); 31 | (<$t>::MAX >> ((<$t>::BITS - 1) - (high - low))) << low 32 | } 33 | }; 34 | } 35 | 36 | impl_region_mask!(u8_region_mask, u8); 37 | impl_region_mask!(u16_region_mask, u16); 38 | impl_region_mask!(u32_region_mask, u32); 39 | impl_region_mask!(u64_region_mask, u64); 40 | impl_region_mask!(u128_region_mask, u128); 41 | 42 | /* 43 | 44 | backup: we might want to do the ZST associated const thing? 45 | 46 | /// Like [`u32_region_mask`], but forces the value into an associated constant. 47 | /// 48 | /// * Advantage: the compiler is forced to compute the constant at compile time, 49 | /// regardless of debug level. 50 | /// * Disadvantage: the `L` and `H` must themselves be constants. 51 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 52 | pub struct U32ConstRegionMask; 53 | impl U32ConstRegionMask { 54 | /// The computed bit mask. 55 | pub const OUT: u32 = u32_region_mask(L, H); 56 | } 57 | 58 | */ 59 | -------------------------------------------------------------------------------- /src/replicate_bits.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_replicate_bits { 2 | ($fn_name:ident, $t:ty) => { 3 | /// Replicates the lowest `count` bits across the entire value. 4 | /// 5 | /// If higher bits are set in the input it will not affect the output (they 6 | /// end up being discarded). 7 | /// 8 | /// This is a form of "sample depth scaling". If your source data's bit 9 | /// depth is *less* than the full range of a `uX` value (eg, only the lowest 10 | /// 5 bits in a `u8`), this function can scale up the sample into the 11 | /// integer's full range. 12 | /// 13 | /// ## Panics 14 | /// * `count` can't exceed the number of bits in the type. 15 | /// * `count` can't be 0. 16 | /// 17 | /// ``` 18 | /// # use bitfrob::*; 19 | #[doc = concat!("assert_eq!(u8_replicate_bits(5, 0b0001_0111_u8), 0b1011_1101_u8);")] 20 | #[doc = concat!("assert_eq!(u16_replicate_bits(5, 0b0001_0111_u16), 0b1011_1101_1110_1111_u16);")] 21 | #[doc = concat!("assert_eq!(u32_replicate_bits(5, 0b0001_0111_u32), 0xBDEF_7BDE_u32);")] 22 | #[doc = concat!("assert_eq!(u64_replicate_bits(5, 0b0001_0111_u64), 0xBDEF_7BDE_F7BD_EF7B_u64);")] 23 | #[doc = concat!("assert_eq!(u128_replicate_bits(5, 0b0001_0111_u128), 0xBDEF_7BDE_F7BD_EF7B_DEF7_BDEF_7BDE_F7BD_u128);")] 24 | /// ``` 25 | #[inline] 26 | #[must_use] 27 | #[cfg_attr(feature = "track_caller", track_caller)] 28 | pub fn $fn_name(mut count: u32, u: $t) -> $t { 29 | assert!(count <= <$t>::BITS); 30 | assert!(count > 0); 31 | let mut out_count = count; 32 | let mut out = u << (<$t>::BITS - count); 33 | while out_count < <$t>::BITS { 34 | out |= (out >> count); 35 | out_count += count; 36 | // Note(Lokathor): As the output builds the number of "good" bits can 37 | // double each time, which reduces the number of iterations when a small 38 | // number of starting bits get replicated across the larger int types. 39 | count *= 2; 40 | } 41 | out 42 | } 43 | }; 44 | } 45 | 46 | impl_replicate_bits!(u8_replicate_bits, u8); 47 | impl_replicate_bits!(u16_replicate_bits, u16); 48 | impl_replicate_bits!(u32_replicate_bits, u32); 49 | impl_replicate_bits!(u64_replicate_bits, u64); 50 | impl_replicate_bits!(u128_replicate_bits, u128); 51 | 52 | #[test] 53 | fn test_replicate_bits_allows_full_bits() { 54 | // test passes if this doesn't panic 55 | let _ = u8_replicate_bits(8, 0xFF); 56 | } 57 | -------------------------------------------------------------------------------- /src/u8x2_.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// Two `u8` values packed as a `u16`. 4 | /// 5 | /// Compared to using `[u8; 2]`, this forces the data to have an alignment of 2, 6 | /// and also keeps volatile reads and writes as a single access (when possible). 7 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 8 | #[allow(non_camel_case_types)] 9 | #[repr(transparent)] 10 | pub struct u8x2(u16); 11 | impl u8x2 { 12 | /// The lower byte 13 | #[inline] 14 | #[must_use] 15 | pub const fn low(self) -> u8 { 16 | u16_get_value(0, 7, self.0) as u8 17 | } 18 | /// Updates the lower byte value, returning the new `u8x2` 19 | #[inline] 20 | #[must_use] 21 | pub const fn with_low(self, low: u8) -> Self { 22 | Self(u16_with_value(0, 7, self.0, low as u16)) 23 | } 24 | /// The upper byte 25 | #[inline] 26 | #[must_use] 27 | pub const fn high(self) -> u8 { 28 | u16_get_value(8, 15, self.0) as u8 29 | } 30 | /// Updates the upper byte value, returning the new `u8x2` 31 | #[inline] 32 | #[must_use] 33 | pub const fn with_high(self, high: u8) -> Self { 34 | Self(u16_with_value(8, 15, self.0, high as u16)) 35 | } 36 | } 37 | 38 | impl From<[u8; 2]> for u8x2 { 39 | #[inline] 40 | #[must_use] 41 | fn from(value: [u8; 2]) -> Self { 42 | Self(u16::from_ne_bytes(value)) 43 | } 44 | } 45 | 46 | impl From for [u8; 2] { 47 | #[inline] 48 | #[must_use] 49 | fn from(value: u8x2) -> Self { 50 | value.0.to_ne_bytes() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/with_bit.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_with_bit { 2 | ($fn_name:ident, $t:ty) => { 3 | /// Replaces the `b` bit in `u`. 4 | /// 5 | /// ## Panics 6 | /// * `b` can't exceed the number of bits in the type. 7 | #[inline] 8 | #[must_use] 9 | #[cfg_attr(feature = "track_caller", track_caller)] 10 | pub const fn $fn_name(b: u32, u: $t, is_set: bool) -> $t { 11 | assert!(b < <$t>::BITS); 12 | let mask = 1 << b; 13 | (u & !mask) | ((is_set as $t) << b) 14 | } 15 | }; 16 | } 17 | 18 | impl_with_bit!(u8_with_bit, u8); 19 | impl_with_bit!(u16_with_bit, u16); 20 | impl_with_bit!(u32_with_bit, u32); 21 | impl_with_bit!(u64_with_bit, u64); 22 | impl_with_bit!(u128_with_bit, u128); 23 | -------------------------------------------------------------------------------- /src/with_region.rs: -------------------------------------------------------------------------------- 1 | use crate::region_mask::*; 2 | 3 | macro_rules! impl_with_region { 4 | ($fn_name:ident, $t:ty, $mask_get_fn:ident) => { 5 | /// Replaces the `low` to `high` bit region of `old`. 6 | /// 7 | /// The `low` and `high` values form an *inclusive* bit range. 8 | /// 9 | /// Bits in `replacement` outside of the region have no effect. 10 | /// 11 | /// ## Panics 12 | /// * `low` and `high` can't exceed the number of bits in the type. 13 | /// * `low` must be less than `high`. 14 | /// 15 | /// ``` 16 | /// # use bitfrob::*; 17 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 2, 0b11111111_", stringify!($t), ", 0), 0b1111_1000_", stringify!($t),");")] 18 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 3, 0b11111111_", stringify!($t), ", 0), 0b1111_0001_", stringify!($t),");")] 19 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 7, 0b11111111_", stringify!($t), ", 0), 0b0000_1111_", stringify!($t),");")] 20 | /// ``` 21 | #[inline] 22 | #[must_use] 23 | #[cfg_attr(feature = "track_caller", track_caller)] 24 | pub const fn $fn_name(low: u32, high: u32, old: $t, replacement: $t) -> $t { 25 | let mask = $mask_get_fn(low, high); 26 | (old & !mask) | (replacement & mask) 27 | } 28 | }; 29 | } 30 | 31 | impl_with_region!(u8_with_region, u8, u8_region_mask); 32 | impl_with_region!(u16_with_region, u16, u16_region_mask); 33 | impl_with_region!(u32_with_region, u32, u32_region_mask); 34 | impl_with_region!(u64_with_region, u64, u64_region_mask); 35 | impl_with_region!(u128_with_region, u128, u128_region_mask); 36 | -------------------------------------------------------------------------------- /src/with_value.rs: -------------------------------------------------------------------------------- 1 | use crate::with_region::*; 2 | 3 | macro_rules! impl_with_value { 4 | ($fn_name:ident, $t:ty, $with_region_fn:ident) => { 5 | /// Replaces the `low` to `high` bit region of `old` with an input up shifted by `low`. 6 | /// 7 | /// The `low` and `high` values form an *inclusive* bit range. 8 | /// 9 | /// The `replacement` value is up shifted by `low` bits so that it will be 10 | /// based at the base of the bit region. 11 | /// 12 | /// If the `replacement` exceeds the bits allowed by the region they will be 13 | /// truncated. 14 | /// 15 | /// ## Panics 16 | /// * `low` and `high` can't exceed the number of bits in the type. 17 | /// * `low` must be less than `high`. 18 | /// 19 | /// ``` 20 | /// # use bitfrob::*; 21 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(0, 2, 0, 1), 1 << 0);")] 22 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(1, 3, 0, 1), 1 << 1);")] 23 | #[doc = concat!("assert_eq!(",stringify!($fn_name), "(4, 7, 0, 1), 1 << 4);")] 24 | /// ``` 25 | #[inline] 26 | #[must_use] 27 | #[cfg_attr(feature = "track_caller", track_caller)] 28 | pub const fn $fn_name(low: u32, high: u32, old: $t, replacement: $t) -> $t { 29 | $with_region_fn(low, high, old, replacement << low) 30 | } 31 | }; 32 | } 33 | 34 | impl_with_value!(u8_with_value, u8, u8_with_region); 35 | impl_with_value!(u16_with_value, u16, u16_with_region); 36 | impl_with_value!(u32_with_value, u32, u32_with_region); 37 | impl_with_value!(u64_with_value, u64, u64_with_region); 38 | impl_with_value!(u128_with_value, u128, u128_with_region); 39 | --------------------------------------------------------------------------------