feagi_data_serialization/
feagi_byte_container.rs

1use byteorder::{ByteOrder, LittleEndian};
2use feagi_data_structures::FeagiDataError;
3use crate::feagi_serializable::FeagiSerializable;
4use crate::FeagiByteStructureType;
5
6const MAX_NUMBER_OF_STRUCTS: usize = u8::MAX as usize;
7
8type StructureIndex = u8;
9type ByteIndexReadingStart = u32;
10type NumberBytesToRead = u32;
11
12//region Feagi Byte Container
13
14/// A container for serialized FEAGI data structures with efficient binary format.
15/// 
16/// `FeagiByteContainer` manages multiple serializable structures in a single byte array,
17/// providing methods to read, write, and validate the contained data. The container uses
18/// a header-based format with version control and structure indexing.
19/// 
20/// # Format
21/// - Global header: version (1 byte) + increment counter (2 bytes) + struct count (1 byte)
22/// - Per-structure headers: data length (4 bytes each)
23/// - Structure data: serialized structure bytes
24/// 
25/// # Example
26/// ```
27/// use feagi_data_serialization::FeagiByteContainer;
28/// 
29/// let mut container = FeagiByteContainer::new_empty();
30/// assert!(container.is_valid());
31/// assert_eq!(container.get_number_of_bytes_used(), 4); // Just the header
32/// ```
33#[derive(Debug, Clone)]
34pub struct FeagiByteContainer {
35    /// The actual contained byte data
36    bytes: Vec<u8>,
37    /// If the data inside the array is considered valid. If not, most functionality is disabled
38    is_data_valid: bool,
39    /// A vector of references to where in the bytes to get the slices of specific structs, and what type of Feagi data they are
40    contained_struct_references: Vec<ContainedStructReference>,
41}
42
43impl FeagiByteContainer{
44    pub const CURRENT_FBS_VERSION: u8 = 2;
45
46    pub const GLOBAL_BYTE_HEADER_BYTE_COUNT: usize = 4; // 1 u8, 1 u16, 1 u8
47
48    pub const STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE: usize = 4; // 1 u32
49
50    pub const STRUCT_HEADER_BYTE_COUNT: usize = 2; // 1 u8, 1 u8
51
52    //region Constructors
53
54    /// Creates a new empty container with default header.
55    /// 
56    /// The container starts with a 4-byte header containing version, zero increment counter,
57    /// and zero structure count. The container is initially valid with just a 4 byte header
58    /// stating 0 contained structures
59    /// 
60    /// # Example
61    /// ```
62    /// use feagi_data_serialization::FeagiByteContainer;
63    /// 
64    /// let container = FeagiByteContainer::new_empty();
65    /// assert!(container.is_valid());
66    /// assert_eq!(container.get_number_of_bytes_used(), 4);
67    /// ```
68    pub fn new_empty() -> Self {
69        Self { bytes: vec![Self::CURRENT_FBS_VERSION, 0, 0, 0], is_data_valid: true, contained_struct_references: Vec::new() }
70    }
71
72    //endregion
73
74    // region Direct Data Access
75
76    /// Returns a reference to the internal byte array.
77    /// 
78    /// Provides direct read access to the raw bytes of the container,
79    /// including headers and all serialized structure data.
80    /// 
81    /// # Example
82    /// ```
83    /// use feagi_data_serialization::FeagiByteContainer;
84    /// 
85    /// let container = FeagiByteContainer::new_empty();
86    /// let bytes = container.get_byte_ref();
87    /// assert_eq!(bytes.len(), 4);
88    /// assert_eq!(bytes[0], 2); // Current version
89    /// ```
90    pub fn get_byte_ref(&self) -> &[u8] {
91        &self.bytes
92    }
93
94    /// Writes data using a callback function and validates the container.
95    /// 
96    /// Allows external code to write directly to the byte array, then validates
97    /// that the resulting data forms a valid container structure.
98    /// 
99    /// # Example
100    /// ```
101    /// use feagi_data_serialization::{FeagiByteContainer};
102    ///
103    /// // NOTE: This function is just here as an example, but this specific implementation is invalid
104    /// let mut container = FeagiByteContainer::new_empty();
105    /// let result = container.try_write_data_to_container_and_verify(&mut |bytes| {
106    ///     *bytes = vec![20u8, 2u8, 3u8]; // This is an invalid byte sequence
107    ///     Ok(())
108    /// });
109    /// // This will fail validation since we're setting invalid data
110    /// assert!(result.is_err());
111    /// ```
112    pub fn try_write_data_to_container_and_verify<F>(&mut self, byte_writer: &mut F) -> Result<(), FeagiDataError>
113    where F: FnMut(&mut Vec<u8>) -> Result<(), FeagiDataError> {
114        byte_writer(&mut self.bytes)?;
115        self.verify_container_valid_and_populate()
116    }
117
118    //endregion
119
120    //region Get Properties
121
122    /// Checks if the container has valid data structure.
123    /// 
124    /// Returns true if the container has been validated and contains properly
125    /// formatted header and structure data.
126    /// 
127    /// # Example
128    /// ```
129    /// use feagi_data_serialization::FeagiByteContainer;
130    /// 
131    /// let container = FeagiByteContainer::new_empty();
132    /// assert!(container.is_valid());
133    /// ```
134    pub fn is_valid(&self) -> bool {
135        self.is_data_valid
136    }
137
138    /// Returns the number of structures contained in this container.
139    /// 
140    /// Only works if the container is valid. Returns an error if the container
141    /// has not been validated or contains invalid data.
142    /// 
143    /// # Example
144    /// ```
145    /// use feagi_data_serialization::FeagiByteContainer;
146    ///
147    /// let container = FeagiByteContainer::new_empty();
148    /// assert_eq!(container.try_get_number_contained_structures().unwrap(), 0);
149    /// ```
150    pub fn try_get_number_contained_structures(&self) -> Result<usize, FeagiDataError> {
151        if self.is_data_valid {
152            return Ok(self.contained_struct_references.len())
153        }
154        Err(FeagiDataError::DeserializationError("Given Byte Container is invalid and thus cannot be read!".into()))
155    }
156
157    /// Returns the total number of bytes currently used by the container.
158    /// 
159    /// This includes headers and all structure data.
160    /// 
161    /// # Example
162    /// ```
163    /// use feagi_data_serialization::FeagiByteContainer;
164    /// 
165    /// let container = FeagiByteContainer::new_empty();
166    /// assert_eq!(container.get_number_of_bytes_used(), 4); // Header only
167    /// ```
168    pub fn get_number_of_bytes_used(&self) -> usize {
169        self.bytes.len()
170    }
171
172    /// Returns the total memory allocated for the byte array.
173    /// 
174    /// This may be larger than the number of bytes used due to Vec capacity.
175    /// 
176    /// # Example
177    /// ```
178    /// use feagi_data_serialization::FeagiByteContainer;
179    /// 
180    /// let container = FeagiByteContainer::new_empty();
181    /// assert!(container.get_number_of_bytes_allocated() >= 4);
182    /// ```
183    pub fn get_number_of_bytes_allocated(&self) -> usize {
184        self.bytes.capacity()
185    }
186
187    /// Returns the increment counter value from the header.
188    /// 
189    /// The increment counter is a 16-bit value stored in bytes 1-2 of the header.
190    /// Only works if the container is valid.
191    /// 
192    /// # Example
193    /// ```
194    /// use feagi_data_serialization::FeagiByteContainer;
195    ///
196    /// let container = FeagiByteContainer::new_empty();
197    /// assert_eq!(container.get_increment_counter().unwrap(), 0u16);
198    /// ```
199    pub fn get_increment_counter(&self) -> Result<u16, FeagiDataError> {
200        if self.is_data_valid {
201            return Ok(LittleEndian::read_u16(&self.bytes[1..3]))
202        }
203        Err(FeagiDataError::DeserializationError("Given Byte Container is invalid and thus cannot be read!".into()))
204    }
205
206    //endregion
207
208    //region Extracting Struct Data
209
210    /// Creates a new structure instance from the data at the specified index.
211    /// 
212    /// Deserializes the structure data at the given index and returns a boxed
213    /// trait object. The structure type is determined from the stored metadata.
214    /// 
215    /// # Example
216    /// ```
217    /// use feagi_data_serialization::FeagiByteContainer;
218    /// 
219    /// let container = FeagiByteContainer::new_empty();
220    /// // This will fail since there are no structures
221    /// assert!(container.try_create_new_struct_from_index(0).is_err());
222    /// ```
223    pub fn try_create_new_struct_from_index(&self, index: StructureIndex) -> Result<Box<dyn FeagiSerializable>, FeagiDataError> {
224        self.verify_structure_index_valid(index)?;
225        let relevant_slice = self.contained_struct_references[index as usize].get_as_byte_slice(&self.bytes);
226        let mut boxed_struct: Box<dyn FeagiSerializable> = self.contained_struct_references[index as usize].structure_type.create_new_struct_of_type();
227        boxed_struct.try_deserialize_and_update_self_from_byte_slice(relevant_slice)?;
228        Ok(boxed_struct)
229    }
230
231    /// Creates a new structure from the first instance of the given type.
232    /// 
233    /// Searches for the first structure matching the specified type and deserializes it.
234    /// Returns None if no structure of that type is found.
235    pub fn try_create_struct_from_first_found_struct_of_type(&self, structure_type: FeagiByteStructureType) -> Result<Option<Box<dyn FeagiSerializable>>, FeagiDataError> {
236        let getting_slice = self.try_get_first_structure_slice_of_type(structure_type);
237        if getting_slice.is_none() {
238            return Ok(None);
239        }
240        let mut boxed_struct: Box<dyn FeagiSerializable> = structure_type.create_new_struct_of_type();
241        boxed_struct.try_deserialize_and_update_self_from_byte_slice(getting_slice.unwrap())?;
242        Ok(Some(boxed_struct))
243    }
244
245    /// Updates an existing structure with data from the specified index.
246    /// 
247    /// Deserializes data at the given index and updates the provided structure.
248    pub fn try_update_struct_from_index(&self, index: StructureIndex, updating_boxed_struct: &mut dyn FeagiSerializable) -> Result<(), FeagiDataError> {
249        self.verify_structure_index_valid(index)?;
250        let relevant_slice = self.contained_struct_references[index as usize].get_as_byte_slice(&self.bytes);
251        updating_boxed_struct.verify_byte_slice_is_of_correct_type(relevant_slice)?;
252        updating_boxed_struct.try_deserialize_and_update_self_from_byte_slice(relevant_slice)?;
253        Ok(())
254    }
255
256    /// Updates a structure from the first found instance of its type.
257    /// 
258    /// Returns true if a matching structure was found and updated, false otherwise.
259    pub fn try_update_struct_from_first_found_struct_of_type(&self, updating_boxed_struct: &mut dyn FeagiSerializable) -> Result<bool, FeagiDataError> {
260        let structure_type: FeagiByteStructureType = updating_boxed_struct.get_type();
261        let getting_slice = self.try_get_first_structure_slice_of_type(structure_type);
262        if getting_slice.is_none() {
263            return Ok(false);
264        }
265        updating_boxed_struct.try_deserialize_and_update_self_from_byte_slice(getting_slice.unwrap())?;
266        Ok(true)
267    }
268
269    /// Returns a list of all structure types contained in this container.
270    /// 
271    /// Provides a quick way to see what types of structures are available
272    /// without deserializing them.
273    /// 
274    /// # Example
275    /// ```
276    /// use feagi_data_serialization::FeagiByteContainer;
277    /// 
278    /// let container = FeagiByteContainer::new_empty();
279    /// let types = container.get_contained_struct_types();
280    /// assert_eq!(types.len(), 0); // Empty container
281    /// ```
282    pub fn get_contained_struct_types(&self) -> Vec<FeagiByteStructureType> {
283        let mut output: Vec<FeagiByteStructureType> = Vec::with_capacity(self.contained_struct_references.len());
284        for contained_struct_reference in &self.contained_struct_references {
285            output.push(contained_struct_reference.structure_type);
286        };
287        output
288    }
289
290    //endregion
291
292    //region Overwriting Data
293
294    /// Overwrites the container with multiple serialized structures.
295    /// 
296    /// Clears existing data and serializes all provided structures into the container.
297    /// Updates the increment counter to the specified value.
298    pub fn overwrite_byte_data_with_multiple_struct_data(&mut self, incoming_structs: Vec<&dyn FeagiSerializable>, new_increment_value: u16) -> Result<(), FeagiDataError> {
299
300        if incoming_structs.len() > MAX_NUMBER_OF_STRUCTS {
301            return Err(FeagiDataError::BadParameters(format!("FeagiByteContainers only support a max of {} contained structs, {} were given!", MAX_NUMBER_OF_STRUCTS, incoming_structs.len())))
302        }
303
304        self.bytes.clear();
305        self.contained_struct_references.clear();
306        self.is_data_valid = false;
307
308        let header_total_number_of_bytes: usize = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT +
309            Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE * incoming_structs.len();
310
311
312        // Fill out contained_struct_references, calculate total number of bytes used for the data section
313        let total_number_of_bytes = {
314            let mut data_start_index = header_total_number_of_bytes;
315            for incoming_struct in &incoming_structs {
316                let per_struct_number_bytes = incoming_struct.get_number_of_bytes_needed();
317                self.contained_struct_references.push(
318                    ContainedStructReference{
319                        structure_type: incoming_struct.get_type(),
320                        byte_start_index: data_start_index as u32,
321                        number_bytes_to_read: per_struct_number_bytes as u32,
322                    }
323                );
324                data_start_index += per_struct_number_bytes;
325            }
326            data_start_index
327        };
328
329        if total_number_of_bytes > self.bytes.capacity() {
330            self.bytes.resize(total_number_of_bytes, 0);
331        }
332
333        // Setup global header
334        self.bytes[0] = Self::CURRENT_FBS_VERSION;
335        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value); // Next 2 bytes is increment counter
336        self.bytes[3] = incoming_structs.len() as u8; // Struct count
337
338        // Write Structure lookup header and Data bytes at the same time
339        let mut structure_lookup_header_byte_index = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT;
340        for struct_index in 0..incoming_structs.len() {
341            let incoming_struct = &incoming_structs[struct_index];
342            let contained_struct_reference = &self.contained_struct_references[struct_index];
343
344            LittleEndian::write_u32(&mut self.bytes[structure_lookup_header_byte_index..structure_lookup_header_byte_index + 4], contained_struct_reference.number_bytes_to_read as u32);
345            incoming_struct.try_serialize_struct_to_byte_slice(contained_struct_reference.get_as_byte_slice_mut(&mut self.bytes))?;
346
347            structure_lookup_header_byte_index += Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE;
348        };
349
350        self.is_data_valid = true;
351        Ok(())
352
353    }
354
355    /// Overwrites the container with a single serialized structure.
356    /// 
357    /// Optimized version for when only one structure needs to be stored.
358    /// Clears existing data and serializes the structure with the given increment value.
359    pub fn overwrite_byte_data_with_single_struct_data(&mut self, incoming_struct: &dyn FeagiSerializable, new_increment_value: u16) -> Result<(), FeagiDataError> {
360
361        self.bytes.clear();
362        self.contained_struct_references.clear();
363        self.is_data_valid = false;
364
365        let number_of_bytes_used_by_struct = incoming_struct.get_number_of_bytes_needed();
366        let total_number_of_bytes = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + (1 * Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE) + number_of_bytes_used_by_struct;
367
368        self.contained_struct_references.push(
369            ContainedStructReference {
370                structure_type: incoming_struct.get_type(),
371                byte_start_index: (Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE) as u32, // This is always the first structure start index
372                number_bytes_to_read: number_of_bytes_used_by_struct as u32
373            }
374        );
375
376
377        if total_number_of_bytes > self.bytes.capacity() {
378            self.bytes.resize(total_number_of_bytes, 0);
379        }
380
381        // Setup global header
382        self.bytes[0] = Self::CURRENT_FBS_VERSION;
383        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value); // Next 2 bytes is increment counter
384        self.bytes[3] = 1u8; // Struct count is always 1 for single struct
385
386        // Write Structure lookup header ( only 1 entry)
387        let data_read_index: u32 = (Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE) as u32;
388        LittleEndian::write_u32(&mut self.bytes[Self::GLOBAL_BYTE_HEADER_BYTE_COUNT..Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + 4], data_read_index);
389
390        // Write data
391        let data_byte_slice = &mut self.bytes[data_read_index as usize..]; // rest of the array
392        incoming_struct.try_serialize_struct_to_byte_slice(data_byte_slice)?;
393
394        self.is_data_valid = true;
395        Ok(())
396
397    }
398
399    /// Updates the increment counter in the header.
400    /// 
401    /// Modifies the 16-bit increment counter stored in bytes 1-2 of the header.
402    /// The container must be valid for this operation to succeed.
403    /// 
404    /// # Example
405    /// ```
406    /// use feagi_data_serialization::FeagiByteContainer;
407    /// 
408    /// let mut container = FeagiByteContainer::new_empty();
409    /// _ = container.set_increment_counter_state(42);
410    /// ```
411    pub fn set_increment_counter_state(&mut self, new_increment_value: u16) -> Result<(), FeagiDataError> {
412        if !self.is_data_valid {
413            return Err(FeagiDataError::DeserializationError("Given Byte Container is invalid and thus cannot have its increment counter changed!".into()))
414        };
415        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value);
416        Ok(())
417    }
418
419    /// Frees any unused memory allocation in the byte vector.
420    /// 
421    /// Shrinks the capacity of the internal byte vector to match its length,
422    /// potentially reducing memory usage.
423    /// 
424    /// # Example
425    /// ```
426    /// use feagi_data_serialization::FeagiByteContainer;
427    /// 
428    /// let mut container = FeagiByteContainer::new_empty();
429    /// container.free_unused_allocation();
430    /// assert_eq!(container.get_number_of_bytes_allocated(), container.get_number_of_bytes_used());
431    /// ```
432    pub fn free_unused_allocation(&mut self) {
433        self.bytes.shrink_to_fit()
434    }
435
436    //endregion
437
438    //region Internal
439
440    /// Verifies the bytes loaded in create a valid FBC container, with indexing that doesn't leave bounds,
441    /// and also configures contained_struct_references.
442    /// WARNING: Does not verify the contained structures themselves!
443    fn verify_container_valid_and_populate(&mut self) -> Result<(), FeagiDataError> {
444        self.is_data_valid = false;
445        self.contained_struct_references.clear();
446        let byte_length = self.bytes.len();
447
448        // Verify Global Header
449        if byte_length < Self::GLOBAL_BYTE_HEADER_BYTE_COUNT { // If we cant even fit the global header, something is wrong
450            return Err(FeagiDataError::DeserializationError("Given Feagi Byte Structure byte length is too short! (Less than 4!)".into()));
451        }
452        if self.bytes[0] != Self::CURRENT_FBS_VERSION {
453            return Err(FeagiDataError::DeserializationError(format!("Given FEAGI Byte Structure is using version {} when this application only supports version {}!", self.bytes[0], Self::CURRENT_FBS_VERSION)));
454        }
455        let number_contained_structs = self.bytes[3] as usize;
456        if number_contained_structs == 0 {
457            self.is_data_valid = true; // This is technically valid, even though no meaningful data was sent
458            return Ok(())
459            // NOTE: It is possible due to an error, that there is data sent after this point. However, we are going to treat this FBC as empty and report it as such.
460        }
461
462        let minimum_count_header_size = Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE * number_contained_structs;
463        let total_header_size = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + minimum_count_header_size;
464        if byte_length < total_header_size {
465            return Err(FeagiDataError::DeserializationError(format!("Feagi Byte Data specifies the existence of {} structures, but the given byte array is under the required {} byte length!", minimum_count_header_size, Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + minimum_count_header_size)));
466        }
467
468        let mut structure_header_byte_index: usize = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT;
469        let mut structure_data_byte_index: usize = total_header_size;
470        for contained_structure_index in 0..number_contained_structs {
471            let structure_length = LittleEndian::read_u32(&self.bytes[structure_header_byte_index..structure_header_byte_index + 4]);
472
473            if structure_data_byte_index + structure_length as usize > byte_length {
474                return Err(FeagiDataError::DeserializationError(
475                    format!("Structure of index {} goes out of bound reaching position {} when given byte length is only {} long!", contained_structure_index, structure_data_byte_index + structure_length as usize, byte_length)));
476            }
477
478            let structure_type = FeagiByteStructureType::try_from(self.bytes[structure_data_byte_index])?;
479            self.contained_struct_references.push( ContainedStructReference {
480                structure_type,
481                byte_start_index: structure_data_byte_index as u32,
482                number_bytes_to_read: structure_length
483            });
484
485            structure_header_byte_index += 4; // Next u32
486            structure_data_byte_index += structure_length as usize;
487        }
488        Ok(())
489    }
490
491    /// Makes sure the given index is valid (not out of range given number of contained structs)
492    fn verify_structure_index_valid(&self, structure_index: StructureIndex) -> Result<(), FeagiDataError> {
493        if structure_index as usize >= self.contained_struct_references.len() {
494            return Err(FeagiDataError::BadParameters(format!("Structure index {} out of bounds! Feagi Byte Container only contains {} structures!", structure_index, self.contained_struct_references.len())));
495        }
496        Ok(())
497    }
498
499    /// Tries to the get the first structure in the contained structure list that is of the requested type. If none are found, returns None.
500    fn try_get_first_structure_slice_of_type(&self, structure_type: FeagiByteStructureType) -> Option<&[u8]> {
501        for index in 0..self.contained_struct_references.len() {
502            if self.contained_struct_references[index].structure_type == structure_type {
503                return Some(self.contained_struct_references[index].get_as_byte_slice(&self.bytes));
504            }
505        };
506        None
507    }
508
509    //endregion
510
511}
512
513//endregion
514
515//region Contained Struct Reference
516
517/// Internal metadata for locating serialized structures within the byte array.
518#[derive(Debug, Clone, Hash, PartialEq, Eq)]
519struct ContainedStructReference {
520    /// Type of the contained structure
521    structure_type: FeagiByteStructureType,
522    /// Starting byte index of the structure data
523    byte_start_index: ByteIndexReadingStart,
524    /// Number of bytes occupied by the structure
525    number_bytes_to_read: NumberBytesToRead
526}
527
528impl ContainedStructReference {
529    /// Returns an immutable slice of the structure's bytes.
530    pub fn get_as_byte_slice<'a>(&self, byte_source: &'a Vec<u8>) -> &'a [u8] {
531        &byte_source[self.byte_start_index as usize ..self.byte_start_index as usize + self.number_bytes_to_read as usize]
532    }
533
534    /// Returns a mutable slice of the structure's bytes.
535    pub fn get_as_byte_slice_mut<'a>(&self, byte_source: &'a mut Vec<u8>) -> &'a mut [u8] {
536        &mut byte_source[self.byte_start_index as usize ..self.byte_start_index as usize + self.number_bytes_to_read as usize]
537    }
538}
539
540//endregion