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}