byte_array_ops/
ops.rs

1use crate::bytes;
2use crate::model::ByteArray;
3use core::cmp::max;
4use core::mem;
5use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
6
7#[doc(hidden)]
8pub(super) struct InternalOps;
9
10impl InternalOps {
11    /// Internal Base operation implementation for bitwise operations on byte arrays.
12    ///
13    /// This implementation prioritizes performance over idiomatic Rust:
14    /// - Uses simple indexed loops instead of iterators (no branching overhead)
15    /// - Avoids bounds checking where possible through careful indexing
16    /// - Two-pass approach leverages zero initialization for correct results
17    ///
18    /// Arrays are right-aligned (network byte order) before operation.
19    ///
20    /// # Note
21    /// identity is needed for cases where the operation is a multiplication and thus zeroing would not work
22    fn base_op(
23        lhs: ByteArray,
24        rhs: ByteArray,
25        byte_op: impl Fn(u8, u8) -> u8,
26        identity: u8,
27    ) -> ByteArray {
28        // byte array must be sized to the greatest of both operands and filled with zeros
29        let max_arr_size = max(lhs.len(), rhs.len());
30        let mut res = bytes![identity; max_arr_size];
31
32        //We XOR the overlapping parts after aligning to the right (Network Byte Order Convention)
33        let first_offset = max_arr_size - lhs.len();
34        let second_offset = max_arr_size - rhs.len();
35
36        for i in 0..lhs.len() {
37            res[first_offset + i] = byte_op(res[first_offset + i], lhs[i]);
38        }
39
40        for i in 0..rhs.len() {
41            res[second_offset + i] = byte_op(res[second_offset + i], rhs[i]);
42        }
43
44        res
45    }
46
47    #[inline]
48    fn xor_op(lhs: ByteArray, rhs: ByteArray) -> ByteArray {
49        Self::base_op(lhs, rhs, |x, y| x ^ y, 0x00)
50    }
51
52    #[inline]
53    fn and_op(lhs: ByteArray, rhs: ByteArray) -> ByteArray {
54        Self::base_op(lhs, rhs, |x, y| x & y, 0xFF)
55    }
56
57    #[inline]
58    fn or_op(lhs: ByteArray, rhs: ByteArray) -> ByteArray {
59        Self::base_op(lhs, rhs, |x, y| x | y, 0x00)
60    }
61}
62
63impl BitXor for ByteArray {
64    type Output = Self;
65
66    fn bitxor(self, rhs: Self) -> Self::Output {
67        InternalOps::xor_op(self, rhs)
68    }
69}
70
71impl BitXorAssign for ByteArray {
72    fn bitxor_assign(&mut self, rhs: Self) {
73        *self = InternalOps::xor_op(mem::take(self), rhs);
74    }
75}
76
77impl BitAnd for ByteArray {
78    type Output = Self;
79
80    fn bitand(self, rhs: Self) -> Self::Output {
81        InternalOps::and_op(self, rhs)
82    }
83}
84
85impl BitAndAssign for ByteArray {
86    fn bitand_assign(&mut self, rhs: Self) {
87        *self = InternalOps::and_op(mem::take(self), rhs)
88    }
89}
90
91impl BitOr for ByteArray {
92    type Output = Self;
93
94    fn bitor(self, rhs: Self) -> Self::Output {
95        InternalOps::or_op(self, rhs)
96    }
97}
98
99impl BitOrAssign for ByteArray {
100    fn bitor_assign(&mut self, rhs: Self) {
101        *self = InternalOps::or_op(mem::take(self), rhs)
102    }
103}
104
105impl Not for ByteArray {
106    type Output = Self;
107
108    fn not(self) -> Self::Output {
109        ByteArray {
110            bytes: self.bytes.iter().map(|&b| !b).collect(),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_xor_simple() {
121        let b1: ByteArray = [0xAA, 0xBB, 0xCC].into();
122        let b2 = ByteArray::from([0x55, 0x44, 0x33]);
123        let b3 = b2 ^ b1;
124
125        assert_eq!(b3.len(), 3);
126        assert_eq!(b3[0], 0xAA ^ 0x55);
127        assert_eq!(b3[1], 0xBB ^ 0x44);
128        assert_eq!(b3[2], 0xCC ^ 0x33);
129    }
130
131    #[test]
132    fn test_xor_unequal_length() {
133        let b1: ByteArray = [0xAA, 0xBB].into();
134        let b2 = ByteArray::from([0x11, 0x22, 0x33]);
135        let res = b1 ^ b2;
136
137        assert_eq!(res.len(), 3);
138        assert_eq!(res[0], 0x11);
139        assert_eq!(res[1], 0xAA ^ 0x22);
140        assert_eq!(res[2], 0xBB ^ 0x33);
141    }
142
143    #[test]
144    fn test_xor_single_byte_right_aligned() {
145        let b1 = ByteArray::from([0x12, 0x35, 0x56]);
146        let b2 = ByteArray::from(0xFF);
147        let res = b1 ^ b2;
148
149        assert_eq!(res.len(), 3);
150        assert_eq!(res[0], 0x12);
151        assert_eq!(res[1], 0x35);
152        assert_eq!(res[2], 0xFF ^ 0x56);
153    }
154
155    #[test]
156    fn test_xor_assign() {
157        let mut b1: ByteArray = [0xAA, 0xBB, 0xCC].into();
158        let b2 = ByteArray::from([0x55, 0x44, 0x33]);
159        b1 ^= b2;
160
161        assert_eq!(b1.len(), 3);
162        assert_eq!(b1[0], 0xAA ^ 0x55);
163        assert_eq!(b1[1], 0xBB ^ 0x44);
164        assert_eq!(b1[2], 0xCC ^ 0x33);
165    }
166
167    #[test]
168    fn test_and_simple() {
169        let b1: ByteArray = [0xFF, 0xAA, 0x55].into();
170        let b2 = ByteArray::from([0x0F, 0xF0, 0x33]);
171        let res = b1 & b2;
172
173        assert_eq!(res.len(), 3);
174        assert_eq!(res[0], 0xFF & 0x0F);
175        assert_eq!(res[1], 0xAA & 0xF0);
176        assert_eq!(res[2], 0x55 & 0x33);
177    }
178
179    #[test]
180    fn test_and_unequal_length() {
181        let b1: ByteArray = [0xAA, 0xBB].into();
182        let b2 = ByteArray::from([0x11, 0x22, 0x33]);
183        let res = b1 & b2;
184
185        assert_eq!(res.len(), 3);
186        assert_eq!(res[0], 0xFF & 0x11);
187        assert_eq!(res[1], 0xAA & 0x22);
188        assert_eq!(res[2], 0xBB & 0x33);
189    }
190
191    #[test]
192    fn test_and_single_byte() {
193        let b1 = ByteArray::from([0xFF, 0xAA, 0x55]);
194        let b2 = ByteArray::from(0x0F);
195        let res = b1 & b2;
196
197        assert_eq!(res.len(), 3);
198        assert_eq!(res[0], 0xFF);
199        assert_eq!(res[1], 0xFF & 0xAA);
200        assert_eq!(res[2], 0x55 & 0x0F);
201    }
202
203    #[test]
204    fn test_and_assign() {
205        let mut b1: ByteArray = [0xFF, 0xAA, 0x55].into();
206        let b2 = ByteArray::from([0x0F, 0xF0, 0x33]);
207        b1 &= b2;
208
209        assert_eq!(b1.len(), 3);
210        assert_eq!(b1[0], 0xFF & 0x0F);
211        assert_eq!(b1[1], 0xAA & 0xF0);
212        assert_eq!(b1[2], 0x55 & 0x33);
213    }
214
215    #[test]
216    fn test_or_simple() {
217        let b1: ByteArray = [0x0F, 0xAA, 0x55].into();
218        let b2 = ByteArray::from([0xF0, 0x55, 0xAA]);
219        let res = b1 | b2;
220
221        assert_eq!(res.len(), 3);
222        assert_eq!(res[0], 0x0F | 0xF0);
223        assert_eq!(res[1], 0xAA | 0x55);
224        assert_eq!(res[2], 0x55 | 0xAA);
225    }
226
227    #[test]
228    fn test_or_unequal_length() {
229        let b1: ByteArray = [0xAA, 0xBB].into();
230        let b2 = ByteArray::from([0x11, 0x22, 0x33]);
231        let res = b1 | b2;
232
233        assert_eq!(res.len(), 3);
234        assert_eq!(res[0], 0x00 | 0x11);
235        assert_eq!(res[1], 0xAA | 0x22);
236        assert_eq!(res[2], 0xBB | 0x33);
237    }
238
239    #[test]
240    fn test_or_single_byte() {
241        let b1 = ByteArray::from([0x10, 0x20, 0x30]);
242        let b2 = ByteArray::from(0x0F);
243        let res = b1 | b2;
244
245        assert_eq!(res.len(), 3);
246        assert_eq!(res[0], 0x10);
247        assert_eq!(res[1], 0x20);
248        assert_eq!(res[2], 0x30 | 0x0F);
249    }
250
251    #[test]
252    fn test_or_assign() {
253        let mut b1: ByteArray = [0x0F, 0xAA, 0x55].into();
254        let b2 = ByteArray::from([0xF0, 0x55, 0xAA]);
255        b1 |= b2;
256
257        assert_eq!(b1.len(), 3);
258        assert_eq!(b1[0], 0x0F | 0xF0);
259        assert_eq!(b1[1], 0xAA | 0x55);
260        assert_eq!(b1[2], 0x55 | 0xAA);
261    }
262
263    #[test]
264    fn test_not_simple() {
265        let b1: ByteArray = [0xFF, 0x00, 0xAA].into();
266        let res = !b1;
267
268        assert_eq!(res.len(), 3);
269        assert_eq!(res[0], !0xFF);
270        assert_eq!(res[1], !0x00);
271        assert_eq!(res[2], !0xAA);
272    }
273
274    #[test]
275    fn test_not_all_ones() {
276        let b1: ByteArray = [0xFF, 0xFF, 0xFF].into();
277        let res = !b1;
278
279        assert_eq!(res.len(), 3);
280        assert_eq!(res.as_bytes(), [0x00, 0x00, 0x00]);
281    }
282
283    #[test]
284    fn test_not_all_zeros() {
285        let b1: ByteArray = [0x00, 0x00, 0x00].into();
286        let res = !b1;
287
288        assert_eq!(res.len(), 3);
289        assert_eq!(res.as_bytes(), [0xFF, 0xFF, 0xFF]);
290    }
291
292    #[test]
293    fn test_not_single_byte() {
294        let b1 = ByteArray::from(0x55);
295        let res = !b1;
296
297        assert_eq!(res.len(), 1);
298        assert_eq!(res[0], 0xAA);
299    }
300
301    #[test]
302    fn test_combined_operations() {
303        let b1: ByteArray = [0xFF, 0x00].into();
304        let b2: ByteArray = [0xF0, 0x0F].into();
305        let b3: ByteArray = [0x55, 0xAA].into();
306
307        let res = (b1 ^ b2) & b3;
308
309        assert_eq!(res.len(), 2);
310        assert_eq!(res[0], (0xFF ^ 0xF0) & 0x55);
311        assert_eq!(res[1], (0x00 ^ 0x0F) & 0xAA);
312    }
313
314    #[test]
315    fn test_chained_xor_assign() {
316        let mut b1: ByteArray = [0xFF, 0xFF].into();
317        let b2: ByteArray = [0x0F, 0xF0].into();
318        let b3: ByteArray = [0x11, 0x22].into();
319
320        b1 ^= b2;
321        b1 ^= b3;
322
323        assert_eq!(b1[0], 0xFF ^ 0x0F ^ 0x11);
324        assert_eq!(b1[1], 0xFF ^ 0xF0 ^ 0x22);
325    }
326}