byte_array_ops/
model.rs

1//! This is the main model for our ByteArray Object
2
3use super::common::Utils;
4use crate::errors::ByteArrayError;
5use crate::errors::ByteArrayError::{InvalidBinaryChar, InvalidHexChar};
6use alloc::vec;
7use alloc::vec::Vec;
8use core::cmp::PartialEq;
9use core::ops::{Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
10
11/// Debug derive and Display is intentionally left out to avoid any intentional data leakages through formatters
12#[derive(Default, PartialEq, Eq, Clone)] // TODO analyze security side effects of debug
13pub struct ByteArray {
14    pub(crate) bytes: Vec<u8>,
15}
16
17impl ByteArray {
18    /// Standard constructor, use [`Self::with_capacity`] if you already know the capacity of your bytes for performance
19    /// reasons
20    pub fn new() -> Self {
21        ByteArray { bytes: vec![] }
22    }
23
24    /// reserves memory for a certain number of bytes for efficiency purposes
25    pub fn with_capacity(size_in_bytes: usize) -> Self {
26        Self {
27            bytes: Vec::with_capacity(size_in_bytes),
28        }
29    }
30
31    /// fills the byte array with zeros to capacity
32    /// this is usually only useful in the rare case where an odd word padding needs to be set
33    /// in all other cases use [`ByteArray::init_zeros`]. This function does nothing if the ByteArray
34    /// capacity has not been set (is 0)
35    ///
36    /// # Example
37    /// ```
38    /// use byte_array_ops::model::ByteArray;
39    /// let arr = ByteArray::default().fill_zeros(); // does nothing on an array with zero capacity
40    /// assert!(arr.as_bytes().is_empty());
41    ///
42    /// let arr = ByteArray::with_capacity(5).fill_zeros();
43    /// assert_eq!(arr.as_bytes(),[0u8,0,0,0,0]);
44    ///
45    /// // or in the rare cases where a different odd word padding is set
46    ///
47    /// let arr = ByteArray::with_capacity(7)
48    ///                 .fill_zeros();
49    ///
50    /// assert_eq!(arr.as_bytes(),[0u8,0,0,0,0,0,0])
51    ///
52    ///
53    /// ```
54    ///
55    ///
56    #[deprecated(
57        since = "0.3.3",
58        note = "This will be renamed to with_zeros at a future version"
59    )]
60    pub fn fill_zeros(self) -> Self {
61        if self.bytes.capacity() == 0 {
62            self
63        } else {
64            ByteArray {
65                bytes: vec![0u8; self.bytes.capacity()],
66            }
67        }
68    }
69
70    /// fills the byte array with the given `value` to capacity
71    /// this is usually only useful in the rare case where an odd word padding needs to be set
72    /// in all other cases use [`ByteArray::init_value`].
73    ///
74    /// # Note
75    /// This function does nothing (noop) if the ByteArray capacity has not been set (is 0)
76    ///
77    /// # Example
78    /// ```
79    /// use byte_array_ops::model::ByteArray;
80    /// let arr = ByteArray::default().fill_zeros(); // does nothing on an array with zero capacity
81    /// assert!(arr.as_bytes().is_empty());
82    ///
83    /// let arr = ByteArray::with_capacity(5).fill_with(126);
84    /// assert_eq!(arr.as_bytes(),[126u8,126,126,126,126]);
85    ///
86    /// // or in the rare cases where a different odd word padding is set
87    ///
88    /// let arr = ByteArray::with_capacity(7)
89    ///                 .fill_with(5);
90    ///
91    /// assert_eq!(arr.as_bytes(),[5u8,5,5,5,5,5,5]);
92    ///
93    ///
94    /// ```
95    ///
96    ///
97    #[deprecated(
98        since = "0.3.3",
99        note = "This will be renamed to with_value at a future version"
100    )]
101    pub fn fill_with(self, value: u8) -> Self {
102        if self.bytes.capacity() == 0 {
103            self
104        } else {
105            ByteArray {
106                bytes: vec![value; self.bytes.capacity()],
107            }
108        }
109    }
110
111    /// Create a byte array from a hex string (without 0x prefix)
112    ///
113    /// # Example
114    /// ```
115    /// use byte_array_ops::ByteArray;
116    /// let arr = ByteArray::from_hex("deadbeef")?;
117    /// assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
118    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
119    /// ```
120    pub fn from_hex(hex_str: &str) -> Result<Self, ByteArrayError> {
121        if hex_str.is_empty() {
122            return Err(ByteArrayError::EmptyInput);
123        }
124
125        // Filter out underscores and collect
126        let bytes: Vec<u8> = hex_str.bytes().filter(|&b| b != b'_').collect();
127
128        // Validate characters
129        if let Some(&invalid) = bytes.iter().find(|&&b| !b.is_ascii_hexdigit()) {
130            return Err(InvalidHexChar(invalid as char));
131        }
132
133        let hex_count = bytes.len();
134        let byte_count = hex_count / 2 + hex_count % 2;
135        let mut ret = ByteArray::with_capacity(byte_count);
136
137        let mut start = 0;
138
139        // Handle odd length - process first char alone (LEFT padding for network byte order)
140        if hex_count % 2 == 1 {
141            ret.bytes
142                .push(Utils::hex_char_to_nibble_unchecked(bytes[0]));
143            start = 1;
144        }
145
146        // Process remaining pairs
147        for i in (start..hex_count).step_by(2) {
148            let byte_val = Utils::hex_char_to_nibble_unchecked(bytes[i]) << 4
149                | Utils::hex_char_to_nibble_unchecked(bytes[i + 1]);
150            ret.bytes.push(byte_val);
151        }
152
153        Ok(ret)
154    }
155
156    /// Create a byte array from a binary string (without 0b prefix)
157    ///
158    /// # Example
159    /// ```
160    /// use byte_array_ops::ByteArray;
161    /// let arr = ByteArray::from_bin("1010010")?;
162    /// assert_eq!(arr.as_bytes(), [0x52]);
163    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
164    /// ```
165    pub fn from_bin(bin_str: &str) -> Result<Self, ByteArrayError> {
166        if bin_str.is_empty() {
167            return Err(ByteArrayError::EmptyInput);
168        }
169
170        // Filter out underscores and collect
171        let bytes: Vec<u8> = bin_str.bytes().filter(|&b| b != b'_').collect();
172
173        // Validate characters
174        if let Some(&invalid) = bytes.iter().find(|&&b| b != b'0' && b != b'1') {
175            return Err(InvalidBinaryChar(invalid as char));
176        }
177
178        let bit_count = bytes.len();
179        let byte_count = bit_count.div_ceil(8);
180        let mut ret = ByteArray::with_capacity(byte_count);
181
182        let rem = bit_count % 8;
183
184        // Handle partial first byte (left padding) OR entire input if < 8 bits
185        let start = if rem != 0 {
186            let mut byte = 0u8;
187            #[allow(clippy::needless_range_loop)] // our version is more readable than clippy's
188            for i in 0..rem {
189                let bit_value = bytes[i] - b'0';
190                byte |= bit_value << (rem - 1 - i);
191            }
192            ret.bytes.push(byte);
193            rem
194        } else {
195            0
196        };
197
198        // Process remaining full bytes (only if there are any left)
199        for i in (start..bit_count).step_by(8) {
200            let mut byte = 0u8;
201
202            for j in 0..8 {
203                let bit_value = bytes[i + j] - b'0';
204                byte |= bit_value << (7 - j);
205            }
206
207            ret.bytes.push(byte);
208        }
209
210        Ok(ret)
211    }
212
213    /// initialize the array with a certain amount of zeros
214    /// internally this creates the byte array representation with `vec![0u8;count]`
215    /// the rest is default initialized
216    pub fn init_zeros(count: usize) -> Self {
217        ByteArray {
218            bytes: vec![0u8; count],
219        }
220    }
221
222    /// initialize the array with a certain amount of `value`
223    /// internally this creates the byte array representation with `vec![value;count]`
224    /// the rest is default initialized
225    pub fn init_value(value: u8, count: usize) -> Self {
226        ByteArray {
227            bytes: vec![value; count],
228        }
229    }
230
231    /// returns a slices to the interior bytes
232    ///
233    /// # NOTE
234    /// There is another method that provides a zero-cost move of the interior bytes using the
235    /// [`Vec::from::<ByteArray>`] implementation. Please check the [`ByteArray`]'s [`From<ByteArray>`] implementation
236    /// documentation
237    ///
238    /// # Example
239    /// ```
240    /// use byte_array_ops::model::ByteArray;
241    ///
242    /// let arr : ByteArray = "0xff2569".parse().unwrap();
243    ///
244    /// let slice = arr.as_bytes();
245    ///
246    /// assert_eq!(slice, [0xff,0x25,0x69]);
247    /// ```
248    ///
249    pub fn as_bytes(&self) -> &[u8] {
250        &self.bytes
251    }
252
253    #[cfg(feature = "experimental")]
254    pub fn resize(&mut self, new_capacity: usize, value: u8) {
255        unimplemented!(
256            "this is insecure and must find a way on how to handle this in a more secure fashion"
257        );
258        self.bytes.resize(new_capacity, value);
259    }
260
261    /// Tries to append an element to the back of a collection. Fails
262    ///
263    /// # Panics
264    ///  if the new capacity exceeds [`isize::MAX`] bytes.
265    ///
266    /// # Examples
267    ///```
268    /// use byte_array_ops::model::ByteArray;
269    /// let mut arr = ByteArray::default()
270    ///
271    /// arr.try_push(0xab).unwrap();
272    ///
273    /// assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
274    /// ```
275    #[cfg(feature = "experimental")]
276    pub fn try_push(&mut self, value: u8) {
277        self.bytes.push(value);
278    }
279}
280
281// index handling
282
283impl Index<usize> for ByteArray {
284    type Output = u8;
285
286    /// index accessor for ByteArray
287    /// # Panics
288    /// When out of bounds. Use `ByteArray::get()` for a checked getter that returns `Option<&u8>`
289    fn index(&self, index: usize) -> &Self::Output {
290        &self.bytes[index]
291    }
292}
293
294impl IndexMut<usize> for ByteArray {
295    /// Mutable index accessor for ByteArray
296    /// # Panics
297    /// When out of bounds. Use `ByteArray::get()` for a checked getter that returns `Option<&u8>`
298    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
299        &mut self.bytes[index]
300    }
301}
302
303impl Index<Range<usize>> for ByteArray {
304    type Output = [u8];
305
306    fn index(&self, range: Range<usize>) -> &Self::Output {
307        &self.bytes[range]
308    }
309}
310
311impl Index<RangeFrom<usize>> for ByteArray {
312    type Output = [u8];
313
314    fn index(&self, range: RangeFrom<usize>) -> &Self::Output {
315        &self.bytes[range] // [2..]
316    }
317}
318impl Index<RangeTo<usize>> for ByteArray {
319    type Output = [u8];
320
321    fn index(&self, range: RangeTo<usize>) -> &Self::Output {
322        &self.bytes[range] // [..5]
323    }
324}
325impl Index<RangeInclusive<usize>> for ByteArray {
326    type Output = [u8];
327
328    fn index(&self, range: RangeInclusive<usize>) -> &Self::Output {
329        &self.bytes[range] // [2..=5]
330    }
331}
332impl Index<RangeFull> for ByteArray {
333    type Output = [u8];
334
335    fn index(&self, range: RangeFull) -> &Self::Output {
336        &self.bytes[range] // [..]
337    }
338}
339
340impl IndexMut<Range<usize>> for ByteArray {
341    fn index_mut(&mut self, range: Range<usize>) -> &mut Self::Output {
342        &mut self.bytes[range]
343    }
344}
345
346impl IndexMut<RangeFrom<usize>> for ByteArray {
347    fn index_mut(&mut self, range: RangeFrom<usize>) -> &mut Self::Output {
348        &mut self.bytes[range]
349    }
350}
351impl IndexMut<RangeTo<usize>> for ByteArray {
352    fn index_mut(&mut self, range: RangeTo<usize>) -> &mut Self::Output {
353        &mut self.bytes[range]
354    }
355}
356impl IndexMut<RangeInclusive<usize>> for ByteArray {
357    fn index_mut(&mut self, range: RangeInclusive<usize>) -> &mut Self::Output {
358        &mut self.bytes[range] // [2..=5]
359    }
360}
361impl IndexMut<RangeFull> for ByteArray {
362    fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output {
363        &mut self.bytes[range] // [..]
364    }
365}
366
367impl AsRef<[u8]> for ByteArray {
368    fn as_ref(&self) -> &[u8] {
369        self.as_bytes()
370    }
371}
372
373impl AsMut<[u8]> for ByteArray {
374    fn as_mut(&mut self) -> &mut [u8] {
375        &mut self.bytes
376    }
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382    use crate::try_hex;
383    use alloc::vec;
384    use core::str::FromStr;
385
386    fn is_normal<T: Sized + Send + Sync + Unpin>() {}
387
388    #[test]
389    fn test_thread_safety_autotraits() {
390        is_normal::<ByteArray>();
391    }
392
393    #[test]
394    fn test_hex_string_constructor() {
395        let b1: ByteArray = "0xfe81eabd5".parse().unwrap();
396
397        assert_eq!(b1.len(), 5);
398        assert_eq!(b1.bytes, vec![0x0f, 0xe8, 0x1e, 0xab, 0xd5]);
399    }
400
401    #[test]
402    fn test_ba_with_capacity() {
403        let arr = ByteArray::with_capacity(10);
404
405        assert_eq!(arr.bytes.capacity(), 10);
406        assert!(ByteArray::try_from(arr).is_ok());
407    }
408
409    #[test]
410    fn test_with_hex() {
411        let arr = ByteArray::from_hex("ffabc");
412
413        assert_eq!(arr.unwrap().bytes, vec![0x0f, 0xfa, 0xbc]);
414    }
415
416    #[test]
417    fn test_with_bin_less_than_8() {
418        let arr = ByteArray::from_bin("1101111").unwrap();
419        assert_eq!(arr.bytes, vec![0x6f]);
420    }
421
422    #[test]
423    fn test_with_bin_more_than_8() {
424        let arr = ByteArray::from_bin("110111010110111").unwrap();
425        assert_eq!(arr.bytes, vec![0x6e, 0xb7]);
426    }
427
428    #[test]
429    fn test_equality_derives() {
430        let arr: ByteArray = "0xffab12345ffaf".parse().unwrap();
431        let arr_2 = ByteArray::from_hex("ffab12345ffafdeadbeef").unwrap();
432        let arr_3 = arr.clone();
433
434        assert_ne!(arr.bytes, arr_2.bytes);
435        assert_eq!(arr.bytes, arr_3.bytes);
436    }
437
438    #[test]
439    fn test_init_zeros() {
440        let arr = ByteArray::init_zeros(8);
441
442        assert_eq!(arr.bytes, [0u8, 0, 0, 0, 0, 0, 0, 0]);
443    }
444
445    #[test]
446    fn test_init_value() {
447        let arr = ByteArray::init_value(254, 8);
448
449        assert_eq!(arr.bytes, [254, 254, 254, 254, 254, 254, 254, 254]);
450    }
451
452    #[test]
453    fn test_mutable_idx_accessor() {
454        let mut arr: ByteArray = "0xffab1245".parse().unwrap();
455
456        arr[3] = 0xbe;
457        arr[1] = 0xfa;
458
459        assert_eq!(arr.bytes, ByteArray::from_str("0xfffa12be").unwrap().bytes);
460    }
461
462    #[test]
463    #[allow(deprecated)]
464    fn test_fill_zeros() {
465        let arr = ByteArray::default().fill_zeros();
466        assert!(arr.as_bytes().is_empty());
467
468        let arr = ByteArray::with_capacity(5).fill_zeros();
469        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0]);
470
471        let arr = ByteArray::with_capacity(7).fill_zeros();
472
473        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0, 0, 0])
474    }
475
476    #[test]
477    #[allow(deprecated)]
478    fn test_fill_with() {
479        let arr = ByteArray::default().fill_zeros();
480        assert!(arr.as_bytes().is_empty());
481
482        let arr = ByteArray::with_capacity(5).fill_with(126);
483        assert_eq!(arr.as_bytes(), [126u8, 126, 126, 126, 126]);
484
485        let arr = ByteArray::with_capacity(7).fill_with(5);
486
487        assert_eq!(arr.as_bytes(), [5u8, 5, 5, 5, 5, 5, 5]);
488    }
489
490    #[test]
491    #[cfg(feature = "experimental")]
492    fn test_push_element() {
493        let mut arr: ByteArray = vec![1, 2].into();
494
495        arr.try_push(0xab);
496
497        assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
498    }
499
500    #[test]
501    fn test_hex_with_underscores() {
502        let arr = ByteArray::from_hex("de_ad_be_ef").unwrap();
503        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
504    }
505
506    #[test]
507    fn test_bin_with_underscores() {
508        let arr = ByteArray::from_bin("1010_0101").unwrap();
509        assert_eq!(arr.as_bytes(), [0xa5]);
510    }
511
512    #[test]
513    fn test_bin_with_underscores_odd_length() {
514        let arr = ByteArray::from_bin("110_1111").unwrap();
515        assert_eq!(arr.as_bytes(), [0x6f]);
516    }
517
518    #[test]
519    #[should_panic]
520    fn test_as_mut_empty() {
521        let mut bytes = ByteArray::default();
522
523        let mut_ref = bytes.as_mut();
524
525        mut_ref[0] = 0x00;
526    }
527
528    #[test]
529    fn test_ranges() {
530        let bytes = try_hex!("ff_aa_fa_b8_ca_12_15_5a_5c_6f").unwrap();
531
532        assert_eq!(bytes[1..2], [0xaa]);
533        assert_eq!(bytes[0..=2], [0xff, 0xaa, 0xfa]);
534        assert_eq!(bytes[6..], [0x15, 0x5a, 0x5c, 0x6f]);
535        assert_eq!(bytes[..3], [0xff, 0xaa, 0xfa]);
536        assert_eq!(
537            bytes[..],
538            [0xff, 0xaa, 0xfa, 0xb8, 0xca, 0x12, 0x15, 0x5a, 0x5c, 0x6f]
539        );
540    }
541
542    #[test]
543    fn test_ranges_mut() {
544        let mut bytes = try_hex!("ff_aa_fa_b8_ca_12_15_5a_5c_6f").unwrap();
545
546        let slice = &mut bytes[1..3];
547        slice[0] = 0x00;
548        slice[1] = 0x01;
549        let expected: ByteArray = "0xff_00_01_b8_ca_12_15_5a_5c_6f".parse().unwrap();
550
551        assert_eq!(bytes, expected);
552
553        let slice = &mut bytes[0..=2];
554        slice[0] = 0x12;
555        slice[1] = 0xab;
556        slice[2] = 0xbc;
557
558        let expected: ByteArray = "0x12_ab_bc_b8_ca_12_15_5a_5c_6f".parse().unwrap();
559
560        assert_eq!(bytes, expected);
561
562        let slice = &mut bytes[6..];
563        slice[0] = 0x01;
564        slice[1] = 0x02;
565        slice[2] = 0x03;
566        slice[3] = 0x04;
567
568        let expected: ByteArray = "0x12_ab_bc_b8_ca_12_01_02_03_04".parse().unwrap();
569
570        assert_eq!(bytes, expected);
571
572        let slice = &mut bytes[..3];
573        slice[0] = 0x00;
574        slice[1] = 0x02;
575        slice[2] = 0x03;
576
577        let expected: ByteArray = "0x00_02_03_b8_ca_12_01_02_03_04".parse().unwrap();
578
579        assert_eq!(bytes, expected);
580
581        let slice = &mut bytes[..];
582
583        slice.iter_mut().for_each(|e| *e = 0x01);
584
585        let expected: ByteArray = "0x01_01_01_01_01_01_01_01_01_01".parse().unwrap();
586
587        assert_eq!(bytes, expected);
588    }
589
590    #[test]
591    fn test_as_ref() {
592        let bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
593        let bytes_ref = bytes.as_ref();
594
595        assert_eq!(bytes.as_bytes(), bytes_ref);
596    }
597
598    #[test]
599    fn test_as_mut() {
600        let mut bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
601
602        let mut_ref = bytes.as_mut();
603
604        mut_ref[0] = 0xFF;
605        mut_ref[5] = 0xab;
606        mut_ref[7] = 0x1a;
607
608        assert_eq!(
609            bytes.as_bytes(),
610            [0xff, 0x02, 0x03, 0x04, 0x05, 0xab, 0x07, 0x1a]
611        )
612    }
613}