byte_array_ops/byte_array/
ops.rs

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