1use crate::cper::section::{CperSectionBody, fer};
7use crate::error::Error;
8use crate::header::Header;
9use crate::record::Record;
10#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12
13#[derive(Default)]
18pub struct Region {
19 pub records: Vec<Record>,
20}
21
22impl Region {
23 pub(crate) fn from_cper_section(section: &CperSectionBody) -> Option<Self> {
24 match section {
25 CperSectionBody::FirmwareErrorRecord(fer) => {
26 let guid = fer.header.guid;
27 if guid == fer::guids::RECORD_ID_CRASHLOG {
28 Region::from_slice(&fer.payload).ok()
29 } else {
30 log::info!("Ignoring unknown Firmware Error Record: {}", guid);
31 None
32 }
33 }
34 _ => None,
35 }
36 }
37
38 pub(crate) fn set_child_context(&mut self, hdr: &Header) {
39 for record in self.records.iter_mut() {
40 record.context.parent_header = Some(hdr.clone());
41 }
42 }
43
44 pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
45 let mut region = Region::default();
46 let mut cursor = 0;
47
48 while cursor < bytes.len() {
49 let header = match Header::from_slice(&bytes[cursor..]) {
50 Ok(Some(header)) => header,
51 Ok(None) => {
52 log::debug!("Found termination marker at offset {cursor}");
53 break;
54 }
55 Err(err) => {
56 log::warn!("Cannot decode record header: {err}");
57 if region.records.is_empty() {
58 return Err(err);
60 }
61 break;
62 }
63 };
64
65 log::debug!("Record version: {0}", header);
66
67 let record_size = header.record_size();
68 log::debug!("Record size: 0x{record_size:04x}");
69
70 if record_size == 0 {
71 log::warn!(
72 "{} record has an empty size. Skipping.",
73 header.record_type().unwrap_or("UNKNOWN")
74 );
75 break;
76 }
77
78 let limit = cursor + record_size;
79 if limit > bytes.len() {
80 log::warn!(
81 "Truncated record detected: record is expected to be {}B but is {}B",
82 record_size,
83 bytes.len() - cursor
84 )
85 }
86
87 region.records.push(Record {
88 header,
89 data: bytes[cursor..limit.min(bytes.len())].into(),
90 ..Default::default()
91 });
92
93 cursor += record_size;
94 }
95
96 if region.records.is_empty() {
97 return Err(Error::EmptyRegion);
98 }
99
100 Ok(region)
101 }
102
103 pub fn to_bytes(&self) -> Vec<u8> {
104 let mut bytes = Vec::new();
105 for record in self.records.iter() {
106 bytes.append(&mut record.data.clone());
107 }
108 bytes
109 }
110}