byte_array_ops/byte_array/
model.rs

1//! This is the main model for our ByteArray Object
2
3use super::common::Utils;
4use crate::byte_array::errors::ByteArrayError;
5use crate::byte_array::errors::ByteArrayError::{InvalidBinaryChar, InvalidHexChar};
6use alloc::vec;
7use alloc::vec::Vec;
8use core::cmp::PartialEq;
9use core::ops::{Index, IndexMut};
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    /// uses the default [Byte padding direction][`ByteArrayOddWordPad`]
26    pub fn with_capacity(size_in_bytes: usize) -> Self {
27        Self {
28            bytes: Vec::with_capacity(size_in_bytes),
29        }
30    }
31
32    /// fills the byte array with zeros to capacity
33    /// this is usually only useful in the rare case where an odd word padding needs to be set
34    /// in all other cases use [`ByteArray::init_zeros`]. This function does nothing if the ByteArray
35    /// capacity has not been set (is 0)
36    ///
37    /// # Example
38    /// ```
39    /// use byte_array_ops::byte_array::model::ByteArray;
40    /// let arr = ByteArray::default().fill_zeros(); // does nothing on an array with zero capacity
41    /// assert!(arr.as_bytes().is_empty());
42    ///
43    /// let arr = ByteArray::with_capacity(5).fill_zeros();
44    /// assert_eq!(arr.as_bytes(),[0u8,0,0,0,0]);
45    ///
46    /// // or in the rare cases where a different odd word padding is set
47    ///
48    /// let arr = ByteArray::with_capacity(7)
49    ///                 .fill_zeros();
50    ///
51    /// assert_eq!(arr.as_bytes(),[0u8,0,0,0,0,0,0])
52    ///
53    ///
54    /// ```
55    ///
56    ///
57    pub fn fill_zeros(self) -> Self {
58        if self.bytes.capacity() == 0 {
59            self
60        } else {
61            ByteArray {
62                bytes: vec![0u8; self.bytes.capacity()],
63            }
64        }
65    }
66
67    /// fills the byte array with the given `value` to capacity
68    /// this is usually only useful in the rare case where an odd word padding needs to be set
69    /// in all other cases use [`ByteArray::init_value`].
70    ///
71    /// # Note
72    /// This function does nothing (noop) if the ByteArray capacity has not been set (is 0)
73    ///
74    /// # Example
75    /// ```
76    /// use byte_array_ops::byte_array::model::ByteArray;
77    /// let arr = ByteArray::default().fill_zeros(); // does nothing on an array with zero capacity
78    /// assert!(arr.as_bytes().is_empty());
79    ///
80    /// let arr = ByteArray::with_capacity(5).fill_with(126);
81    /// assert_eq!(arr.as_bytes(),[126u8,126,126,126,126]);
82    ///
83    /// // or in the rare cases where a different odd word padding is set
84    ///
85    /// let arr = ByteArray::with_capacity(7)
86    ///                 .fill_with(5);
87    ///
88    /// assert_eq!(arr.as_bytes(),[5u8,5,5,5,5,5,5]);
89    ///
90    ///
91    /// ```
92    ///
93    ///
94    pub fn fill_with(self, value: u8) -> Self {
95        if self.bytes.capacity() == 0 {
96            self
97        } else {
98            ByteArray {
99                bytes: vec![value; self.bytes.capacity()],
100            }
101        }
102    }
103
104    /// Create a byte array from a hex string (without 0x prefix)
105    ///
106    /// # Example
107    /// ```
108    /// use byte_array_ops::ByteArray;
109    /// let arr = ByteArray::from_hex("deadbeef")?;
110    /// assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
111    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
112    /// ```
113    pub fn from_hex(hex_str: &str) -> Result<Self, ByteArrayError> {
114        if hex_str.is_empty() {
115            return Err(ByteArrayError::EmptyInput);
116        }
117
118        // Filter out underscores and collect
119        let bytes: Vec<u8> = hex_str.bytes().filter(|&b| b != b'_').collect();
120
121        // Validate characters
122        if let Some(&invalid) = bytes.iter().find(|&&b| !b.is_ascii_hexdigit()) {
123            return Err(InvalidHexChar(invalid as char));
124        }
125
126        let hex_count = bytes.len();
127        let byte_count = hex_count / 2 + hex_count % 2;
128        let mut ret = ByteArray::with_capacity(byte_count);
129
130        let mut start = 0;
131
132        // Handle odd length - process first char alone (LEFT padding for network byte order)
133        if hex_count % 2 == 1 {
134            ret.bytes
135                .push(Utils::hex_char_to_nibble_unchecked(bytes[0]));
136            start = 1;
137        }
138
139        // Process remaining pairs
140        for i in (start..hex_count).step_by(2) {
141            let byte_val = Utils::hex_char_to_nibble_unchecked(bytes[i]) << 4
142                | Utils::hex_char_to_nibble_unchecked(bytes[i + 1]);
143            ret.bytes.push(byte_val);
144        }
145
146        Ok(ret)
147    }
148
149    /// Create a byte array from a binary string (without 0b prefix)
150    ///
151    /// # Example
152    /// ```
153    /// use byte_array_ops::ByteArray;
154    /// let arr = ByteArray::from_bin("1010010")?;
155    /// assert_eq!(arr.as_bytes(), [0x52]);
156    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
157    /// ```
158    pub fn from_bin(bin_str: &str) -> Result<Self, ByteArrayError> {
159        if bin_str.is_empty() {
160            return Err(ByteArrayError::EmptyInput);
161        }
162
163        // Filter out underscores and collect
164        let bytes: Vec<u8> = bin_str.bytes().filter(|&b| b != b'_').collect();
165
166        // Validate characters
167        if let Some(&invalid) = bytes.iter().find(|&&b| b != b'0' && b != b'1') {
168            return Err(InvalidBinaryChar(invalid as char));
169        }
170
171        let bit_count = bytes.len();
172        let byte_count = bit_count.div_ceil(8);
173        let mut ret = ByteArray::with_capacity(byte_count);
174
175        let rem = bit_count % 8;
176
177        // Handle partial first byte (left padding) OR entire input if < 8 bits
178        let start = if rem != 0 {
179            let mut byte = 0u8;
180            #[allow(clippy::needless_range_loop)] // our version is more readable than clippy's
181            for i in 0..rem {
182                let bit_value = bytes[i] - b'0';
183                byte |= bit_value << (rem - 1 - i);
184            }
185            ret.bytes.push(byte);
186            rem
187        } else {
188            0
189        };
190
191        // Process remaining full bytes (only if there are any left)
192        for i in (start..bit_count).step_by(8) {
193            let mut byte = 0u8;
194
195            for j in 0..8 {
196                let bit_value = bytes[i + j] - b'0';
197                byte |= bit_value << (7 - j);
198            }
199
200            ret.bytes.push(byte);
201        }
202
203        Ok(ret)
204    }
205
206    /// initialize the array with a certain amount of zeros
207    /// internally this creates the byte array representation with `vec![0u8;count]`
208    /// the rest is default initialized
209    pub fn init_zeros(count: usize) -> Self {
210        ByteArray {
211            bytes: vec![0u8; count],
212        }
213    }
214
215    /// initialize the array with a certain amount of `value`
216    /// internally this creates the byte array representation with `vec![value;count]`
217    /// the rest is default initialized
218    pub fn init_value(value: u8, count: usize) -> Self {
219        ByteArray {
220            bytes: vec![value; count],
221        }
222    }
223
224    /// returns a slices to the interior bytes
225    ///
226    /// # NOTE
227    /// There is another method that provides a zero-cost move of the interior bytes using the
228    /// [`Vec::from::<ByteArray>`] implementation. Please check the [`ByteArray`]'s [`From<ByteArray>`] implementation
229    /// documentation
230    ///
231    /// # Example
232    /// ```
233    /// use byte_array_ops::byte_array::model::ByteArray;
234    ///
235    /// let arr : ByteArray = "0xff2569".parse().unwrap();
236    ///
237    /// let slice = arr.as_bytes();
238    ///
239    /// assert_eq!(slice, [0xff,0x25,0x69]);
240    /// ```
241    ///
242    /// # Alternative (Zero-cost move)
243    /// ```
244    /// use byte_array_ops::byte_array::model::ByteArray;
245    /// let arr : ByteArray = "0b11101111".parse().unwrap();
246    ///
247    /// assert_eq!(arr.as_bytes(), [0xef]);
248    ///
249    /// let moved_data : Vec<u8> = arr.into();
250    /// assert_eq!(moved_data, vec![0xef]);
251    /// ```
252    ///
253    pub fn as_bytes(&self) -> &[u8] {
254        &self.bytes
255    }
256
257    /// returns the number of bytes in the array
258    /// TODO add example
259    pub fn len(&self) -> usize {
260        self.bytes.len()
261    }
262
263    /// returns true if there are no bytes in the array
264    /// TODO example
265    pub fn is_empty(&self) -> bool {
266        self.bytes.is_empty()
267    }
268
269    #[cfg(feature = "experimental")]
270    pub fn resize(&mut self, new_capacity: usize, value: u8) {
271        unimplemented!(
272            "this is insecure and must find a way on how to handle this in a more secure fashion"
273        );
274        self.bytes.resize(new_capacity, value);
275    }
276
277    /// Tries to append an element to the back of a collection. Fails
278    ///
279    /// # Panics
280    ///  if the new capacity exceeds [`isize::MAX`] bytes.
281    ///
282    /// # Examples
283    ///```
284    /// use byte_array_ops::byte_array::model::ByteArray;
285    /// let mut arr = ByteArray::default()
286    ///
287    /// arr.try_push(0xab).unwrap();
288    ///
289    /// assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
290    /// ```
291    #[cfg(feature = "experimental")]
292    pub fn try_push(&mut self, value: u8) {
293        self.bytes.push(value);
294    }
295}
296
297// index handling
298
299impl Index<usize> for ByteArray {
300    type Output = u8;
301
302    /// index accessor for ByteArray
303    /// # Panics
304    /// When out of bound use [`ByteArray::get`] instead if you want a checked getter
305    fn index(&self, index: usize) -> &Self::Output {
306        &self.bytes[index]
307    }
308}
309
310impl IndexMut<usize> for ByteArray {
311    /// Mutable index accessor for ByteArray
312    /// # Panics
313    /// When out of bound use [`ByteArray::get`] instead if you want a checked getter
314    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
315        &mut self.bytes[index]
316    }
317}
318
319impl ByteArray {
320    pub fn get(&self, index: usize) -> Option<&u8> {
321        self.bytes.get(index)
322    }
323}
324
325impl AsRef<[u8]> for ByteArray {
326    fn as_ref(&self) -> &[u8] {
327        self.as_bytes()
328    }
329}
330
331impl AsMut<[u8]> for ByteArray {
332    fn as_mut(&mut self) -> &mut [u8] {
333        &mut self.bytes
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340    use alloc::vec;
341    use core::str::FromStr;
342
343    #[test]
344    fn test_hex_string_constructor() {
345        let b1: ByteArray = "0xfe81eabd5".parse().unwrap();
346
347        assert_eq!(b1.len(), 5);
348        assert_eq!(b1.bytes, vec![0x0f, 0xe8, 0x1e, 0xab, 0xd5]);
349    }
350
351    #[test]
352    fn test_ba_with_capacity() {
353        let arr = ByteArray::with_capacity(10);
354
355        assert_eq!(arr.bytes.capacity(), 10);
356        assert!(ByteArray::try_from(arr).is_ok());
357    }
358
359    #[test]
360    fn test_with_hex() {
361        let arr = ByteArray::from_hex("ffabc");
362
363        assert_eq!(arr.unwrap().bytes, vec![0x0f, 0xfa, 0xbc]);
364    }
365
366    #[test]
367    fn test_with_bin_less_than_8() {
368        let arr = ByteArray::from_bin("1101111").unwrap();
369        assert_eq!(arr.bytes, vec![0x6f]);
370    }
371
372    #[test]
373    fn test_with_bin_more_than_8() {
374        let arr = ByteArray::from_bin("110111010110111").unwrap();
375        assert_eq!(arr.bytes, vec![0x6e, 0xb7]);
376    }
377
378    #[test]
379    fn test_len() {
380        let arr = ByteArray::from_hex("ffab12345bc").expect("error converting");
381
382        assert_eq!(arr.len(), 6);
383    }
384
385    #[test]
386    fn test_is_empty() {
387        let arr = ByteArray::default();
388        assert_eq!(arr.is_empty(), true);
389
390        let arr = ByteArray::from_hex("bb").expect("error converting");
391
392        assert_eq!(arr.is_empty(), false);
393    }
394
395    #[test]
396    fn test_equality_derives() {
397        let arr: ByteArray = "0xffab12345ffaf".parse().unwrap();
398        let arr_2 = ByteArray::from_hex("ffab12345ffafdeadbeef").unwrap();
399        let arr_3 = arr.clone();
400
401        assert_ne!(arr.bytes, arr_2.bytes);
402        assert_eq!(arr.bytes, arr_3.bytes);
403    }
404
405    #[test]
406    fn test_init_zeros() {
407        let arr = ByteArray::init_zeros(8);
408
409        assert_eq!(arr.bytes, [0u8, 0, 0, 0, 0, 0, 0, 0]);
410    }
411
412    #[test]
413    fn test_init_value() {
414        let arr = ByteArray::init_value(254, 8);
415
416        assert_eq!(arr.bytes, [254, 254, 254, 254, 254, 254, 254, 254]);
417    }
418
419    #[test]
420    fn test_mutable_idx_accessor() {
421        let mut arr: ByteArray = "0xffab1245".parse().unwrap();
422
423        arr[3] = 0xbe;
424        arr[1] = 0xfa;
425
426        assert_eq!(arr.bytes, ByteArray::from_str("0xfffa12be").unwrap().bytes);
427    }
428
429    #[test]
430    fn test_fill_zeros() {
431        let arr = ByteArray::default().fill_zeros();
432        assert!(arr.as_bytes().is_empty());
433
434        let arr = ByteArray::with_capacity(5).fill_zeros();
435        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0]);
436
437        let arr = ByteArray::with_capacity(7).fill_zeros();
438
439        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0, 0, 0])
440    }
441
442    #[test]
443    fn test_fill_with() {
444        let arr = ByteArray::default().fill_zeros();
445        assert!(arr.as_bytes().is_empty());
446
447        let arr = ByteArray::with_capacity(5).fill_with(126);
448        assert_eq!(arr.as_bytes(), [126u8, 126, 126, 126, 126]);
449
450        let arr = ByteArray::with_capacity(7).fill_with(5);
451
452        assert_eq!(arr.as_bytes(), [5u8, 5, 5, 5, 5, 5, 5]);
453    }
454
455    #[test]
456    #[cfg(feature = "experimental")]
457    fn test_push_element() {
458        let mut arr: ByteArray = vec![1, 2].into();
459
460        arr.try_push(0xab);
461
462        assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
463    }
464
465    #[test]
466    fn test_hex_with_underscores() {
467        let arr = ByteArray::from_hex("de_ad_be_ef").unwrap();
468        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
469    }
470
471    #[test]
472    fn test_bin_with_underscores() {
473        let arr = ByteArray::from_bin("1010_0101").unwrap();
474        assert_eq!(arr.as_bytes(), [0xa5]);
475    }
476
477    #[test]
478    fn test_bin_with_underscores_odd_length() {
479        let arr = ByteArray::from_bin("110_1111").unwrap();
480        assert_eq!(arr.as_bytes(), [0x6f]);
481    }
482
483    #[test]
484    #[should_panic]
485    fn test_as_mut_empty() {
486        let mut bytes = ByteArray::default();
487
488        let mut_ref = bytes.as_mut();
489
490        mut_ref[0] = 0x00;
491    }
492
493    #[test]
494    fn test_as_ref() {
495        let bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
496        let bytes_ref = bytes.as_ref();
497
498        assert_eq!(bytes.as_bytes(), bytes_ref);
499    }
500
501    #[test]
502    fn test_as_mut() {
503        let mut bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
504
505        let mut_ref = bytes.as_mut();
506
507        mut_ref[0] = 0xFF;
508        mut_ref[5] = 0xab;
509        mut_ref[7] = 0x1a;
510
511        assert_eq!(
512            bytes.as_bytes(),
513            [0xff, 0x02, 0x03, 0x04, 0x05, 0xab, 0x07, 0x1a]
514        )
515    }
516}