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;
55
56pub const SECTION_HEADER_COMPACT_SIZE: u16 = 512; #[derive(Encode, Decode, Debug)]
60pub struct MainHeader {
61 pub magic: [u8; 4], pub format_version: u8,
66 pub first_section_offset: u16, pub page_size: u16,
68}
69
70impl Display for MainHeader {
71 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
72 writeln!(
73 f,
74 " Magic -> {:2x}{:2x}{:2x}{:2x}",
75 self.magic[0], self.magic[1], self.magic[2], self.magic[3]
76 )?;
77 writeln!(f, " format_version -> {}", self.format_version)?;
78 writeln!(f, " first_section_offset -> {}", self.first_section_offset)?;
79 writeln!(f, " page_size -> {}", self.page_size)
80 }
81}
82
83#[derive(Encode, Decode, Debug)]
87pub struct SectionHeader {
88 pub magic: [u8; 2], pub block_size: u16, pub entry_type: UnifiedLogType,
91 pub offset_to_next_section: u32, pub used: u32, pub is_open: bool, }
95
96impl Display for SectionHeader {
97 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
98 writeln!(f, " Magic -> {:2x}{:2x}", self.magic[0], self.magic[1])?;
99 writeln!(f, " type -> {:?}", self.entry_type)?;
100 write!(
101 f,
102 " use -> {} / {} (open: {})",
103 self.used, self.offset_to_next_section, self.is_open
104 )
105 }
106}
107
108impl Default for SectionHeader {
109 fn default() -> Self {
110 Self {
111 magic: SECTION_MAGIC,
112 block_size: 512,
113 entry_type: UnifiedLogType::Empty,
114 offset_to_next_section: 0,
115 used: 0,
116 is_open: true,
117 }
118 }
119}
120
121pub enum AllocatedSection<S: SectionStorage> {
122 NoMoreSpace,
123 Section(SectionHandle<S>),
124}
125
126pub trait SectionStorage: Send + Sync {
128 fn initialize<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
130 fn post_update_header<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
132 fn append<E: Encode>(&mut self, entry: &E) -> Result<usize, EncodeError>;
134 fn flush(&mut self) -> CuResult<usize>;
136}
137
138#[derive(Default)]
141pub struct SectionHandle<S: SectionStorage> {
142 header: SectionHeader, storage: S,
144}
145
146impl<S: SectionStorage> SectionHandle<S> {
147 pub fn create(header: SectionHeader, mut storage: S) -> CuResult<Self> {
148 let _ = storage.initialize(&header).map_err(|e| e.to_string())?;
150 Ok(Self { header, storage })
151 }
152
153 pub fn mark_closed(&mut self) {
154 self.header.is_open = false;
155 }
156 pub fn append<E: Encode>(&mut self, entry: E) -> Result<usize, EncodeError> {
157 self.storage.append(&entry)
158 }
159
160 pub fn get_storage(&self) -> &S {
161 &self.storage
162 }
163
164 pub fn get_storage_mut(&mut self) -> &mut S {
165 &mut self.storage
166 }
167
168 pub fn post_update_header(&mut self) -> Result<usize, EncodeError> {
169 self.storage.post_update_header(&self.header)
170 }
171}
172
173pub struct UnifiedLogStatus {
176 pub total_used_space: usize,
177 pub total_allocated_space: usize,
178}
179
180#[derive(Encode, Decode, Debug, Clone)]
182pub struct EndOfLogMarker {
183 pub temporary: bool,
184}
185
186pub trait UnifiedLogWrite<S: SectionStorage>: Send + Sync {
190 fn add_section(
196 &mut self,
197 entry_type: UnifiedLogType,
198 requested_section_size: usize,
199 ) -> CuResult<SectionHandle<S>>;
200
201 fn flush_section(&mut self, section: &mut SectionHandle<S>);
203
204 fn status(&self) -> UnifiedLogStatus;
206}
207
208pub trait UnifiedLogRead {
210 fn read_next_section_type(&mut self, datalogtype: UnifiedLogType) -> CuResult<Option<Vec<u8>>>;
213
214 fn raw_read_section(&mut self) -> CuResult<(SectionHeader, Vec<u8>)>;
218}
219
220pub fn stream_write<E: Encode, S: SectionStorage>(
222 logger: Arc<Mutex<impl UnifiedLogWrite<S>>>,
223 entry_type: UnifiedLogType,
224 minimum_allocation_amount: usize,
225) -> CuResult<impl WriteStream<E>> {
226 LogStream::new(entry_type, logger, minimum_allocation_amount)
227}
228
229pub struct LogStream<S: SectionStorage, L: UnifiedLogWrite<S>> {
231 entry_type: UnifiedLogType,
232 parent_logger: Arc<Mutex<L>>,
233 current_section: SectionHandle<S>,
234 current_position: usize,
235 minimum_allocation_amount: usize,
236 last_log_bytes: usize,
237}
238
239impl<S: SectionStorage, L: UnifiedLogWrite<S>> LogStream<S, L> {
240 pub fn new(
242 entry_type: UnifiedLogType,
243 parent_logger: Arc<Mutex<L>>,
244 minimum_allocation_amount: usize,
245 ) -> CuResult<Self> {
246 #[cfg(feature = "std")]
247 let section = parent_logger
248 .lock()
249 .map_err(|e| {
250 CuError::from("Could not lock a section at LogStream creation")
251 .add_cause(e.to_string().as_str())
252 })?
253 .add_section(entry_type, minimum_allocation_amount)?;
254
255 #[cfg(not(feature = "std"))]
256 let section = parent_logger
257 .lock()
258 .add_section(entry_type, minimum_allocation_amount)?;
259
260 Ok(Self {
261 entry_type,
262 parent_logger,
263 current_section: section,
264 current_position: 0,
265 minimum_allocation_amount,
266 last_log_bytes: 0,
267 })
268 }
269}
270
271impl<S: SectionStorage, L: UnifiedLogWrite<S>> Debug for LogStream<S, L> {
272 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
273 write!(
274 f,
275 "MmapStream {{ entry_type: {:?}, current_position: {}, minimum_allocation_amount: {} }}",
276 self.entry_type, self.current_position, self.minimum_allocation_amount
277 )
278 }
279}
280
281impl<E: Encode, S: SectionStorage, L: UnifiedLogWrite<S>> WriteStream<E> for LogStream<S, L> {
282 fn log(&mut self, obj: &E) -> CuResult<()> {
283 let result = self.current_section.append(obj);
286 match result {
287 Ok(nb_bytes) => {
288 self.current_position += nb_bytes;
289 self.current_section.header.used += nb_bytes as u32;
290 self.last_log_bytes = nb_bytes;
291 Ok(())
293 }
294 Err(e) => match e {
295 EncodeError::UnexpectedEnd => {
296 #[cfg(feature = "std")]
297 let logger_guard = self.parent_logger.lock();
298
299 #[cfg(not(feature = "std"))]
300 let mut logger_guard = self.parent_logger.lock();
301
302 #[cfg(feature = "std")]
303 let mut logger_guard =
304 match logger_guard {
305 Ok(g) => g,
306 Err(_) => return Err(
307 "Logger mutex poisoned while reporting EncodeError::UnexpectedEnd"
308 .into(),
309 ), };
311
312 logger_guard.flush_section(&mut self.current_section);
313 self.current_section = logger_guard
314 .add_section(self.entry_type, self.minimum_allocation_amount)?;
315
316 let result = self
317 .current_section
318 .append(obj)
319 .map_err(|e| {
320 CuError::from(
321 "Failed to encode object in a newly minted section. Unrecoverable failure.",
322 )
323 .add_cause(e.to_string().as_str())
324 })?; self.current_position += result;
327 self.current_section.header.used += result as u32;
328 self.last_log_bytes = result;
329 Ok(())
330 }
331 _ => {
332 let err =
333 <&str as Into<CuError>>::into("Unexpected error while encoding object.")
334 .add_cause(e.to_string().as_str());
335 Err(err)
336 }
337 },
338 }
339 }
340
341 fn last_log_bytes(&self) -> Option<usize> {
342 Some(self.last_log_bytes)
343 }
344}
345
346impl<S: SectionStorage, L: UnifiedLogWrite<S>> Drop for LogStream<S, L> {
347 fn drop(&mut self) {
348 #[cfg(feature = "std")]
349 match self.parent_logger.lock() {
350 Ok(mut logger_guard) => {
351 logger_guard.flush_section(&mut self.current_section);
352 }
353 Err(_) => {
354 if !std::thread::panicking() {
356 eprintln!("⚠️ MmapStream::drop: logger mutex poisoned");
357 }
358 }
359 }
360
361 #[cfg(not(feature = "std"))]
362 {
363 let mut logger_guard = self.parent_logger.lock();
364 logger_guard.flush_section(&mut self.current_section);
365 }
366 }
367}