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}