1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5extern crate core;
6
7#[cfg(feature = "std")]
8pub mod memmap;
9
10#[cfg(feature = "std")]
11mod compat {
12 pub use crate::memmap::MmapUnifiedLogger as UnifiedLogger;
14 pub use crate::memmap::MmapUnifiedLoggerBuilder as UnifiedLoggerBuilder;
15 pub use crate::memmap::MmapUnifiedLoggerRead as UnifiedLoggerRead;
16 pub use crate::memmap::MmapUnifiedLoggerWrite as UnifiedLoggerWrite;
17 pub use crate::memmap::UnifiedLoggerIOReader;
18}
19
20#[cfg(feature = "std")]
21pub use compat::*;
22
23#[cfg(not(feature = "std"))]
24mod imp {
25 pub use alloc::string::ToString;
26 pub use alloc::sync::Arc;
27 pub use alloc::vec::Vec;
28 pub use core::fmt::Debug;
29 pub use core::fmt::Display;
30 pub use core::fmt::Formatter;
31 pub use core::fmt::Result as FmtResult;
32 pub use spin::Mutex;
33}
34
35#[cfg(feature = "std")]
36mod imp {
37 pub use std::fmt::Debug;
38 pub use std::fmt::Display;
39 pub use std::fmt::Formatter;
40 pub use std::fmt::Result as FmtResult;
41 pub use std::sync::Arc;
42 pub use std::sync::Mutex;
43}
44
45use imp::*;
46
47use bincode::error::EncodeError;
48use bincode::{Decode, Encode};
49use cu29_traits::{CuError, CuResult, UnifiedLogType, WriteStream};
50
51#[allow(dead_code)]
53pub const MAIN_MAGIC: [u8; 4] = [0xB4, 0xA5, 0x50, 0xFF]; pub const SECTION_MAGIC: [u8; 2] = [0xFA, 0x57]; pub const SECTION_HEADER_COMPACT_SIZE: u16 = 512; #[derive(Encode, Decode, Debug)]
62pub struct MainHeader {
63 pub magic: [u8; 4], pub first_section_offset: u16, pub page_size: u16,
66}
67
68impl Display for MainHeader {
69 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
70 writeln!(
71 f,
72 " Magic -> {:2x}{:2x}{:2x}{:2x}",
73 self.magic[0], self.magic[1], self.magic[2], self.magic[3]
74 )?;
75 writeln!(f, " first_section_offset -> {}", self.first_section_offset)?;
76 writeln!(f, " page_size -> {}", self.page_size)
77 }
78}
79
80#[derive(Encode, Decode, Debug)]
84pub struct SectionHeader {
85 pub magic: [u8; 2], pub block_size: u16, pub entry_type: UnifiedLogType,
88 pub offset_to_next_section: u32, pub used: u32, }
91
92impl Display for SectionHeader {
93 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
94 writeln!(f, " Magic -> {:2x}{:2x}", self.magic[0], self.magic[1])?;
95 writeln!(f, " type -> {:?}", self.entry_type)?;
96 write!(
97 f,
98 " use -> {} / {}",
99 self.used, self.offset_to_next_section
100 )
101 }
102}
103
104impl Default for SectionHeader {
105 fn default() -> Self {
106 Self {
107 magic: SECTION_MAGIC,
108 block_size: 512,
109 entry_type: UnifiedLogType::Empty,
110 offset_to_next_section: 0,
111 used: 0,
112 }
113 }
114}
115
116pub enum AllocatedSection<S: SectionStorage> {
117 NoMoreSpace,
118 Section(SectionHandle<S>),
119}
120
121pub trait SectionStorage: Send + Sync {
123 fn initialize<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
125 fn post_update_header<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError>;
127 fn append<E: Encode>(&mut self, entry: &E) -> Result<usize, EncodeError>;
129 fn flush(&mut self) -> CuResult<usize>;
131}
132
133#[derive(Default)]
136pub struct SectionHandle<S: SectionStorage> {
137 header: SectionHeader, storage: S,
139}
140
141impl<S: SectionStorage> SectionHandle<S> {
142 pub fn create(header: SectionHeader, mut storage: S) -> CuResult<Self> {
143 let _ = storage.initialize(&header).map_err(|e| e.to_string())?;
145 Ok(Self { header, storage })
146 }
147 pub fn append<E: Encode>(&mut self, entry: E) -> Result<usize, EncodeError> {
148 self.storage.append(&entry)
149 }
150
151 pub fn get_storage(&self) -> &S {
152 &self.storage
153 }
154
155 pub fn get_storage_mut(&mut self) -> &mut S {
156 &mut self.storage
157 }
158
159 pub fn post_update_header(&mut self) -> Result<usize, EncodeError> {
160 self.storage.post_update_header(&self.header)
161 }
162}
163
164pub struct UnifiedLogStatus {
167 pub total_used_space: usize,
168 pub total_allocated_space: usize,
169}
170
171pub trait UnifiedLogWrite<S: SectionStorage>: Send + Sync {
175 fn add_section(
181 &mut self,
182 entry_type: UnifiedLogType,
183 requested_section_size: usize,
184 ) -> CuResult<SectionHandle<S>>;
185
186 fn flush_section(&mut self, section: &mut SectionHandle<S>);
188
189 fn status(&self) -> UnifiedLogStatus;
191}
192
193pub trait UnifiedLogRead {
195 fn read_next_section_type(&mut self, datalogtype: UnifiedLogType) -> CuResult<Option<Vec<u8>>>;
198
199 fn raw_read_section(&mut self) -> CuResult<(SectionHeader, Vec<u8>)>;
203}
204
205pub fn stream_write<E: Encode, S: SectionStorage>(
207 logger: Arc<Mutex<impl UnifiedLogWrite<S>>>,
208 entry_type: UnifiedLogType,
209 minimum_allocation_amount: usize,
210) -> CuResult<impl WriteStream<E>> {
211 LogStream::new(entry_type, logger, minimum_allocation_amount)
212}
213
214struct LogStream<S: SectionStorage, L: UnifiedLogWrite<S>> {
216 entry_type: UnifiedLogType,
217 parent_logger: Arc<Mutex<L>>,
218 current_section: SectionHandle<S>,
219 current_position: usize,
220 minimum_allocation_amount: usize,
221}
222
223impl<S: SectionStorage, L: UnifiedLogWrite<S>> LogStream<S, L> {
224 fn new(
225 entry_type: UnifiedLogType,
226 parent_logger: Arc<Mutex<L>>,
227 minimum_allocation_amount: usize,
228 ) -> CuResult<Self> {
229 #[cfg(feature = "std")]
230 let section = parent_logger
231 .lock()
232 .expect("Could not lock a section at MmapStream creation")
233 .add_section(entry_type, minimum_allocation_amount)?;
234
235 #[cfg(not(feature = "std"))]
236 let section = parent_logger
237 .lock()
238 .add_section(entry_type, minimum_allocation_amount)?;
239
240 Ok(Self {
241 entry_type,
242 parent_logger,
243 current_section: section,
244 current_position: 0,
245 minimum_allocation_amount,
246 })
247 }
248}
249
250impl<S: SectionStorage, L: UnifiedLogWrite<S>> Debug for LogStream<S, L> {
251 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
252 write!(f, "MmapStream {{ entry_type: {:?}, current_position: {}, minimum_allocation_amount: {} }}", self.entry_type, self.current_position, self.minimum_allocation_amount)
253 }
254}
255
256impl<E: Encode, S: SectionStorage, L: UnifiedLogWrite<S>> WriteStream<E> for LogStream<S, L> {
257 fn log(&mut self, obj: &E) -> CuResult<()> {
258 let result = self.current_section.append(obj);
261 match result {
262 Ok(nb_bytes) => {
263 self.current_position += nb_bytes;
264 self.current_section.header.used += nb_bytes as u32;
265 Ok(())
266 }
267 Err(e) => match e {
268 EncodeError::UnexpectedEnd => {
269 #[cfg(feature = "std")]
270 let logger_guard = self.parent_logger.lock();
271
272 #[cfg(not(feature = "std"))]
273 let mut logger_guard = self.parent_logger.lock();
274
275 #[cfg(feature = "std")]
276 let mut logger_guard =
277 match logger_guard {
278 Ok(g) => g,
279 Err(_) => return Err(
280 "Logger mutex poisoned while reporting EncodeError::UnexpectedEnd"
281 .into(),
282 ), };
284
285 logger_guard.flush_section(&mut self.current_section);
286 self.current_section = logger_guard
287 .add_section(self.entry_type, self.minimum_allocation_amount)?;
288
289 let result = self.current_section.append(obj).expect(
290 "Failed to encode object in a newly minted section. Unrecoverable failure.",
291 ); self.current_position += result;
294 self.current_section.header.used += result as u32;
295 Ok(())
296 }
297 _ => {
298 let err =
299 <&str as Into<CuError>>::into("Unexpected error while encoding object.")
300 .add_cause(e.to_string().as_str());
301 Err(err)
302 }
303 },
304 }
305 }
306}
307
308impl<S: SectionStorage, L: UnifiedLogWrite<S>> Drop for LogStream<S, L> {
309 fn drop(&mut self) {
310 #[cfg(feature = "std")]
311 let logger_guard = self.parent_logger.lock();
312
313 #[cfg(not(feature = "std"))]
314 let mut logger_guard = self.parent_logger.lock();
315
316 #[cfg(feature = "std")]
317 let mut logger_guard = match logger_guard {
318 Ok(g) => g,
319 Err(_) => return,
320 };
321 logger_guard.flush_section(&mut self.current_section);
322 #[cfg(feature = "std")]
323 if !std::thread::panicking() {
324 eprintln!("⚠️ MmapStream::drop: logger mutex poisoned");
325 }
326 }
327}