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