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 crate::errors::ByteArraySecurityError::DataRemnanceRisk;
7use alloc::vec;
8use alloc::vec::Vec;
9use core::cmp::PartialEq;
10use core::ops::{Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo};
11
12/// Debug derive and Display is intentionally left out to avoid any intentional data leakages through formatters
13#[derive(Default, PartialEq, Eq, Clone)] // TODO analyze security side effects of debug
14pub struct ByteArray {
15    pub(crate) bytes: Vec<u8>,
16}
17
18impl ByteArray {
19    /// Standard constructor, use [`Self::with_capacity`] if you already know the capacity of your bytes for performance
20    /// reasons
21    pub fn new() -> Self {
22        ByteArray { bytes: vec![] }
23    }
24
25    /// reserves memory for a certain number of bytes for efficiency purposes
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::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    #[deprecated(
58        since = "0.3.3",
59        note = "This will be renamed to with_zeros at a future version"
60    )]
61    pub fn fill_zeros(self) -> Self {
62        if self.bytes.capacity() == 0 {
63            self
64        } else {
65            ByteArray {
66                bytes: vec![0u8; self.bytes.capacity()],
67            }
68        }
69    }
70
71    /// fills the byte array with the given `value` to capacity
72    /// this is usually only useful in the rare case where an odd word padding needs to be set
73    /// in all other cases use [`ByteArray::init_value`].
74    ///
75    /// # Note
76    /// This function does nothing (noop) if the ByteArray capacity has not been set (is 0)
77    ///
78    /// # Example
79    /// ```
80    /// use byte_array_ops::model::ByteArray;
81    /// let arr = ByteArray::default().fill_zeros(); // does nothing on an array with zero capacity
82    /// assert!(arr.as_bytes().is_empty());
83    ///
84    /// let arr = ByteArray::with_capacity(5).fill_with(126);
85    /// assert_eq!(arr.as_bytes(),[126u8,126,126,126,126]);
86    ///
87    /// // or in the rare cases where a different odd word padding is set
88    ///
89    /// let arr = ByteArray::with_capacity(7)
90    ///                 .fill_with(5);
91    ///
92    /// assert_eq!(arr.as_bytes(),[5u8,5,5,5,5,5,5]);
93    ///
94    ///
95    /// ```
96    ///
97    ///
98    #[deprecated(
99        since = "0.3.3",
100        note = "This will be renamed to with_value at a future version"
101    )]
102    pub fn fill_with(self, value: u8) -> Self {
103        if self.bytes.capacity() == 0 {
104            self
105        } else {
106            ByteArray {
107                bytes: vec![value; self.bytes.capacity()],
108            }
109        }
110    }
111
112    /// Create a byte array from a hex string (without 0x prefix)
113    ///
114    /// # Example
115    /// ```
116    /// use byte_array_ops::ByteArray;
117    /// let arr = ByteArray::from_hex("deadbeef")?;
118    /// assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
119    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
120    /// ```
121    pub fn from_hex(hex_str: &str) -> Result<Self, ByteArrayError> {
122        if hex_str.is_empty() {
123            return Err(ByteArrayError::EmptyInput);
124        }
125
126        // Filter out underscores and collect
127        let bytes: Vec<u8> = hex_str.bytes().filter(|&b| b != b'_').collect();
128
129        // Validate characters
130        if let Some(&invalid) = bytes.iter().find(|&&b| !b.is_ascii_hexdigit()) {
131            return Err(InvalidHexChar(invalid as char));
132        }
133
134        let hex_count = bytes.len();
135        let byte_count = hex_count / 2 + hex_count % 2;
136        let mut ret = ByteArray::with_capacity(byte_count);
137
138        let mut start = 0;
139
140        // Handle odd length - process first char alone (LEFT padding for network byte order)
141        if hex_count % 2 == 1 {
142            ret.bytes
143                .push(Utils::hex_char_to_nibble_unchecked(bytes[0]));
144            start = 1;
145        }
146
147        // Process remaining pairs
148        for i in (start..hex_count).step_by(2) {
149            let byte_val = Utils::hex_char_to_nibble_unchecked(bytes[i]) << 4
150                | Utils::hex_char_to_nibble_unchecked(bytes[i + 1]);
151            ret.bytes.push(byte_val);
152        }
153
154        Ok(ret)
155    }
156
157    /// Create a byte array from a binary string (without 0b prefix)
158    ///
159    /// # Example
160    /// ```
161    /// use byte_array_ops::ByteArray;
162    /// let arr = ByteArray::from_bin("1010010")?;
163    /// assert_eq!(arr.as_bytes(), [0x52]);
164    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
165    /// ```
166    pub fn from_bin(bin_str: &str) -> Result<Self, ByteArrayError> {
167        if bin_str.is_empty() {
168            return Err(ByteArrayError::EmptyInput);
169        }
170
171        // Filter out underscores and collect
172        let bytes: Vec<u8> = bin_str.bytes().filter(|&b| b != b'_').collect();
173
174        // Validate characters
175        if let Some(&invalid) = bytes.iter().find(|&&b| b != b'0' && b != b'1') {
176            return Err(InvalidBinaryChar(invalid as char));
177        }
178
179        let bit_count = bytes.len();
180        let byte_count = bit_count.div_ceil(8);
181        let mut ret = ByteArray::with_capacity(byte_count);
182
183        let rem = bit_count % 8;
184
185        // Handle partial first byte (left padding) OR entire input if < 8 bits
186        let start = if rem != 0 {
187            let mut byte = 0u8;
188            #[allow(clippy::needless_range_loop)] // our version is more readable than clippy's
189            for i in 0..rem {
190                let bit_value = bytes[i] - b'0';
191                byte |= bit_value << (rem - 1 - i);
192            }
193            ret.bytes.push(byte);
194            rem
195        } else {
196            0
197        };
198
199        // Process remaining full bytes (only if there are any left)
200        for i in (start..bit_count).step_by(8) {
201            let mut byte = 0u8;
202
203            for j in 0..8 {
204                let bit_value = bytes[i + j] - b'0';
205                byte |= bit_value << (7 - j);
206            }
207
208            ret.bytes.push(byte);
209        }
210
211        Ok(ret)
212    }
213
214    /// initialize the array with a certain amount of zeros
215    /// internally this creates the byte array representation with `vec![0u8;count]`
216    /// the rest is default initialized
217    pub fn init_zeros(count: usize) -> Self {
218        ByteArray {
219            bytes: vec![0u8; count],
220        }
221    }
222
223    /// initialize the array with a certain amount of `value`
224    /// internally this creates the byte array representation with `vec![value;count]`
225    /// the rest is default initialized
226    pub fn init_value(value: u8, count: usize) -> Self {
227        ByteArray {
228            bytes: vec![value; count],
229        }
230    }
231
232    /// returns a slices to the interior bytes
233    ///
234    /// # NOTE
235    /// There is another method that provides a zero-cost move of the interior bytes using the
236    /// [`Vec::from::<ByteArray>`] implementation. Please check the [`ByteArray`]'s [`From<ByteArray>`] implementation
237    /// documentation
238    ///
239    /// # Example
240    /// ```
241    /// use byte_array_ops::model::ByteArray;
242    ///
243    /// let arr : ByteArray = "0xff2569".parse().unwrap();
244    ///
245    /// let slice = arr.as_bytes();
246    ///
247    /// assert_eq!(slice, [0xff,0x25,0x69]);
248    /// ```
249    ///
250    pub fn as_bytes(&self) -> &[u8] {
251        &self.bytes
252    }
253
254    #[cfg(feature = "experimental")]
255    pub fn resize(&mut self, new_capacity: usize, value: u8) {
256        unimplemented!(
257            "this is insecure and must find a way on how to handle this in a more secure fashion"
258        );
259        self.bytes.resize(new_capacity, value);
260    }
261
262    /// Tries to append an element to the back of a collection. Fails
263    ///
264    /// # Panics
265    ///  if the new capacity exceeds [`isize::MAX`] bytes.
266    ///
267    /// # Examples
268    ///```
269    /// use byte_array_ops::model::ByteArray;
270    /// let mut arr = ByteArray::default()
271    ///
272    /// arr.try_push(0xab).unwrap();
273    ///
274    /// assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
275    /// ```
276    #[cfg(feature = "experimental")]
277    pub fn try_push(&mut self, value: u8) {
278        self.bytes.push(value);
279    }
280
281    /// internal Utility to combine two bytearrays while ensuring secure reallocation
282    #[doc(hidden)]
283    #[inline(always)]
284    fn combine(
285        lhs: ByteArray,
286        rhs: ByteArray,
287        prealloc_cap: usize,
288    ) -> Result<Self, ByteArrayError> {
289        const CAP_SAFETY_EXTENSION: usize = 10;
290        let total_cap = prealloc_cap + CAP_SAFETY_EXTENSION;
291        let mut buf = Vec::<u8>::with_capacity(total_cap);
292
293        let addr_pre_extend = buf.as_ptr();
294        buf.extend_from_slice(lhs.as_bytes());
295        if addr_pre_extend != buf.as_ptr() {
296            return Err(DataRemnanceRisk.into());
297        }
298        buf.extend_from_slice(rhs.as_bytes());
299        if addr_pre_extend != buf.as_ptr() {
300            return Err(DataRemnanceRisk.into());
301        }
302
303        // drop old byte buffers before creating a new byte array to avoid leaking information in case the from method was interrupted
304        // Note: we implement zeroize on drop
305        drop(lhs);
306        drop(rhs);
307
308        Ok(ByteArray::from(buf))
309    }
310
311    /// Chained constructor helper for concatening ByteArrays together into one while ensuring to unintended reallocations happens
312    /// to avoid data remnance.
313    /// Preallocate the correct size with `ByteArray::with_capacity` then unintended reallocations might happen)
314    ///
315    /// # Capacity allocation caveat
316    /// this function adjusts the inner bytes capacity according to the sum of the lengths of the actual data of both ByteArrays,
317    /// if you want to preserve the capacity to be the sum of the total capacities of both arrays; then use [`ByteArray::with_extend_preserve_cap`]
318    ///
319    /// # Errors
320    ///
321    /// This function fails if a reallocation happens with [`crate::errors::ByteArraySecurityError::DataRemnanceRisk`]
322    ///
323    /// # Example
324    /// ```
325    /// use byte_array_ops::ByteArray;
326    ///
327    /// let arr1 = ByteArray::from_hex("dead")?;
328    /// let arr2 = ByteArray::from_hex("beef")?;
329    /// let result = arr1.try_extend(arr2)?;
330    ///
331    /// assert_eq!(result.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
332    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
333    /// ```
334    #[inline]
335    pub fn try_extend(self, other: ByteArray) -> Result<Self, ByteArrayError> {
336        let cap = self.bytes.len() + other.bytes.len();
337        Self::combine(self, other, cap)
338    }
339
340    /// Same as [`ByteArray::try_extend`] but instead of reserving actual lengths, this reserves the
341    /// sum of capacities of both bytearrays
342    ///
343    /// # Warning
344    /// For Bytearrays with very large capacities but whose actual length is very small this tends to be
345    /// very inefficient and might allocate too much memory on constrained environments.
346    /// For these cases use [`ByteArray::try_extend`] chained , this might be safer at the cost of more
347    /// secure reallocations
348    ///
349    /// # Example
350    /// ```
351    /// use byte_array_ops::ByteArray;
352    ///
353    /// let arr1 = ByteArray::with_capacity(100);
354    /// let arr2 = ByteArray::from_hex("ff")?;
355    /// let result = arr1.try_extend_with_preserve_cap(arr2)?;
356    ///
357    /// // Result has the data from arr2
358    /// assert_eq!(result.as_bytes(), [0xff]);
359    /// assert_eq!(result.len(), 1);
360    /// # Ok::<(), byte_array_ops::errors::ByteArrayError>(())
361    /// ```
362    #[inline]
363    pub fn try_extend_with_preserve_cap(self, other: ByteArray) -> Result<Self, ByteArrayError> {
364        let cap = self.bytes.capacity() + other.bytes.capacity();
365        Self::combine(self, other, cap)
366    }
367}
368
369// index handling
370
371impl Index<usize> for ByteArray {
372    type Output = u8;
373
374    /// index accessor for ByteArray
375    /// # Panics
376    /// When out of bounds. Use `ByteArray::get()` for a checked getter that returns `Option<&u8>`
377    fn index(&self, index: usize) -> &Self::Output {
378        &self.bytes[index]
379    }
380}
381
382impl IndexMut<usize> for ByteArray {
383    /// Mutable index accessor for ByteArray
384    /// # Panics
385    /// When out of bounds. Use `ByteArray::get()` for a checked getter that returns `Option<&u8>`
386    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
387        &mut self.bytes[index]
388    }
389}
390
391impl Index<Range<usize>> for ByteArray {
392    type Output = [u8];
393
394    fn index(&self, range: Range<usize>) -> &Self::Output {
395        &self.bytes[range]
396    }
397}
398
399impl Index<RangeFrom<usize>> for ByteArray {
400    type Output = [u8];
401
402    fn index(&self, range: RangeFrom<usize>) -> &Self::Output {
403        &self.bytes[range] // [2..]
404    }
405}
406impl Index<RangeTo<usize>> for ByteArray {
407    type Output = [u8];
408
409    fn index(&self, range: RangeTo<usize>) -> &Self::Output {
410        &self.bytes[range] // [..5]
411    }
412}
413impl Index<RangeInclusive<usize>> for ByteArray {
414    type Output = [u8];
415
416    fn index(&self, range: RangeInclusive<usize>) -> &Self::Output {
417        &self.bytes[range] // [2..=5]
418    }
419}
420impl Index<RangeFull> for ByteArray {
421    type Output = [u8];
422
423    fn index(&self, range: RangeFull) -> &Self::Output {
424        &self.bytes[range] // [..]
425    }
426}
427
428impl IndexMut<Range<usize>> for ByteArray {
429    fn index_mut(&mut self, range: Range<usize>) -> &mut Self::Output {
430        &mut self.bytes[range]
431    }
432}
433
434impl IndexMut<RangeFrom<usize>> for ByteArray {
435    fn index_mut(&mut self, range: RangeFrom<usize>) -> &mut Self::Output {
436        &mut self.bytes[range]
437    }
438}
439impl IndexMut<RangeTo<usize>> for ByteArray {
440    fn index_mut(&mut self, range: RangeTo<usize>) -> &mut Self::Output {
441        &mut self.bytes[range]
442    }
443}
444impl IndexMut<RangeInclusive<usize>> for ByteArray {
445    fn index_mut(&mut self, range: RangeInclusive<usize>) -> &mut Self::Output {
446        &mut self.bytes[range] // [2..=5]
447    }
448}
449impl IndexMut<RangeFull> for ByteArray {
450    fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output {
451        &mut self.bytes[range] // [..]
452    }
453}
454
455impl AsRef<[u8]> for ByteArray {
456    fn as_ref(&self) -> &[u8] {
457        self.as_bytes()
458    }
459}
460
461impl AsMut<[u8]> for ByteArray {
462    fn as_mut(&mut self) -> &mut [u8] {
463        &mut self.bytes
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470    use crate::try_hex;
471    use alloc::vec;
472    use core::str::FromStr;
473
474    fn is_normal<T: Sized + Send + Sync + Unpin>() {}
475
476    #[test]
477    fn test_thread_safety_autotraits() {
478        is_normal::<ByteArray>();
479    }
480
481    #[test]
482    fn test_hex_string_constructor() {
483        let b1: ByteArray = "0xfe81eabd5".parse().unwrap();
484
485        assert_eq!(b1.len(), 5);
486        assert_eq!(b1.bytes, vec![0x0f, 0xe8, 0x1e, 0xab, 0xd5]);
487    }
488
489    #[test]
490    fn test_ba_with_capacity() {
491        let arr = ByteArray::with_capacity(10);
492
493        assert_eq!(arr.bytes.capacity(), 10);
494        assert!(ByteArray::try_from(arr).is_ok());
495    }
496
497    #[test]
498    fn test_with_hex() {
499        let arr = ByteArray::from_hex("ffabc");
500
501        assert_eq!(arr.unwrap().bytes, vec![0x0f, 0xfa, 0xbc]);
502    }
503
504    #[test]
505    fn test_with_bin_less_than_8() {
506        let arr = ByteArray::from_bin("1101111").unwrap();
507        assert_eq!(arr.bytes, vec![0x6f]);
508    }
509
510    #[test]
511    fn test_with_bin_more_than_8() {
512        let arr = ByteArray::from_bin("110111010110111").unwrap();
513        assert_eq!(arr.bytes, vec![0x6e, 0xb7]);
514    }
515
516    #[test]
517    fn test_equality_derives() {
518        let arr: ByteArray = "0xffab12345ffaf".parse().unwrap();
519        let arr_2 = ByteArray::from_hex("ffab12345ffafdeadbeef").unwrap();
520        let arr_3 = arr.clone();
521
522        assert_ne!(arr.bytes, arr_2.bytes);
523        assert_eq!(arr.bytes, arr_3.bytes);
524    }
525
526    #[test]
527    fn test_init_zeros() {
528        let arr = ByteArray::init_zeros(8);
529
530        assert_eq!(arr.bytes, [0u8, 0, 0, 0, 0, 0, 0, 0]);
531    }
532
533    #[test]
534    fn test_init_value() {
535        let arr = ByteArray::init_value(254, 8);
536
537        assert_eq!(arr.bytes, [254, 254, 254, 254, 254, 254, 254, 254]);
538    }
539
540    #[test]
541    fn test_mutable_idx_accessor() {
542        let mut arr: ByteArray = "0xffab1245".parse().unwrap();
543
544        arr[3] = 0xbe;
545        arr[1] = 0xfa;
546
547        assert_eq!(arr.bytes, ByteArray::from_str("0xfffa12be").unwrap().bytes);
548    }
549
550    #[test]
551    #[allow(deprecated)]
552    fn test_fill_zeros() {
553        let arr = ByteArray::default().fill_zeros();
554        assert!(arr.as_bytes().is_empty());
555
556        let arr = ByteArray::with_capacity(5).fill_zeros();
557        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0]);
558
559        let arr = ByteArray::with_capacity(7).fill_zeros();
560
561        assert_eq!(arr.as_bytes(), [0u8, 0, 0, 0, 0, 0, 0])
562    }
563
564    #[test]
565    #[allow(deprecated)]
566    fn test_fill_with() {
567        let arr = ByteArray::default().fill_zeros();
568        assert!(arr.as_bytes().is_empty());
569
570        let arr = ByteArray::with_capacity(5).fill_with(126);
571        assert_eq!(arr.as_bytes(), [126u8, 126, 126, 126, 126]);
572
573        let arr = ByteArray::with_capacity(7).fill_with(5);
574
575        assert_eq!(arr.as_bytes(), [5u8, 5, 5, 5, 5, 5, 5]);
576    }
577
578    #[test]
579    #[cfg(feature = "experimental")]
580    fn test_push_element() {
581        let mut arr: ByteArray = vec![1, 2].into();
582
583        arr.try_push(0xab);
584
585        assert_eq!(arr.as_bytes(), [1, 2, 0xab]);
586    }
587
588    #[test]
589    fn test_hex_with_underscores() {
590        let arr = ByteArray::from_hex("de_ad_be_ef").unwrap();
591        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
592    }
593
594    #[test]
595    fn test_bin_with_underscores() {
596        let arr = ByteArray::from_bin("1010_0101").unwrap();
597        assert_eq!(arr.as_bytes(), [0xa5]);
598    }
599
600    #[test]
601    fn test_bin_with_underscores_odd_length() {
602        let arr = ByteArray::from_bin("110_1111").unwrap();
603        assert_eq!(arr.as_bytes(), [0x6f]);
604    }
605
606    #[test]
607    #[should_panic]
608    fn test_as_mut_empty() {
609        let mut bytes = ByteArray::default();
610
611        let mut_ref = bytes.as_mut();
612
613        mut_ref[0] = 0x00;
614    }
615
616    #[test]
617    fn test_ranges() {
618        let bytes = try_hex!("ff_aa_fa_b8_ca_12_15_5a_5c_6f").unwrap();
619
620        assert_eq!(bytes[1..2], [0xaa]);
621        assert_eq!(bytes[0..=2], [0xff, 0xaa, 0xfa]);
622        assert_eq!(bytes[6..], [0x15, 0x5a, 0x5c, 0x6f]);
623        assert_eq!(bytes[..3], [0xff, 0xaa, 0xfa]);
624        assert_eq!(
625            bytes[..],
626            [0xff, 0xaa, 0xfa, 0xb8, 0xca, 0x12, 0x15, 0x5a, 0x5c, 0x6f]
627        );
628    }
629
630    #[test]
631    fn test_ranges_mut() {
632        let mut bytes = try_hex!("ff_aa_fa_b8_ca_12_15_5a_5c_6f").unwrap();
633
634        let slice = &mut bytes[1..3];
635        slice[0] = 0x00;
636        slice[1] = 0x01;
637        let expected: ByteArray = "0xff_00_01_b8_ca_12_15_5a_5c_6f".parse().unwrap();
638
639        assert_eq!(bytes, expected);
640
641        let slice = &mut bytes[0..=2];
642        slice[0] = 0x12;
643        slice[1] = 0xab;
644        slice[2] = 0xbc;
645
646        let expected: ByteArray = "0x12_ab_bc_b8_ca_12_15_5a_5c_6f".parse().unwrap();
647
648        assert_eq!(bytes, expected);
649
650        let slice = &mut bytes[6..];
651        slice[0] = 0x01;
652        slice[1] = 0x02;
653        slice[2] = 0x03;
654        slice[3] = 0x04;
655
656        let expected: ByteArray = "0x12_ab_bc_b8_ca_12_01_02_03_04".parse().unwrap();
657
658        assert_eq!(bytes, expected);
659
660        let slice = &mut bytes[..3];
661        slice[0] = 0x00;
662        slice[1] = 0x02;
663        slice[2] = 0x03;
664
665        let expected: ByteArray = "0x00_02_03_b8_ca_12_01_02_03_04".parse().unwrap();
666
667        assert_eq!(bytes, expected);
668
669        let slice = &mut bytes[..];
670
671        slice.iter_mut().for_each(|e| *e = 0x01);
672
673        let expected: ByteArray = "0x01_01_01_01_01_01_01_01_01_01".parse().unwrap();
674
675        assert_eq!(bytes, expected);
676    }
677
678    #[test]
679    fn test_as_ref() {
680        let bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
681        let bytes_ref = bytes.as_ref();
682
683        assert_eq!(bytes.as_bytes(), bytes_ref);
684    }
685
686    #[test]
687    fn test_as_mut() {
688        let mut bytes: ByteArray = "0x01_02_03_04_05_06_07_08".parse().unwrap();
689
690        let mut_ref = bytes.as_mut();
691
692        mut_ref[0] = 0xFF;
693        mut_ref[5] = 0xab;
694        mut_ref[7] = 0x1a;
695
696        assert_eq!(
697            bytes.as_bytes(),
698            [0xff, 0x02, 0x03, 0x04, 0x05, 0xab, 0x07, 0x1a]
699        )
700    }
701
702    #[test]
703    fn test_concat_create_from() {
704        // test empty concat both
705
706        let bytes_first = ByteArray::default();
707        let bytes_second = ByteArray::default();
708
709        //let concat = ByteArray::from([bytes_first,bytes_second]);
710        // test empty concat left
711
712        // test empty concat right
713
714        //test normal concat
715    }
716
717    #[test]
718    fn test_concat_create_chained() {}
719
720    // try_extend edge case tests
721
722    #[test]
723    fn test_try_extend_both_empty() {
724        let arr1 = ByteArray::default();
725        let arr2 = ByteArray::default();
726        let result = arr1.try_extend(arr2).unwrap();
727        assert_eq!(result.len(), 0);
728    }
729
730    #[test]
731    fn test_try_extend_first_empty() {
732        let arr1 = ByteArray::default();
733        let arr2 = ByteArray::from_hex("beef").unwrap();
734        let result = arr1.try_extend(arr2).unwrap();
735        assert_eq!(result.as_bytes(), [0xbe, 0xef]);
736    }
737
738    #[test]
739    fn test_try_extend_second_empty() {
740        let arr1 = ByteArray::from_hex("dead").unwrap();
741        let arr2 = ByteArray::default();
742        let result = arr1.try_extend(arr2).unwrap();
743        assert_eq!(result.as_bytes(), [0xde, 0xad]);
744    }
745
746    #[test]
747    fn test_try_extend_small_arrays() {
748        let arr1 = ByteArray::from_hex("aa").unwrap();
749        let arr2 = ByteArray::from_hex("bb").unwrap();
750        let result = arr1.try_extend(arr2).unwrap();
751        assert_eq!(result.as_bytes(), [0xaa, 0xbb]);
752        assert_eq!(result.len(), 2);
753    }
754
755    #[test]
756    fn test_try_extend_preserve_cap_empty_with_capacity() {
757        let arr1 = ByteArray::with_capacity(50); // capacity 50, length 0
758        let arr2 = ByteArray::with_capacity(30); // capacity 30, length 0
759        let result = arr1.try_extend_with_preserve_cap(arr2).unwrap();
760
761        assert_eq!(result.len(), 0);
762        assert_eq!(result.bytes.capacity(), 90); // 50 + 30 + 10 safety
763    }
764
765    #[test]
766    fn test_try_extend_capacity_based_on_length() {
767        let arr1 = ByteArray::from_hex("aa").unwrap(); // length = 1
768        let arr2 = ByteArray::from_hex("bb").unwrap(); // length = 1
769        let result = arr1.try_extend(arr2).unwrap();
770
771        // Capacity should be based on lengths: 1 + 1 + 10 = 12
772        assert_eq!(result.bytes.capacity(), 12);
773        assert_eq!(result.as_bytes(), [0xaa, 0xbb]);
774    }
775
776    #[test]
777    fn test_try_extend_chaining() {
778        let arr1 = ByteArray::from_hex("aa").unwrap();
779        let arr2 = ByteArray::from_hex("bb").unwrap();
780        let arr3 = ByteArray::from_hex("cc").unwrap();
781
782        let result = arr1.try_extend(arr2).unwrap().try_extend(arr3).unwrap();
783        assert_eq!(result.as_bytes(), [0xaa, 0xbb, 0xcc]);
784    }
785}