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