amebazii/types/mod.rs
1use std::io;
2
3use crate::error::Error;
4
5pub mod enums;
6pub use enums::*; // revisit
7
8pub mod flash;
9pub use flash::{Flash, Partition};
10
11pub mod fst;
12pub use fst::FST;
13
14pub mod header;
15pub use header::{EntryHeader, ImageHeader, KeyBlock, SectionHeader};
16
17pub mod image;
18pub use image::*; // revisit
19
20pub mod section;
21pub use section::Section;
22
23pub mod nvdm;
24pub use nvdm::*;
25
26pub mod sysctrl;
27pub use sysctrl::{FlashInfo, ForceOldImage, SpiConfig, SystemData};
28
29/// `DataType` is a type alias for an optional fixed-size array of `u8` bytes.
30///
31/// This type represents an optional key where the key is an array of `u8` of a fixed size,
32/// defined by the constant generic parameter `N`. If the key is not present, the type
33/// will be `None`, otherwise, it will contain the key as an array of bytes.
34///
35/// The fixed size of the key is determined at compile time by the `N` constant, allowing
36/// different key sizes to be handled dynamically with a single type alias.
37///
38/// # Example:
39/// ```rust
40/// // DataType with a key of length 4 bytes
41/// let key: DataType<4> = Some([1, 2, 3, 4]);
42/// ```
43///
44/// ## Fields:
45/// - `Some([u8; N])`: Contains a fixed-size array of `u8` bytes representing the key.
46/// - `None`: Indicates that the key is not present.
47///
48/// # Type Parameters:
49/// - `N`: The fixed length of the key array (i.e., the number of `u8` bytes in the key).
50///
51/// # Traits Implemented:
52/// - Implements `Option`, meaning it can be `Some` containing a key or `None` for a missing key.
53pub type DataType<const N: usize> = Option<[u8; N]>;
54
55/// Converts a hexadecimal string into a `DataType` array.
56///
57/// This function takes a hexadecimal string (`hexstr`), decodes it into bytes,
58/// and then attempts to convert the bytes into a `DataType` of a specific size.
59///
60/// The size of the resulting `DataType` is determined by the constant `N`. This function
61/// will panic if the length of the decoded byte array does not match the expected size `N`.
62///
63/// # Type Parameters:
64/// - `N`: The size of the `DataType` array. This is a constant array length that the decoded
65/// hexadecimal string must match. It is passed at compile-time to ensure type safety.
66///
67/// # Arguments:
68/// - `hexstr`: A string containing the hexadecimal representation of the key. The string must
69/// contain an even number of characters (each representing a byte).
70///
71/// # Returns:
72/// - `Some(DataType<N>)`: A `DataType` array of size `N`, constructed from the decoded bytes.
73/// Returns `None` if the hexadecimal string is empty or the decoded bytes do not match the expected length.
74///
75/// # Panics:
76/// - This function will panic if the length of the decoded byte array is not equal to `N`.
77///
78/// # Example:
79/// ```
80/// let hexstr = "a1b2c3d4e5f67890";
81/// let key = key_from_hex::<8>(hexstr);
82/// assert_eq!(key, Some([0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x78, 0x90]));
83/// ```
84pub fn key_from_hex<const N: usize>(hexstr: &str) -> DataType<N> {
85 let bytes = hex::decode(hexstr).unwrap();
86 assert!(bytes.len() == N);
87 Some(bytes.try_into().unwrap())
88}
89
90/// Converts a `DataType` array into a hexadecimal string.
91///
92/// This function takes a `DataType` array (`key`) and converts it into its corresponding
93/// hexadecimal string representation. If the key is `None`, it returns `None`.
94///
95/// # Type Parameters:
96/// - `N`: The size of the `DataType` array. This is a constant array length that ensures type safety.
97///
98/// # Arguments:
99/// - `key`: A reference to a `DataType` array of size `N`. This is the key to be encoded into a
100/// hexadecimal string. It must be a valid key array of the appropriate size.
101///
102/// # Returns:
103/// - `Some(String)`: The hexadecimal string representation of the key if the key is `Some`.
104/// Returns `None` if the key is `None`.
105///
106/// # Example:
107/// ```
108/// let key = Some([0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x78, 0x90]);
109/// let hexstr = key_to_hex(key);
110/// assert_eq!(hexstr, Some("a1b2c3d4e5f67890".to_string()));
111/// ```
112pub fn key_to_hex<const N: usize>(key: DataRefType<N>) -> Option<String> {
113 match key {
114 None => None, // If the key is None, return None.
115 Some(key) => Some(hex::encode(key)), // Otherwise, convert the key to a hex string and return.
116 }
117}
118
119/// `DataRefType` is a type alias for an optional reference to a fixed-size array of `u8` bytes.
120///
121/// This type is similar to `DataType`, but instead of owning the key, it holds a reference
122/// to a fixed-size array of `u8` bytes, which is useful when the key data is borrowed
123/// rather than owned. It is also parameterized by a constant generic `N`, allowing different
124/// sizes of keys to be used with a single type.
125///
126/// `DataRefType` is typically used when the key is stored elsewhere in memory, and you want
127/// to reference it without copying or taking ownership of the data. This is useful for
128/// scenarios where the key data already exists and you need to work with it without
129/// transferring ownership.
130///
131/// # Example:
132/// ```rust
133/// let key_ref: DataRefType<4> = Some(&[1, 2, 3, 4]);
134/// ```
135///
136/// ## Fields:
137/// - `Some(&[u8; N])`: A reference to a fixed-size array of `u8` bytes representing the key.
138/// - `None`: Indicates that the key is not present.
139///
140/// # Type Parameters:
141/// - `N`: The fixed length of the key array (i.e., the number of `u8` bytes in the key).
142///
143/// # Traits Implemented:
144/// - Implements `Option`, meaning it can be `Some` containing a reference to a key or `None` for a missing key
145pub type DataRefType<'a, const N: usize> = Option<&'a [u8; N]>;
146
147/// Checks if the given data is valid by ensuring none of the bytes are equal to `0xFF`.
148///
149/// This macro checks if all elements in the provided data are non-`0xFF`.
150///
151/// # Parameters
152/// - `$key`: The key or data collection to check, which must be an iterable type (e.g., a slice or array).
153///
154/// # Returns
155/// - `true` if no byte in the data is `0xFF`.
156/// - `false` if any byte in the data is `0xFF`.
157///
158/// # Example
159/// ```rust
160/// let key = [0x01, 0x02, 0x03, 0x04, 0x05];
161/// assert!(is_valid_data!(key)); // All bytes are non-0xFF, so it's valid.
162///
163/// let invalid_key = [0xFF, 0xFF, 0xFF, 0xFF];
164/// assert!(!is_valid_data!(invalid_key)); // Contains only 0xFF bytes, so it's invalid.
165/// ```
166#[macro_export]
167macro_rules! is_valid_data {
168 ($key:expr) => {
169 $key.iter().any(|&x| x != 0xFF)
170 };
171}
172
173/// Reads valid data from the reader into the target, ensuring that the data does not contain any `0xFF` bytes.
174///
175/// This macro attempts to read a specific amount of data from a reader, checks if the data is valid
176/// (i.e., it does not contain any `0xFF` bytes), and if valid, assigns the data to the provided target.
177///
178/// # Parameters
179/// - `$target`: The target variable where the data will be stored (of type `Option<[u8; $length]>`).
180/// - `$length`: The length of the data to read (must match the expected size of the data).
181/// - `$reader`: The reader from which the data will be read. The reader must implement the `Read` trait.
182///
183/// # Example
184/// ```rust
185/// let mut reader: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x05];
186/// let mut target: Option<[u8; 5]> = None;
187/// read_valid_data!(target, 5, reader);
188/// assert!(target.is_some()); // The data read is valid, so target should be Some([0x01, 0x02, 0x03, 0x04, 0x05]).
189/// ```
190///
191/// # Error Handling
192/// - If the data contains any `0xFF` byte, it will not be assigned to the target.
193/// - The macro expects the reader to support reading the exact number of bytes as specified by `$length`.
194/// - This macro will return an error if the reader cannot fulfill the request.
195#[macro_export]
196macro_rules! read_valid_data {
197 ($target:expr, $length:expr, $reader:expr) => {
198 let mut buf = [0u8; $length];
199 $reader.read_exact(&mut buf)?;
200 if is_valid_data!(buf) {
201 $target = Some(buf);
202 }
203 };
204}
205
206/// `write_padding!` - A macro to write padding bytes to a writer.
207///
208/// This macro writes a series of padding bytes (either filled with `0xFF` or a custom byte)
209/// to a writer, ensuring that the stream is correctly aligned or that the desired padding size
210/// is achieved. It provides two variants:
211///
212/// 1. **Default fill (`0xFF`)**: The first variant writes padding filled with the byte `0xFF`.
213/// 2. **Custom fill**: The second variant allows for specifying a custom byte for padding.
214///
215/// The macro handles the error propagation automatically, returning the result of the `write_all` method.
216///
217/// # Parameters:
218/// - `$writer`: The writer to which padding bytes should be written. This must implement the
219/// `std::io::Write` trait.
220/// - `$size`: The size (in bytes) of the padding to be written.
221/// - `$fill` (optional): The byte value to fill the padding. Defaults to `0xFF` if not provided.
222///
223/// # Example 1: Default padding (filled with `0xFF`):
224/// ```rust
225/// use std::io::Cursor;
226/// let mut buffer = Cursor::new(Vec::new());
227/// write_padding!(buffer, 16); // Writes 16 bytes of `0xFF` to the buffer
228/// ```
229///
230/// # Example 2: Custom padding byte:
231/// ```rust
232/// use std::io::Cursor;
233/// let mut buffer = Cursor::new(Vec::new());
234/// write_padding!(buffer, 8, 0x00); // Writes 8 bytes of `0x00` to the buffer
235/// ```
236#[macro_export]
237macro_rules! write_padding {
238 // Variant 1: Default padding (filled with `0xFF`)
239 ($writer:expr, $size:expr) => {
240 if $size > 4096 {
241 write_fill($writer, 0xFF, $size as u64)?;
242 } else {
243 $writer.write_all(&vec![0xFF; $size as usize])?;
244 }
245 };
246
247 // Variant 2: Custom padding byte
248 ($writer:expr, $size:literal, $fill:literal) => {
249 if $size > 4096 {
250 write_fill($writer, $fill, $size as u64)?;
251 } else {
252 $writer.write_all(&vec![$fill; $size as usize])?;
253 }
254 };
255}
256
257/// Writes data to a stream.
258///
259/// This macro writes the key to the stream if it is present. If the key is `None`,
260/// the macro writes padding instead, ensuring the correct number of bytes is always written.
261///
262/// # Parameters:
263/// - `$writer`: The writer where the key (or padding) should be written. Must implement the `std::io::Write` trait.
264/// - `$key`: The key to write. This can be `Some([u8; N])` where `N` is the length of the key, or `None` to indicate that the key is missing.
265/// - `$len`: The length of the key (or the padding) in bytes. This will determine how many bytes to write if the key is absent.
266///
267/// # Example:
268/// ```rust
269/// let mut buffer = Vec::new();
270/// let key: Option<[u8; 10]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
271/// write_data!(buffer, key, 10);
272/// ```
273#[macro_export]
274macro_rules! write_data {
275 // Case when the key is present: writes the key to the writer
276 ($writer:expr, $key:expr, $len:literal) => {
277 if let Some(key) = &$key {
278 $writer.write_all(key)?;
279 } else {
280 // If the key is None, write padding instead
281 write_padding!($writer, $len);
282 }
283 };
284}
285
286/// Writes padding to a binary stream to ensure that the next write operation aligns to a specified size.
287///
288/// This macro writes padding bytes to the stream in order to align the current write position to the specified size.
289/// The padding is done with a specified fill byte and can optionally be skipped if the alignment is already met.
290///
291/// The macro can be used in different forms depending on whether you need to specify a fill byte and whether the padding
292/// is optional. The following variants are available:
293///
294/// 1. **Default Padding with 0x00 Fill (non-optional):**
295/// Aligns the current stream position to the next boundary of the specified size and fills with `0x00`.
296///
297/// ```rust
298/// write_aligned!(writer, 16);
299/// ```
300/// This will ensure the stream is aligned to a 16-byte boundary, and `0x00` is used for padding.
301///
302/// 2. **Default Padding with Custom Fill (non-optional):**
303/// Aligns the current stream position to the next boundary of the specified size and fills with a custom byte value.
304///
305/// ```rust
306/// write_aligned!(writer, 16, 0xFF);
307/// ```
308/// This will align to a 16-byte boundary and use `0xFF` as the padding byte.
309///
310/// 3. **Optional Padding with 0x00 Fill:**
311/// Optionally applies padding if necessary to align the stream position to the specified size. If the stream is already
312/// aligned, no padding is written.
313///
314/// ```rust
315/// write_aligned!(writer, 16, optional);
316/// ```
317/// This will only write padding if needed to align to a 16-byte boundary and will use `0x00` as the fill byte.
318///
319/// 4. **Optional Padding with Custom Fill:**
320/// Optionally applies padding with a custom fill byte if the stream is not already aligned to the specified size.
321///
322/// ```rust
323/// write_aligned!(writer, 16, 0xFF, optional);
324/// ```
325/// This will apply padding with `0xFF` only if needed to align to a 16-byte boundary.
326
327#[macro_export]
328macro_rules! write_aligned {
329 // 1. Default padding with 0x00 fill and optional padding
330 ($writer:expr, $size:expr, optional) => {
331 write_aligned!($writer, $size, 0x00, optional);
332 };
333
334 // 2. Default padding with 0x00 fill (non-optional)
335 ($writer:expr, $size:expr) => {
336 write_aligned!($writer, $size, 0x00);
337 };
338
339 // 3. Custom padding with optional fill byte
340 ($writer:expr, $size:expr, $fill:expr, optional) => {
341 let pos = $writer.stream_position()?;
342 let padding = (pos % $size);
343 if padding > 0 {
344 if padding > 4096 {
345 write_fill($writer, $fill, ($size - padding) as u64)?;
346 } else {
347 $writer.write_all(&vec![$fill; ($size - padding) as usize])?;
348 }
349 }
350 };
351
352 // 4. Custom padding with specified fill byte (non-optional)
353 ($writer:expr, $size:expr, $fill:expr) => {
354 let pos = $writer.stream_position()?;
355 let padding = $size - (pos % $size);
356 if padding > 4096 {
357 write_fill($writer, $fill, padding as u64)?;
358 } else {
359 $writer.write_all(&vec![$fill; padding as usize])?;
360 }
361 };
362}
363
364/// Read (skip) padding bytes in a reader.
365///
366/// This macro skips a specified number of bytes in the stream, commonly used when there is
367/// padding between fields in a binary format.
368///
369/// # Parameters:
370/// - `$reader`: The reader to skip the padding in. This must implement the `std::io::Read`
371/// and `std::io::Seek` traits.
372/// - `$size`: The number of bytes to skip. This will be passed to the `seek` function in the
373/// form of `SeekFrom::Current`.
374///
375/// # Example:
376/// ```rust
377/// use std::io::Cursor;
378/// let mut buffer = Cursor::new(Vec::new());
379/// read_padding!(buffer, 16); // Skips 16 bytes in the buffer
380/// ```
381#[macro_export]
382macro_rules! read_padding {
383 // Variant to skip a number of bytes by using SeekFrom::Current
384 ($reader:expr, $size:expr) => {
385 $reader.seek(io::SeekFrom::Current(($size) as i64))?;
386 };
387}
388
389/// A trait for types that can be deserialized from a stream.
390///
391/// This trait provides a method `read_from` that allows a type to implement how it reads
392/// data from a stream. Types that implement this trait can be read from a reader (e.g., a
393/// file or buffer) using the `from_stream` function.
394///
395/// # Method
396/// - `read_from`: Reads data from a stream into the implementing type.
397///
398/// # Example
399/// ```
400/// use amebazii:types::FromStream;
401/// struct MyStruct {
402/// field1: u32,
403/// field2: String,
404/// }
405///
406/// impl FromStream for MyStruct {
407/// fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
408/// where
409/// R: std::io::Read + std::io::Seek,
410/// {
411/// // Implement logic to read from the reader and populate `self`
412/// Ok(())
413/// }
414/// }
415/// ```
416pub trait FromStream {
417 /// Reads data from a stream and populates the fields of the type.
418 ///
419 /// This method is called by the `from_stream` function to read data from a provided
420 /// reader and deserialize it into the implementing type.
421 ///
422 /// # Parameters
423 /// - `reader`: A mutable reference to the reader from which the data will be read.
424 ///
425 /// # Returns
426 /// - `Ok(())`: If the data is successfully read and the type is populated.
427 /// - `Err(Error)`: If an error occurs while reading from the stream.
428 ///
429 /// # Example
430 /// ```rust
431 /// use amebazii:types::FromStream;
432 ///
433 /// let mut reader = std::io::Cursor::new(vec![1, 2, 3, 4]);
434 /// let mut my_struct = MyStruct::default();
435 /// my_struct.read_from(&mut reader).unwrap();
436 /// ```
437 fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
438 where
439 R: io::Read + io::Seek;
440}
441
442/// Reads a type from a stream.
443///
444/// This function attempts to read a value of type `T` from a reader that implements both the
445/// `io::Read` and `io::Seek` traits. The type `T` must implement the `FromStream` trait to
446/// define how it can be read from the stream, and it must also implement `Default` to create
447/// an instance to populate.
448///
449/// # Parameters
450/// - `reader`: A mutable reference to the reader from which data will be read.
451///
452/// # Returns
453/// - `Ok(T)`: The deserialized value of type `T`.
454/// - `Err(Error)`: If an error occurs while reading from the stream.
455///
456/// # Example
457/// ```rust
458/// use amebazii::types::{from_stream, FromStream, Error};
459///
460/// let mut reader = std::io::Cursor::new(vec![1, 2, 3, 4]);
461/// let my_struct: MyStruct = from_stream(&mut reader).unwrap();
462/// ```
463pub fn from_stream<R, T>(reader: &mut R) -> Result<T, Error>
464where
465 R: io::Read + io::Seek,
466 T: FromStream + Default,
467{
468 let mut obj = T::default();
469 obj.read_from(reader)?;
470 Ok(obj)
471}
472
473/// A trait for types that can provide their binary size.
474///
475/// This trait allows types to specify the size, in bytes, of their serialized binary representation.
476/// Types that implement this trait must define the `binary_size` method to return the size of the type's binary form.
477///
478/// # Example
479/// ```rust
480/// use amebazii::types::BinarySize;
481/// struct MyStruct {
482/// field1: u32,
483/// field2: String,
484/// }
485///
486/// impl BinarySize for MyStruct {
487/// fn binary_size() -> usize {
488/// // Return the size of `MyStruct`'s binary representation
489/// std::mem::size_of::<u32>() + field2.len()
490/// }
491/// }
492/// ```
493pub trait BinarySize {
494 /// Returns the binary size of the type in bytes.
495 ///
496 /// # Returns
497 /// - `usize`: The number of bytes required to serialize the type.
498 ///
499 fn binary_size() -> usize;
500}
501
502/// A trait for types that can be serialized to a stream.
503///
504/// This trait defines the `write_to` method, which allows a type to be serialized (written) to a stream, such as a file or buffer.
505///
506/// # Example
507/// ```rust
508/// use amebazii::types::{ToStream, Error};
509/// struct MyStruct {
510/// field1: u32,
511/// field2: String,
512/// }
513///
514/// impl ToStream for MyStruct {
515/// fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
516/// where
517/// W: std::io::Write + std::io::Seek,
518/// {
519/// // Implement the logic to write the struct's fields to the stream
520/// Ok(())
521/// }
522/// }
523/// ```
524pub trait ToStream {
525 /// Writes the type's data to a stream.
526 ///
527 /// This method serializes the implementing type into a provided stream (writer). It is used by the `transfer_to`
528 /// and `to_bytes` functions to write the data to various output formats.
529 ///
530 /// # Parameters
531 /// - `writer`: A mutable reference to a writer that implements `io::Write` and `io::Seek`.
532 ///
533 /// # Returns
534 /// - `Ok(())`: If the data is successfully written to the stream.
535 /// - `Err(Error)`: If an error occurs while writing to the stream.
536 ///
537 fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
538 where
539 W: io::Write + io::Seek;
540}
541
542/// Transfers a type's data to a stream.
543///
544/// This function serializes the data of the given object (`obj`) and writes it to the provided writer. The object
545/// must implement the `ToStream` trait, which specifies how to serialize the data to the stream.
546///
547/// # Parameters
548/// - `obj`: A reference to the object that will be written to the stream.
549/// - `writer`: A mutable reference to the writer that will receive the serialized data.
550///
551/// # Returns
552/// - `Ok(())`: If the object was successfully written to the stream.
553/// - `Err(Error)`: If an error occurred while writing the object to the stream.
554///
555/// # Example
556/// ```rust
557/// use amebazii::types::{transfer_to, ToStream};
558///
559/// let my_struct = MyStruct { field1: 42, field2: String::from("Hello") };
560/// let mut buf = Vec::new();
561/// transfer_to(&my_struct, &mut buf).unwrap();
562/// ```
563pub fn transfer_to<W, T>(obj: &T, writer: &mut W) -> Result<(), Error>
564where
565 W: io::Write + io::Seek,
566 T: ToStream,
567{
568 obj.write_to(writer)
569}
570
571/// Serializes an object into a vector of bytes.
572///
573/// This function serializes the object (`obj`) into a `Vec<u8>`.
574///
575/// # Parameters
576/// - `obj`: A reference to the object to be serialized.
577///
578/// # Returns
579/// - `Ok(Vec<u8>)`: A vector of bytes representing the serialized object.
580/// - `Err(Error)`: If an error occurs while writing the object to the byte vector.
581///
582/// # Example
583/// ```rust
584/// use amebazii::types::{to_bytes, ToStream};
585///
586/// let my_struct = MyStruct { field1: 42, field2: String::from("Hello") };
587/// let bytes = to_bytes(&my_struct).unwrap();
588/// ```
589pub fn to_bytes<T>(obj: &T) -> Result<Vec<u8>, Error>
590where
591 T: ToStream,
592{
593 let mut buf = Vec::new();
594 let mut cursor = io::Cursor::new(&mut buf);
595 obj.write_to(&mut cursor)?;
596 Ok(buf)
597}
598
599/// Serializes an object into a vector of bytes with an optimized capacity.
600///
601/// This function serializes the object (`obj`) into a `Vec<u8>`, ensuring that the vector is allocated with
602/// the minimum required capacity to hold the serialized data. The object must implement both `ToStream` and
603/// `BinarySize` to allow calculating the exact binary size beforehand.
604///
605/// # Parameters
606/// - `obj`: A reference to the object to be serialized.
607///
608/// # Returns
609/// - `Ok(Vec<u8>)`: A vector of bytes with the minimum required capacity for the serialized object.
610/// - `Err(Error)`: If an error occurs while writing the object to the byte vector.
611pub fn to_bytes_with_capacity<T>(obj: &T) -> Result<Vec<u8>, Error>
612where
613 T: ToStream + BinarySize,
614{
615 let mut buf = Vec::with_capacity(T::binary_size());
616 let mut cursor = io::Cursor::new(&mut buf);
617 obj.write_to(&mut cursor)?;
618 Ok(buf)
619}