ctoolbox/formats/eite/util/
bitwise.rs1use anyhow::{Result, anyhow};
2
3fn mask_byte(v: i32) -> u8 {
5 #![allow(clippy::as_conversions)]
6 (v & 0xFF) as u8
7}
8
9pub fn bit_and_8(a: u8, b: u8) -> u8 {
11 #![allow(clippy::as_conversions)]
12 mask_byte(i32::from(a & b))
13}
14
15pub fn bit_not_8(a: u8) -> u8 {
17 #![allow(clippy::as_conversions)]
18 mask_byte(i32::from(!a))
19}
20
21pub fn bit_lshift_8(a: u8, places: u8) -> u8 {
23 #![allow(clippy::as_conversions)]
24 assert!(places <= 8);
25 mask_byte((u32::from(a) << places) as i32)
26}
27
28pub fn bit_rshift_8(a: u8, places: u8) -> u8 {
30 #![allow(clippy::as_conversions)]
31 assert!(places <= 8);
32 mask_byte((u32::from(a) >> places) as i32)
33}
34
35pub fn least_significant_byte(v: i32) -> u8 {
37 mask_byte(v)
38}
39
40pub fn byte_to_int_bit_array(byte: u8) -> Vec<u8> {
44 let mut bits = Vec::with_capacity(8);
45 for i in (0..8).rev() {
46 bits.push((byte >> i) & 1);
47 }
48 bits
49}
50
51pub fn byte_from_int_bit_array(bits: &[u8]) -> Result<u8> {
57 if bits.len() != 8 {
58 return Err(anyhow!(
59 "byte_from_int_bit_array: expected 8 bits, got {}",
60 bits.len()
61 ));
62 }
63 let mut val: u8 = 0;
64 for &b in bits {
65 if b > 1 {
66 return Err(anyhow!(
67 "byte_from_int_bit_array: invalid bit value {b}"
68 ));
69 }
70 val = (val << 1) | b;
71 }
72 Ok(val)
73}
74
75pub fn byte_array_to_int_bit_array(bytes: &[u8]) -> Vec<u8> {
77 let mut out = Vec::with_capacity(bytes.len() * 8);
78 for &b in bytes {
79 out.extend(byte_to_int_bit_array(b));
80 }
81 out
82}
83
84pub fn byte_array_from_int_bit_array(bits: &[u8]) -> Result<Vec<u8>> {
91 if !bits.len().is_multiple_of(8) {
92 return Err(anyhow!(
93 "byte_array_from_int_bit_array: bit length {} not divisible by 8",
94 bits.len()
95 ));
96 }
97 let mut out = Vec::with_capacity(bits.len() / 8);
98 for chunk in bits.chunks(8) {
99 out.push(byte_from_int_bit_array(chunk)?);
100 }
101 Ok(out)
102}
103
104#[cfg(test)]
105#[allow(clippy::unwrap_in_result, clippy::panic_in_result_fn)]
106mod tests {
107 use super::*;
108
109 #[crate::ctb_test]
110 fn test_bitwise_ops() {
111 assert_eq!(bit_and_8(0b1111_0000, 0b1010_1010), 0b1010_0000);
112 assert_eq!(bit_not_8(0b0000_1111), 0b1111_0000);
113 assert_eq!(bit_lshift_8(0b0000_1111, 2), 0b0011_1100);
114 assert_eq!(bit_rshift_8(0b1111_0000, 4), 0b0000_1111);
115 }
116
117 #[crate::ctb_test]
118 fn test_byte_to_bits_and_back() -> Result<()> {
119 let cases = [
120 (0x00u8, [0, 0, 0, 0, 0, 0, 0, 0]),
121 (0xFFu8, [1, 1, 1, 1, 1, 1, 1, 1]),
122 (0x5Au8, [0, 1, 0, 1, 1, 0, 1, 0]), (0xC3u8, [1, 1, 0, 0, 0, 0, 1, 1]), ];
125 for (byte, bits_expected) in cases.iter() {
126 let bits = byte_to_int_bit_array(*byte);
127 assert_eq!(bits.as_slice(), bits_expected);
128 let round = byte_from_int_bit_array(&bits)?;
129 assert_eq!(round, *byte);
130 }
131 Ok(())
132 }
133
134 #[crate::ctb_test]
135 fn test_bytes_to_bits_round_trip() -> Result<()> {
136 let data = b"\x00\x01\x7F\x80\xFF";
137 let bits = byte_array_to_int_bit_array(data);
138 assert_eq!(bits.len(), data.len() * 8);
139 let restored = byte_array_from_int_bit_array(&bits)?;
140 assert_eq!(restored, data);
141 Ok(())
142 }
143
144 #[crate::ctb_test]
145 fn test_byte_array_from_int_bit_array_invalid_length() {
146 let bits: Vec<u8> = vec![0u8, 1, 0]; assert!(byte_array_from_int_bit_array(&bits).is_err());
148 }
149}