byte_array_ops/
type_conv.rs

1//! This module contains all type conversion definitions to and from [`ByteArray`]
2
3use crate::errors::ByteArrayError;
4use crate::model::ByteArray;
5use alloc::vec;
6use alloc::vec::Vec;
7use core::str::FromStr;
8
9impl<const N: usize> From<&[u8; N]> for ByteArray {
10    /// converts a byte slice into our Byte array, odd word padding is not needed here as arrays are always aligned
11    /// TODO document this as potentially expensive copy
12    fn from(value: &[u8; N]) -> Self {
13        ByteArray::from(&value[..])
14    }
15}
16
17impl From<&[u8]> for ByteArray {
18    /// converts a byte slice into our Byte array, odd word padding is not needed here as arrays are always aligned
19    /// TODO document this as potentially expensive copy
20    fn from(value: &[u8]) -> Self {
21        ByteArray {
22            bytes: value.to_vec(),
23        }
24    }
25}
26
27impl From<Vec<u8>> for ByteArray {
28    fn from(bytes: Vec<u8>) -> Self {
29        ByteArray {
30            bytes, // Zero-cost move
31        }
32    }
33}
34
35impl From<u8> for ByteArray {
36    fn from(value: u8) -> Self {
37        ByteArray { bytes: vec![value] }
38    }
39}
40
41impl FromStr for ByteArray {
42    type Err = ByteArrayError;
43    /// parses a [`&str`] as follows :
44    ///  - does it start with a format identifier with `0x`, `0b`, or `0o` then parses in hex, binary or octal respectively (case insensitive)
45    ///     - The routines for the conversion here ignore underscore `_` allowing its use as a separator for example `"0b1110_0010_0110"`
46    ///  - for all other cases defaults to `UTF-8` as encoding and parses the slice as such
47    ///
48    /// TODO add example with string slice lower than 2 chars
49    ///
50    ///
51    fn from_str(s: &str) -> Result<Self, Self::Err> {
52        if s.is_empty() {
53            return Err(ByteArrayError::EmptyInput);
54        }
55
56        if s.starts_with("0x") || s.starts_with("0X") {
57            return ByteArray::from_hex(&s[2..]);
58        }
59
60        if s.starts_with("0b") || s.starts_with("0B") {
61            return ByteArray::from_bin(&s[2..]);
62        }
63
64        if s.starts_with("0o") || s.starts_with("0O") {
65            unimplemented!()
66        }
67
68        // It's a regular UTF-8 string
69        Ok(ByteArray::from(s.as_bytes()))
70    }
71}
72
73impl<const N: usize> From<[u8; N]> for ByteArray {
74    fn from(value: [u8; N]) -> Self {
75        (&value).into()
76    }
77}
78
79impl ByteArray {
80    #[allow(unsafe_code)]
81    /// raw pointer access for cases where the data is passed over an ffi interface and needs
82    /// to be read
83    ///
84    /// # Safety
85    /// > Do `NOT` use only if you must. use [`ByteArray::as_bytes`] or [`ByteArray::as_ref`] as a safe alternative
86    ///
87    /// This function returns the raw pointer to the inner type as is and avoids any checks it is thus
88    /// marked unsafe
89    pub unsafe fn as_ptr(&self) -> *const u8 {
90        self.bytes.as_ptr()
91    }
92
93    #[allow(unsafe_code)]
94    /// raw mutable pointer access for cases where the data is passed over an ffi interface and needs
95    /// to be mutated
96    ///
97    /// # Safety
98    /// > Do `NOT` use only if you must. use [`ByteArray::as_mut`] as a safe alternative
99    ///
100    /// This function returns the raw pointer to the inner type as is and avoids any checks it is thus
101    /// marked unsafe
102    pub unsafe fn as_mut_ptr(&mut self) -> *mut u8 {
103        self.bytes.as_mut_ptr()
104    }
105}
106
107#[cfg(test)]
108#[allow(unsafe_code)]
109mod unsafe_tests {
110    use crate::try_hex;
111
112    #[test]
113    fn test_unsafe_ptr() {
114        let data = try_hex!("00fe0abcd0abcdfeab").unwrap();
115        unsafe {
116            assert_eq!(*data.as_ptr().add(1), 0xfe);
117            assert_eq!(*data.as_ptr().add(7), 0xfe);
118        }
119    }
120
121    #[test]
122    fn test_unsafe_mut_ptr() {
123        let mut data = try_hex!("cafebeef").unwrap();
124        unsafe {
125            *data.as_mut_ptr().add(3) = 0xed;
126        }
127        assert_eq!(*data.get(3).unwrap(), 0xed);
128    }
129}
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_from_slice_reference() {
136        let bytes = [0xde, 0xad, 0xbe, 0xef];
137        let bytes_slice = &bytes;
138
139        let byte_array: ByteArray = bytes_slice.into();
140
141        assert_eq!(byte_array.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
142    }
143
144    #[test]
145    fn test_from_vec() {
146        let bytes: Vec<u8> = vec![0xde, 0xad, 0xbe, 0xef];
147
148        let arr = ByteArray::from(bytes);
149
150        assert_eq!(arr.as_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
151    }
152
153    #[test]
154    fn test_from_single_byte() {
155        let arr: ByteArray = 0xff.into();
156
157        assert_eq!(arr.as_bytes(), [0xff]);
158    }
159
160    #[test]
161    fn test_from_array_literal() {
162        let arr: ByteArray = [0xaa, 0xbb, 0xcc].into();
163
164        assert_eq!(arr.as_bytes(), [0xaa, 0xbb, 0xcc]);
165    }
166
167    #[test]
168    fn test_from_array_reference() {
169        let bytes = [0x01, 0x02, 0x03, 0x04];
170        let arr: ByteArray = (&bytes).into();
171
172        assert_eq!(arr.as_bytes(), [0x01, 0x02, 0x03, 0x04]);
173    }
174
175    #[test]
176    fn test_from_empty_slice() {
177        let empty: &[u8] = &[];
178        let arr: ByteArray = empty.into();
179
180        assert!(arr.is_empty());
181    }
182
183    #[test]
184    fn test_from_empty_vec() {
185        let empty: Vec<u8> = Vec::new();
186        let arr = ByteArray::from(empty);
187
188        assert!(arr.is_empty());
189    }
190
191    #[test]
192    fn test_from_str_hex() {
193        let arr: ByteArray = "0xdeadbeef".parse().unwrap();
194
195        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
196    }
197
198    #[test]
199    fn test_from_str_hex_uppercase() {
200        let arr: ByteArray = "0XDEADBEEF".parse().unwrap();
201
202        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
203    }
204
205    #[test]
206    fn test_from_str_hex_mixed_case() {
207        let arr: ByteArray = "0xDeAdBeEf".parse().unwrap();
208
209        assert_eq!(arr.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
210    }
211
212    #[test]
213    fn test_from_str_hex_odd_length() {
214        let arr: ByteArray = "0xfff".parse().unwrap();
215
216        assert_eq!(arr.as_bytes(), [0x0f, 0xff]);
217    }
218
219    #[test]
220    fn test_from_str_binary() {
221        let arr: ByteArray = "0b11011110".parse().unwrap();
222
223        assert_eq!(arr.as_bytes(), [0xde]);
224    }
225
226    #[test]
227    fn test_from_str_binary_uppercase() {
228        let arr: ByteArray = "0B11011110".parse().unwrap();
229
230        assert_eq!(arr.as_bytes(), [0xde]);
231    }
232
233    #[test]
234    fn test_from_str_utf8() {
235        let arr: ByteArray = "hello".parse().unwrap();
236
237        assert_eq!(arr.as_bytes(), b"hello");
238    }
239
240    #[test]
241    fn test_from_str_utf8_single_char() {
242        let arr: ByteArray = "A".parse().unwrap();
243
244        assert_eq!(arr.as_bytes(), b"A");
245    }
246
247    #[test]
248    fn test_from_str_empty_error() {
249        let result: Result<ByteArray, ByteArrayError> = "".parse();
250
251        assert!(result.is_err());
252        if let Err(e) = result {
253            assert!(matches!(e, ByteArrayError::EmptyInput));
254        }
255    }
256
257    #[test]
258    fn test_from_str_hex_invalid_char() {
259        let result: Result<ByteArray, ByteArrayError> = "0xgggg".parse();
260
261        assert!(result.is_err());
262    }
263
264    #[test]
265    fn test_from_str_binary_invalid_char() {
266        let result: Result<ByteArray, ByteArrayError> = "0b12345".parse();
267
268        assert!(result.is_err());
269    }
270
271    #[test]
272    fn test_from_large_array() {
273        let large: Vec<u8> = (0..1000).map(|i| (i % 256) as u8).collect();
274        let arr = ByteArray::from(large.clone());
275
276        assert_eq!(arr.len(), 1000);
277        assert_eq!(arr.as_bytes(), large.as_slice());
278    }
279}