1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4extern crate core;
5
6#[cfg(feature = "std")]
7pub mod memmap;
8pub mod noop;
9
10#[cfg(feature = "std")]
11mod compat {
12 pub use crate::memmap::LogPosition;
14 pub use crate::memmap::MmapUnifiedLogger as UnifiedLogger;
15 pub use crate::memmap::MmapUnifiedLoggerBuilder as UnifiedLoggerBuilder;
16 pub use crate::memmap::MmapUnifiedLoggerRead as UnifiedLoggerRead;
17 pub use crate::memmap::MmapUnifiedLoggerWrite as UnifiedLoggerWrite;
18 pub use crate::memmap::UnifiedLoggerIOReader;
19}
20
21#[cfg(feature = "std")]
22pub use compat::*;
23pub use noop::{NoopLogger, NoopSectionStorage};
24
25use alloc::string::ToString;
26#[cfg(not(feature = "std"))]
27use alloc::sync::Arc;
28use alloc::vec::Vec;
29use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
30#[cfg(not(feature = "std"))]
31use spin::Mutex;
32#[cfg(feature = "std")]
33use std::sync::{Arc, Mutex};
34
35use bincode::error::EncodeError;
36use bincode::{Decode, Encode};
37use cu29_traits::{CuError, CuResult, UnifiedLogType, WriteStream};
38
39#[allow(dead_code)]
41pub const MAIN_MAGIC: [u8; 4] = [0xB4, 0xA5, 0x50, 0xFF]; pub const SECTION_MAGIC: [u8; 2] = [0xFA, 0x57]; pub const UNIFIED_LOG_FORMAT_VERSION: u8 = 1;
48
49pub const SECTION_HEADER_COMPACT_SIZE: u16 = 512; #[derive(Encode, Decode, Debug)]
53pub struct MainHeader {
54 pub magic: [u8; 4], pub format_version: u8,
56 pub first_section_offset: u16, pub page_size: u16,
58}
59
60impl Display for MainHeader {
61 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
62 writeln!(
63 f,
64 " Magic -> {:2x}{:2x}{:2x}{:2x}",
65 self.magic[0], self.magic[1], self.magic[2], self.magic[3]
66 )?;
67 writeln!(f, " format_version -> {}", self.format_version)?;
68 writeln!(f, " first_section_offset -> {}", self.first_section_offset)?;
69 writeln!(f, " page_size -> {}", self.page_size)
70 }
71}
72
73#[derive(Encode, Decode, Debug)]
77pub struct SectionHeader {
78 pub magic: [u8; 2], pub block_size: u16, pub entry_type: UnifiedLogType,
81 pub offset_to_next_section: u32, pub used: u32, pub is_open: bool, }
85
86impl Display for SectionHeader {
87 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
88 writeln!(f, " Magic -> {:2x}{:2x}", self.magic[0], self.magic[1])?;
89 writeln!(f, " type -> {:?}", self.entry_type)?;
90 write!(
91 f,
92 " use -> {} / {} (open: {})",
93 self.used, self.offset_to_next_section, self.is_open
94 )
95 }
96}
97
98impl Default for SectionHeader {
99 fn default() -> Self {
100 Self {
101 magic: SECTION_MAGIC,
102 block_size: 512,
103 entry_type: UnifiedLogType::Empty,
104 offset_to_next_section: 0,
105 used: 0,
106 is_open: true,
107 }
108 }
109}
110
111pub enum AllocatedSection<S: SectionStorage> {
112 NoMoreSpace,
113 Section(SectionHandle<S>),
114}
115
116pub trait SectionStorage: Send + Sync {
118 fn initialize<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
120 fn post_update_header<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
122 fn append<E: Encode>(&mut self, entry: &E) -> Result<usize, EncodeError>;
124 fn flush(&mut self) -> CuResult<usize>;
126}
127
128#[derive(Default)]
131pub struct SectionHandle<S: SectionStorage> {
132 header: SectionHeader, storage: S,
134}
135
136impl<S: SectionStorage> SectionHandle<S> {
137 pub fn create(header: SectionHeader, mut storage: S) -> CuResult<Self> {
138 let _ = storage.initialize(&header).map_err(|e| e.to_string())?;
140 Ok(Self { header, storage })
141 }
142
143 pub fn mark_closed(&mut self) {
144 self.header.is_open = false;
145 }
146 pub fn append<E: Encode>(&mut self, entry: E) -> Result<usize, EncodeError> {
147 self.storage.append(&entry)
148 }
149
150 pub fn get_storage(&self) -> &S {
151 &self.storage
152 }
153
154 pub fn get_storage_mut(&mut self) -> &mut S {
155 &mut self.storage
156 }
157
158 pub fn post_update_header(&mut self) -> Result<usize, EncodeError> {
159 self.storage.post_update_header(&self.header)
160 }
161}
162
163pub struct UnifiedLogStatus {
166 pub total_used_space: usize,
167 pub total_allocated_space: usize,
168}
169
170#[derive(Encode, Decode, Debug, Clone)]
172pub struct EndOfLogMarker {
173 pub temporary: bool,
174}
175
176pub trait UnifiedLogWrite<S: SectionStorage>: Send + Sync {
180 fn add_section(
186 &mut self,
187 entry_type: UnifiedLogType,
188 requested_section_size: usize,
189 ) -> CuResult<SectionHandle<S>>;
190
191 fn flush_section(&mut self, section: &mut SectionHandle<S>);
193
194 fn status(&self) -> UnifiedLogStatus;
196}
197
198pub trait UnifiedLogRead {
200 fn read_next_section_type(&mut self, datalogtype: UnifiedLogType) -> CuResult<Option<Vec<u8>>>;
203
204 fn raw_read_section(&mut self) -> CuResult<(SectionHeader, Vec<u8>)>;
208}
209
210pub fn stream_write<E: Encode, S: SectionStorage>(
212 logger: Arc<Mutex<impl UnifiedLogWrite<S>>>,
213 entry_type: UnifiedLogType,
214 minimum_allocation_amount: usize,
215) -> CuResult<impl WriteStream<E>> {
216 LogStream::new(entry_type, logger, minimum_allocation_amount)
217}
218
219pub struct LogStream<S: SectionStorage, L: UnifiedLogWrite<S>> {
221 entry_type: UnifiedLogType,
222 parent_logger: Arc<Mutex<L>>,
223 current_section: SectionHandle<S>,
224 current_position: usize,
225 minimum_allocation_amount: usize,
226 last_log_bytes: usize,
227}
228
229impl<S: SectionStorage, L: UnifiedLogWrite<S>> LogStream<S, L> {
230 fn new(
231 entry_type: UnifiedLogType,
232 parent_logger: Arc<Mutex<L>>,
233 minimum_allocation_amount: usize,
234 ) -> CuResult<Self> {
235 #[cfg(feature = "std")]
236 let section = parent_logger
237 .lock()
238 .map_err(|e| {
239 CuError::from("Could not lock a section at LogStream creation")
240 .add_cause(e.to_string().as_str())
241 })?
242 .add_section(entry_type, minimum_allocation_amount)?;
243
244 #[cfg(not(feature = "std"))]
245 let section = parent_logger
246 .lock()
247 .add_section(entry_type, minimum_allocation_amount)?;
248
249 Ok(Self {
250 entry_type,
251 parent_logger,
252 current_section: section,
253 current_position: 0,
254 minimum_allocation_amount,
255 last_log_bytes: 0,
256 })
257 }
258}
259
260impl<S: SectionStorage, L: UnifiedLogWrite<S>> Debug for LogStream<S, L> {
261 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
262 write!(
263 f,
264 "MmapStream {{ entry_type: {:?}, current_position: {}, minimum_allocation_amount: {} }}",
265 self.entry_type, self.current_position, self.minimum_allocation_amount
266 )
267 }
268}
269
270impl<E: Encode, S: SectionStorage, L: UnifiedLogWrite<S>> WriteStream<E> for LogStream<S, L> {
271 fn log(&mut self, obj: &E) -> CuResult<()> {
272 let result = self.current_section.append(obj);
275 match result {
276 Ok(nb_bytes) => {
277 self.current_position += nb_bytes;
278 self.current_section.header.used += nb_bytes as u32;
279 self.last_log_bytes = nb_bytes;
280 Ok(())
282 }
283 Err(e) => match e {
284 EncodeError::UnexpectedEnd => {
285 #[cfg(feature = "std")]
286 let logger_guard = self.parent_logger.lock();
287
288 #[cfg(not(feature = "std"))]
289 let mut logger_guard = self.parent_logger.lock();
290
291 #[cfg(feature = "std")]
292 let mut logger_guard =
293 match logger_guard {
294 Ok(g) => g,
295 Err(_) => return Err(
296 "Logger mutex poisoned while reporting EncodeError::UnexpectedEnd"
297 .into(),
298 ), };
300
301 logger_guard.flush_section(&mut self.current_section);
302 self.current_section = logger_guard
303 .add_section(self.entry_type, self.minimum_allocation_amount)?;
304
305 let result = self
306 .current_section
307 .append(obj)
308 .map_err(|e| {
309 CuError::from(
310 "Failed to encode object in a newly minted section. Unrecoverable failure.",
311 )
312 .add_cause(e.to_string().as_str())
313 })?; self.current_position += result;
316 self.current_section.header.used += result as u32;
317 self.last_log_bytes = result;
318 Ok(())
319 }
320 _ => {
321 let err =
322 <&str as Into<CuError>>::into("Unexpected error while encoding object.")
323 .add_cause(e.to_string().as_str());
324 Err(err)
325 }
326 },
327 }
328 }
329
330 fn last_log_bytes(&self) -> Option<usize> {
331 Some(self.last_log_bytes)
332 }
333}
334
335impl<S: SectionStorage, L: UnifiedLogWrite<S>> Drop for LogStream<S, L> {
336 fn drop(&mut self) {
337 #[cfg(feature = "std")]
338 match self.parent_logger.lock() {
339 Ok(mut logger_guard) => {
340 logger_guard.flush_section(&mut self.current_section);
341 }
342 Err(_) => {
343 if !std::thread::panicking() {
345 eprintln!("⚠️ MmapStream::drop: logger mutex poisoned");
346 }
347 }
348 }
349
350 #[cfg(not(feature = "std"))]
351 {
352 let mut logger_guard = self.parent_logger.lock();
353 logger_guard.flush_section(&mut self.current_section);
354 }
355 }
356}