Skip to main content

feagi_serialization/
feagi_byte_container.rs

1use crate::feagi_serializable::FeagiSerializable;
2use crate::FeagiByteStructureType;
3use byteorder::{ByteOrder, LittleEndian};
4use feagi_structures::FeagiDataError;
5
6const MAX_NUMBER_OF_STRUCTS: usize = u8::MAX as usize;
7const NUMBER_BYTES_IN_AGENT_IDENTIFIER: usize = 8;
8
9type StructureIndex = u8;
10type ByteIndexReadingStart = u32;
11type NumberBytesToRead = u32;
12
13//region Feagi Byte Container
14
15/// A container for serialized FEAGI data structures with efficient binary format.
16///
17/// `FeagiByteContainer` manages multiple serializable structures in a single byte array,
18/// providing methods to read, write, and validate the contained data. The container uses
19/// a header-based format with version control and structure indexing.
20///
21/// # Format
22/// - Global header: version (1 byte) + increment counter (2 bytes) + struct count (1 byte)
23/// - Session ID (8 bytes)
24/// - Per-structure headers: data length (4 bytes each)
25/// - Structure data: serialized structure bytes
26///
27/// # Example
28/// ```
29/// use feagi_serialization::FeagiByteContainer;
30///
31/// let mut container = FeagiByteContainer::new_empty();
32/// assert!(container.is_valid());
33/// assert_eq!(container.get_number_of_bytes_used(), 12); // Header + session ID
34/// ```
35#[derive(Debug, Clone)]
36pub struct FeagiByteContainer {
37    /// The actual contained byte data
38    bytes: Vec<u8>,
39    /// If the data inside the array is considered valid. If not, most functionality is disabled
40    is_data_valid: bool,
41    /// A vector of references to where in the bytes to get the slices of specific structs, and what type of Feagi data they are
42    contained_struct_references: Vec<ContainedStructReference>,
43}
44
45impl FeagiByteContainer {
46    pub const CURRENT_FBS_VERSION: u8 = 4;
47
48    pub const GLOBAL_BYTE_HEADER_BYTE_COUNT: usize = 4; // 1 u8, 1 u16, 1 u8
49
50    pub const AGENT_ID_BYTE_COUNT: usize = NUMBER_BYTES_IN_AGENT_IDENTIFIER; // 8 bytes
51
52    pub const STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE: usize = 4; // 1 u32
53
54    pub const STRUCT_HEADER_BYTE_COUNT: usize = 2; // 1 u8, 1 u8
55
56    //region Constructors
57
58    /// Creates a new empty container with default header.
59    ///
60    /// The container starts with a 4-byte header + 8 byte session ID containing version, zero increment counter,
61    /// and zero structure count and a blank session ID. The container is initially valid with just a 4 byte header
62    /// stating 0 contained structures
63    ///
64    /// # Example
65    /// ```
66    /// use feagi_serialization::FeagiByteContainer;
67    ///
68    /// let container = FeagiByteContainer::new_empty();
69    /// assert!(container.is_valid());
70    /// assert_eq!(container.get_number_of_bytes_used(), 12); // Header + session ID
71    /// ```
72    pub fn new_empty() -> Self {
73        Self {
74            bytes: Self::make_blank_header().to_vec(),
75            is_data_valid: true,
76            contained_struct_references: Vec::new(),
77        }
78    }
79
80    //endregion
81
82    // region Direct Data Access
83
84    /// Returns a reference to the internal byte array.
85    ///
86    /// Provides direct read access to the raw bytes of the container,
87    /// including headers and all serialized structure data.
88    ///
89    /// # Example
90    /// ```
91    /// use feagi_serialization::FeagiByteContainer;
92    ///
93    /// let container = FeagiByteContainer::new_empty();
94    /// let bytes = container.get_byte_ref();
95    /// assert_eq!(bytes.len(), 12);
96    /// assert_eq!(bytes[0], 3); // Current version (CURRENT_FBS_VERSION)
97    /// ```
98    pub fn get_byte_ref(&self) -> &[u8] {
99        &self.bytes
100    }
101
102    /// Writes data using a callback function and validates the container.
103    ///
104    /// Allows external code to write directly to the byte array, then validates
105    /// that the resulting data forms a valid container structure.
106    ///
107    /// # Example
108    /// ```
109    /// use feagi_serialization::{FeagiByteContainer};
110    ///
111    /// // NOTE: This function is just here as an example, but this specific implementation is invalid
112    /// let mut container = FeagiByteContainer::new_empty();
113    /// let result = container.try_write_data_to_container_and_verify(&mut |bytes| {
114    ///     *bytes = vec![20u8, 2u8, 3u8]; // This is an invalid byte sequence
115    ///     Ok(())
116    /// });
117    /// // This will fail validation since we're setting invalid data
118    /// assert!(result.is_err());
119    /// ```
120    pub fn try_write_data_to_container_and_verify<F>(
121        &mut self,
122        byte_writer: &mut F,
123    ) -> Result<(), FeagiDataError>
124    where
125        F: FnMut(&mut Vec<u8>) -> Result<(), FeagiDataError>,
126    {
127        byte_writer(&mut self.bytes)?;
128        self.verify_container_valid_and_populate()
129    }
130
131    /// Writes data to the container by taking ownership of a byte vector then validates it. Resets
132    /// allocation. Only use this if you have no option
133    ///
134    /// # Example
135    /// ```
136    /// use feagi_serialization::{FeagiByteContainer};
137    ///
138    /// // NOTE: This here as an example, but this specific implementation is invalid
139    /// let bytes = vec![20u8, 2u8, 3u8];
140    /// let mut container = FeagiByteContainer::new_empty();
141    /// let result = container.try_write_data_by_ownership_to_container_and_verify(bytes);
142    /// // This will fail validation since we're setting invalid data
143    /// assert!(result.is_err());
144    /// ```
145    pub fn try_write_data_by_ownership_to_container_and_verify(
146        &mut self,
147        new_data: Vec<u8>,
148    ) -> Result<(), FeagiDataError> {
149        self.bytes = new_data;
150        self.verify_container_valid_and_populate()
151    }
152
153    /// Writes data to the container by expanding the internal byte vector (if needed) and
154    /// overwriting the internal data with the given slice. Does not free allocation.
155    ///
156    /// # Example
157    /// ```
158    /// use feagi_serialization::{FeagiByteContainer};
159    ///
160    /// // NOTE: This here as an example, but this specific implementation is invalid
161    /// let bytes = vec![20u8, 2u8, 3u8];
162    /// let mut container = FeagiByteContainer::new_empty();
163    /// let result = container.try_write_data_by_copy_and_verify(&bytes);
164    /// // This will fail validation since we're setting invalid data
165    /// assert!(result.is_err());
166    /// ```
167    pub fn try_write_data_by_copy_and_verify(
168        &mut self,
169        new_data: &[u8],
170    ) -> Result<(), FeagiDataError> {
171        self.bytes.clear();
172        self.bytes.extend_from_slice(new_data);
173        self.verify_container_valid_and_populate()
174    }
175
176    //endregion
177
178    //region Get Properties
179
180    /// Checks if the container has valid data structure.
181    ///
182    /// Returns true if the container has been validated and contains properly
183    /// formatted header and structure data.
184    ///
185    /// # Example
186    /// ```
187    /// use feagi_serialization::FeagiByteContainer;
188    ///
189    /// let container = FeagiByteContainer::new_empty();
190    /// assert!(container.is_valid());
191    /// ```
192    pub fn is_valid(&self) -> bool {
193        self.is_data_valid
194    }
195
196    /// Returns the number of structures contained in this container.
197    ///
198    /// Only works if the container is valid. Returns an error if the container
199    /// has not been validated or contains invalid data.
200    ///
201    /// # Example
202    /// ```
203    /// use feagi_serialization::FeagiByteContainer;
204    ///
205    /// let container = FeagiByteContainer::new_empty();
206    /// assert_eq!(container.try_get_number_contained_structures().unwrap(), 0);
207    /// ```
208    pub fn try_get_number_contained_structures(&self) -> Result<usize, FeagiDataError> {
209        if self.is_data_valid {
210            return Ok(self.contained_struct_references.len());
211        }
212        Err(FeagiDataError::DeserializationError(
213            "Given Byte Container is invalid and thus cannot be read!".into(),
214        ))
215    }
216
217    /// Returns the total number of bytes currently used by the container.
218    ///
219    /// This includes headers and all structure data.
220    ///
221    /// # Example
222    /// ```
223    /// use feagi_serialization::FeagiByteContainer;
224    ///
225    /// let container = FeagiByteContainer::new_empty();
226    /// assert_eq!(container.get_number_of_bytes_used(), 12); // Header + session ID
227    /// ```
228    pub fn get_number_of_bytes_used(&self) -> usize {
229        self.bytes.len()
230    }
231
232    /// Returns the total memory allocated for the byte array.
233    ///
234    /// This may be larger than the number of bytes used due to Vec capacity.
235    ///
236    /// # Example
237    /// ```
238    /// use feagi_serialization::FeagiByteContainer;
239    ///
240    /// let container = FeagiByteContainer::new_empty();
241    /// assert!(container.get_number_of_bytes_allocated() >= 4);
242    /// ```
243    pub fn get_number_of_bytes_allocated(&self) -> usize {
244        self.bytes.capacity()
245    }
246
247    /// Returns the increment counter value from the header.
248    ///
249    /// The increment counter is a 16-bit value stored in bytes 1-2 of the header.
250    /// Only works if the container is valid.
251    ///
252    /// # Example
253    /// ```
254    /// use feagi_serialization::FeagiByteContainer;
255    ///
256    /// let container = FeagiByteContainer::new_empty();
257    /// assert_eq!(container.get_increment_counter().unwrap(), 0u16);
258    /// ```
259    pub fn get_increment_counter(&self) -> Result<u16, FeagiDataError> {
260        if self.is_data_valid {
261            return Ok(LittleEndian::read_u16(&self.bytes[1..3]));
262        }
263        Err(FeagiDataError::DeserializationError(
264            "Given Byte Container is invalid and thus cannot be read!".into(),
265        ))
266    }
267
268    pub fn get_agent_identifier_bytes(
269        &self,
270    ) -> Result<&[u8; Self::AGENT_ID_BYTE_COUNT], FeagiDataError> {
271        if self.is_data_valid {
272            let session_id_bytes = &self.bytes[Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
273                ..Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT];
274            let session_id_bytes: &[u8; Self::AGENT_ID_BYTE_COUNT] =
275                session_id_bytes.try_into().unwrap();
276            return Ok(session_id_bytes);
277        }
278        Err(FeagiDataError::DeserializationError(
279            "Given Byte Container is invalid and thus cannot be read!".into(),
280        ))
281    }
282
283    //endregion
284
285    //region Extracting Struct Data
286
287    /// Creates a new structure instance from the data at the specified index.
288    ///
289    /// Deserializes the structure data at the given index and returns a boxed
290    /// trait object. The structure type is determined from the stored metadata.
291    ///
292    /// # Example
293    /// ```
294    /// use feagi_serialization::FeagiByteContainer;
295    ///
296    /// let container = FeagiByteContainer::new_empty();
297    /// // This will fail since there are no structures
298    /// assert!(container.try_create_new_struct_from_index(0).is_err());
299    /// ```
300    pub fn try_create_new_struct_from_index(
301        &self,
302        index: StructureIndex,
303    ) -> Result<Box<dyn FeagiSerializable>, FeagiDataError> {
304        self.verify_structure_index_valid(index)?;
305        let relevant_slice =
306            self.contained_struct_references[index as usize].get_as_byte_slice(&self.bytes);
307        let mut boxed_struct: Box<dyn FeagiSerializable> = self.contained_struct_references
308            [index as usize]
309            .structure_type
310            .create_new_struct_of_type();
311        boxed_struct.try_deserialize_and_update_self_from_byte_slice(relevant_slice)?;
312        Ok(boxed_struct)
313    }
314
315    /// Creates a new structure from the first instance of the given type.
316    ///
317    /// Searches for the first structure matching the specified type and deserializes it.
318    /// Returns None if no structure of that type is found.
319    pub fn try_create_struct_from_first_found_struct_of_type(
320        &self,
321        structure_type: FeagiByteStructureType,
322    ) -> Result<Option<Box<dyn FeagiSerializable>>, FeagiDataError> {
323        let getting_slice = self.try_get_first_structure_slice_of_type(structure_type);
324        if getting_slice.is_none() {
325            return Ok(None);
326        }
327        let mut boxed_struct: Box<dyn FeagiSerializable> =
328            structure_type.create_new_struct_of_type();
329        boxed_struct.try_deserialize_and_update_self_from_byte_slice(getting_slice.unwrap())?;
330        Ok(Some(boxed_struct))
331    }
332
333    /// Updates an existing structure with data from the specified index.
334    ///
335    /// Deserializes data at the given index and updates the provided structure.
336    pub fn try_update_struct_from_index(
337        &self,
338        index: StructureIndex,
339        updating_boxed_struct: &mut dyn FeagiSerializable,
340    ) -> Result<(), FeagiDataError> {
341        self.verify_structure_index_valid(index)?;
342        let relevant_slice =
343            self.contained_struct_references[index as usize].get_as_byte_slice(&self.bytes);
344        updating_boxed_struct.verify_byte_slice_is_of_correct_type(relevant_slice)?;
345        updating_boxed_struct.try_deserialize_and_update_self_from_byte_slice(relevant_slice)?;
346        Ok(())
347    }
348
349    /// Updates a structure from the first found instance of its type.
350    ///
351    /// Returns true if a matching structure was found and updated, false otherwise.
352    pub fn try_update_struct_from_first_found_struct_of_type(
353        &self,
354        updating_boxed_struct: &mut dyn FeagiSerializable,
355    ) -> Result<bool, FeagiDataError> {
356        let structure_type: FeagiByteStructureType = updating_boxed_struct.get_type();
357        let getting_slice = self.try_get_first_structure_slice_of_type(structure_type);
358        if getting_slice.is_none() {
359            return Ok(false);
360        }
361        updating_boxed_struct
362            .try_deserialize_and_update_self_from_byte_slice(getting_slice.unwrap())?;
363        Ok(true)
364    }
365
366    /// Returns a list of all structure types contained in this container.
367    ///
368    /// Provides a quick way to see what types of structures are available
369    /// without deserializing them.
370    ///
371    /// # Example
372    /// ```
373    /// use feagi_serialization::FeagiByteContainer;
374    ///
375    /// let container = FeagiByteContainer::new_empty();
376    /// let types = container.get_contained_struct_types();
377    /// assert_eq!(types.len(), 0); // Empty container
378    /// ```
379    pub fn get_contained_struct_types(&self) -> Vec<FeagiByteStructureType> {
380        let mut output: Vec<FeagiByteStructureType> =
381            Vec::with_capacity(self.contained_struct_references.len());
382        for contained_struct_reference in &self.contained_struct_references {
383            output.push(contained_struct_reference.structure_type);
384        }
385        output
386    }
387
388    //endregion
389
390    //region Overwriting Data
391
392    /// Overwrites the container with multiple serialized structures.
393    ///
394    /// Clears existing data and serializes all provided structures into the container.
395    /// Updates the increment counter to the specified value.
396    pub fn overwrite_byte_data_with_multiple_struct_data(
397        &mut self,
398        incoming_structs: Vec<&dyn FeagiSerializable>,
399        new_increment_value: u16,
400    ) -> Result<(), FeagiDataError> {
401        if incoming_structs.len() > MAX_NUMBER_OF_STRUCTS {
402            return Err(FeagiDataError::BadParameters(format!(
403                "FeagiByteContainers only support a max of {} contained structs, {} were given!",
404                MAX_NUMBER_OF_STRUCTS,
405                incoming_structs.len()
406            )));
407        }
408
409        //self.bytes.clear(); // NOTE: Just... Don't clear the bytes. We are overwriting them or expanding if needed anyways
410        self.contained_struct_references.clear();
411        self.is_data_valid = false;
412
413        let header_total_number_of_bytes: usize = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
414            + Self::AGENT_ID_BYTE_COUNT
415            + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE * incoming_structs.len();
416
417        // Fill out contained_struct_references, calculate total number of bytes used for the data section
418        let total_number_of_bytes = {
419            let mut data_start_index = header_total_number_of_bytes;
420            for incoming_struct in &incoming_structs {
421                let per_struct_number_bytes = incoming_struct.get_number_of_bytes_needed();
422                self.contained_struct_references
423                    .push(ContainedStructReference {
424                        structure_type: incoming_struct.get_type(),
425                        byte_start_index: data_start_index as u32,
426                        number_bytes_to_read: per_struct_number_bytes as u32,
427                    });
428                data_start_index += per_struct_number_bytes;
429            }
430            data_start_index
431        };
432
433        // Ensure exact payload length. Without truncation, stale trailing bytes from previous
434        // larger payloads can leak into transport buffers and waste bandwidth.
435        if self.bytes.len() != total_number_of_bytes {
436            self.bytes.resize(total_number_of_bytes, 0);
437        }
438
439        // Setup global header
440        //self.bytes[0] = Self::CURRENT_FBS_VERSION; .. This never changes
441        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value); // Next 2 bytes is increment counter
442        self.bytes[3] = incoming_structs.len() as u8; // Struct count
443
444        // Skip Session ID section
445
446        // Write Structure lookup header and Data bytes at the same time
447        let mut structure_size_header_byte_index =
448            Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT;
449        for (struct_index, incoming_struct) in incoming_structs.iter().enumerate() {
450            let contained_struct_reference = &self.contained_struct_references[struct_index];
451
452            LittleEndian::write_u32(
453                &mut self.bytes[structure_size_header_byte_index
454                    ..structure_size_header_byte_index
455                        + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE],
456                contained_struct_reference.number_bytes_to_read,
457            );
458            incoming_struct.try_serialize_struct_to_byte_slice(
459                contained_struct_reference.get_as_byte_slice_mut(&mut self.bytes),
460            )?;
461
462            structure_size_header_byte_index +=
463                Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE;
464        }
465
466        self.is_data_valid = true;
467        Ok(())
468    }
469
470    /// Overwrites the container with a single serialized structure.
471    ///
472    /// Optimized version for when only one structure needs to be stored.
473    /// Clears existing data and serializes the structure with the given increment value.
474    pub fn overwrite_byte_data_with_single_struct_data(
475        &mut self,
476        incoming_struct: &dyn FeagiSerializable,
477        new_increment_value: u16,
478    ) -> Result<(), FeagiDataError> {
479        //self.bytes.clear(); // NOTE: Just... Don't clear the bytes. We are overwriting them or expanding if needed anyways
480        self.contained_struct_references.clear();
481        self.is_data_valid = false;
482
483        let number_of_bytes_used_by_struct = incoming_struct.get_number_of_bytes_needed();
484        let total_number_of_bytes = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
485            + Self::AGENT_ID_BYTE_COUNT
486            + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE
487            + number_of_bytes_used_by_struct;
488
489        self.contained_struct_references
490            .push(ContainedStructReference {
491                structure_type: incoming_struct.get_type(),
492                byte_start_index: (Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
493                    + Self::AGENT_ID_BYTE_COUNT
494                    + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE)
495                    as u32, // First structure starts after header + session + length
496                number_bytes_to_read: number_of_bytes_used_by_struct as u32,
497            });
498
499        // Ensure exact payload length. Without truncation, stale trailing bytes from previous
500        // larger payloads can leak into transport buffers and waste bandwidth.
501        if self.bytes.len() != total_number_of_bytes {
502            self.bytes.resize(total_number_of_bytes, 0);
503        }
504
505        // Setup global header
506        //self.bytes[0] = Self::CURRENT_FBS_VERSION; // this never changes
507        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value); // Next 2 bytes is increment counter
508        self.bytes[3] = 1u8; // Struct count is always 1 for single struct
509
510        // Skip Session ID section
511
512        // Write Structure lookup header ( only 1 entry)
513        let data_size: u32 = number_of_bytes_used_by_struct as u32;
514        LittleEndian::write_u32(
515            &mut self.bytes[Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT
516                ..Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT + 4],
517            data_size,
518        );
519
520        // Write data
521        let data_start_index: usize = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
522            + Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE
523            + Self::AGENT_ID_BYTE_COUNT; // first index is always here
524        let data_byte_slice = &mut self.bytes[data_start_index..]; // rest of the array
525        incoming_struct.try_serialize_struct_to_byte_slice(data_byte_slice)?;
526
527        self.is_data_valid = true;
528        Ok(())
529    }
530
531    /// Updates the increment counter in the header.
532    ///
533    /// Modifies the 16-bit increment counter stored in bytes 1-2 of the header.
534    /// The container must be valid for this operation to succeed.
535    ///
536    /// # Example
537    /// ```
538    /// use feagi_serialization::FeagiByteContainer;
539    ///
540    /// let mut container = FeagiByteContainer::new_empty();
541    /// _ = container.set_increment_counter_state(42);
542    /// ```
543    pub fn set_increment_counter_state(
544        &mut self,
545        new_increment_value: u16,
546    ) -> Result<(), FeagiDataError> {
547        if !self.is_data_valid {
548            return Err(FeagiDataError::DeserializationError("Given Byte Container is invalid and thus cannot have its increment counter changed!".into()));
549        };
550        LittleEndian::write_u16(&mut self.bytes[1..3], new_increment_value);
551        Ok(())
552    }
553
554    pub fn set_agent_identifier(
555        &mut self,
556        agent_identifier: impl AgentIdentifier,
557    ) -> Result<(), FeagiDataError> {
558        if !self.is_data_valid {
559            return Err(FeagiDataError::DeserializationError(
560                "Given Byte Container is invalid and thus cannot have its session id changed!"
561                    .into(),
562            ));
563        }
564        self.bytes[Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
565            ..Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT]
566            .copy_from_slice(agent_identifier.get_identifier_bytes());
567        Ok(())
568    }
569
570    /// Frees any unused memory allocation in the byte vector.
571    ///
572    /// Shrinks the capacity of the internal byte vector to match its length,
573    /// potentially reducing memory usage.
574    ///
575    /// # Example
576    /// ```
577    /// use feagi_serialization::FeagiByteContainer;
578    ///
579    /// let mut container = FeagiByteContainer::new_empty();
580    /// container.free_unused_allocation();
581    /// assert_eq!(container.get_number_of_bytes_allocated(), container.get_number_of_bytes_used());
582    /// ```
583    pub fn free_unused_allocation(&mut self) {
584        self.bytes.shrink_to_fit()
585    }
586
587    //endregion
588
589    //region Internal
590
591    const fn make_blank_header() -> [u8; 4 + Self::AGENT_ID_BYTE_COUNT] {
592        let mut arr = [0u8; 4 + Self::AGENT_ID_BYTE_COUNT];
593        arr[0] = Self::CURRENT_FBS_VERSION;
594        arr
595    }
596
597    /// Verifies the bytes loaded in create a valid FBC container, with indexing that doesn't leave bounds,
598    /// and also configures contained_struct_references.
599    /// WARNING: Does not verify the contained structures themselves!
600    fn verify_container_valid_and_populate(&mut self) -> Result<(), FeagiDataError> {
601        self.is_data_valid = false;
602        self.contained_struct_references.clear();
603        let byte_length = self.bytes.len();
604
605        // Verify Global Header
606        if byte_length < Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT {
607            // If we cant even fit the global header + session ID, something is wrong
608            return Err(FeagiDataError::DeserializationError(
609                "Given Feagi Byte Structure byte length is too short! (Less than 12!)".into(),
610            ));
611        }
612        if self.bytes[0] != Self::CURRENT_FBS_VERSION {
613            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)));
614        }
615        let number_contained_structs = self.bytes[3] as usize;
616        if number_contained_structs == 0 {
617            self.is_data_valid = true; // This is technically valid, even though no meaningful data was sent
618            return Ok(());
619            // 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.
620        }
621
622        let structure_lookup_header_size_in_bytes =
623            Self::STRUCTURE_LOOKUP_HEADER_BYTE_COUNT_PER_STRUCTURE * number_contained_structs;
624        let total_header_size = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
625            + Self::AGENT_ID_BYTE_COUNT
626            + structure_lookup_header_size_in_bytes;
627        if byte_length < total_header_size {
628            return Err(FeagiDataError::DeserializationError(format!(
629                "Feagi Byte Data specifies the existence of {} structures, but the given byte array is under the required {} byte length!",
630                structure_lookup_header_size_in_bytes, total_header_size
631            )));
632        }
633
634        let mut structure_header_byte_index: usize =
635            Self::GLOBAL_BYTE_HEADER_BYTE_COUNT + Self::AGENT_ID_BYTE_COUNT;
636        let mut structure_data_byte_index: usize = Self::GLOBAL_BYTE_HEADER_BYTE_COUNT
637            + Self::AGENT_ID_BYTE_COUNT
638            + structure_lookup_header_size_in_bytes;
639        for contained_structure_index in 0..number_contained_structs {
640            let structure_length = LittleEndian::read_u32(
641                &self.bytes[structure_header_byte_index..structure_header_byte_index + 4],
642            );
643
644            if structure_data_byte_index + structure_length as usize > byte_length {
645                return Err(FeagiDataError::DeserializationError(
646                    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)));
647            }
648
649            let structure_type =
650                FeagiByteStructureType::try_from(self.bytes[structure_data_byte_index])?;
651            self.contained_struct_references
652                .push(ContainedStructReference {
653                    structure_type,
654                    byte_start_index: structure_data_byte_index as u32,
655                    number_bytes_to_read: structure_length,
656                });
657
658            structure_header_byte_index += 4; // Next u32
659            structure_data_byte_index += structure_length as usize;
660        }
661        self.is_data_valid = true;
662        Ok(())
663    }
664
665    /// Makes sure the given index is valid (not out of range given number of contained structs)
666    fn verify_structure_index_valid(
667        &self,
668        structure_index: StructureIndex,
669    ) -> Result<(), FeagiDataError> {
670        if structure_index as usize >= self.contained_struct_references.len() {
671            return Err(FeagiDataError::BadParameters(format!("Structure index {} out of bounds! Feagi Byte Container only contains {} structures!", structure_index, self.contained_struct_references.len())));
672        }
673        Ok(())
674    }
675
676    /// Tries to the get the first structure in the contained structure list that is of the requested type. If none are found, returns None.
677    fn try_get_first_structure_slice_of_type(
678        &self,
679        structure_type: FeagiByteStructureType,
680    ) -> Option<&[u8]> {
681        for index in 0..self.contained_struct_references.len() {
682            if self.contained_struct_references[index].structure_type == structure_type {
683                return Some(
684                    self.contained_struct_references[index].get_as_byte_slice(&self.bytes),
685                );
686            }
687        }
688        None
689    }
690
691    //endregion
692}
693
694impl std::fmt::Display for FeagiByteContainer {
695    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
696        write!(
697            f,
698            "FeagiByteContainer({} bytes used out of {} allocated)",
699            self.get_number_of_bytes_used(),
700            self.get_number_of_bytes_allocated()
701        )
702    }
703}
704
705//endregion
706
707//region Contained Struct Reference
708
709/// Internal metadata for locating serialized structures within the byte array.
710#[derive(Debug, Clone, Hash, PartialEq, Eq)]
711struct ContainedStructReference {
712    /// Type of the contained structure
713    structure_type: FeagiByteStructureType,
714    /// Starting byte index of the structure data
715    byte_start_index: ByteIndexReadingStart,
716    /// Number of bytes occupied by the structure
717    number_bytes_to_read: NumberBytesToRead,
718}
719
720impl ContainedStructReference {
721    /// Returns an immutable slice of the structure's bytes.
722    pub fn get_as_byte_slice<'a>(&self, byte_source: &'a [u8]) -> &'a [u8] {
723        &byte_source[self.byte_start_index as usize
724            ..self.byte_start_index as usize + self.number_bytes_to_read as usize]
725    }
726
727    /// Returns a mutable slice of the structure's bytes.
728    pub fn get_as_byte_slice_mut<'a>(&self, byte_source: &'a mut [u8]) -> &'a mut [u8] {
729        &mut byte_source[self.byte_start_index as usize
730            ..self.byte_start_index as usize + self.number_bytes_to_read as usize]
731    }
732}
733
734//endregion
735
736pub trait AgentIdentifier
737where
738    Self: Sized + 'static,
739{
740    fn get_identifier_bytes(&self) -> &[u8; NUMBER_BYTES_IN_AGENT_IDENTIFIER];
741}