cu_sdlogger/
logger.rs

1use crate::sdmmc::{Block, BlockCount, BlockDevice, BlockIdx};
2use alloc::sync::Arc;
3use bincode::config::standard;
4use bincode::enc::write::Writer as BincodeWriter;
5use bincode::error::EncodeError;
6use bincode::{Encode, encode_into_slice, encode_into_writer};
7use core::cell::UnsafeCell;
8use cu29::prelude::*;
9
10const BLK: usize = 512;
11
12pub struct ForceSyncSend<T>(UnsafeCell<T>);
13impl<T> ForceSyncSend<T> {
14    pub const fn new(inner: T) -> Self {
15        Self(UnsafeCell::new(inner))
16    }
17    #[inline]
18    fn inner(&self) -> &T {
19        unsafe { &*self.0.get() }
20    }
21    #[inline]
22    #[allow(clippy::mut_from_ref)]
23    fn inner_mut(&self) -> &mut T {
24        unsafe { &mut *self.0.get() }
25    }
26}
27unsafe impl<T> Send for ForceSyncSend<T> {}
28unsafe impl<T> Sync for ForceSyncSend<T> {}
29
30impl<B: BlockDevice> BlockDevice for ForceSyncSend<B> {
31    type Error = B::Error;
32
33    #[cfg(all(feature = "eh02", not(feature = "eh1")))]
34    fn read(&self, blocks: &mut [Block], start: BlockIdx, reason: &str) -> Result<(), Self::Error> {
35        self.inner_mut().read(blocks, start, reason)
36    }
37
38    #[cfg(feature = "eh1")]
39    fn read(&self, blocks: &mut [Block], start: BlockIdx) -> Result<(), Self::Error> {
40        self.inner_mut().read(blocks, start)
41    }
42
43    fn write(&self, blocks: &[Block], start: BlockIdx) -> Result<(), Self::Error> {
44        self.inner_mut().write(blocks, start)
45    }
46    fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
47        self.inner().num_blocks()
48    }
49}
50
51/// Implements a bincode `Writer` for linear logging over a block device.
52pub struct SdBlockWriter<BD: BlockDevice> {
53    bd: Arc<ForceSyncSend<BD>>,
54    current_blk: BlockIdx, // absolute block number for payload
55    position_blk: usize,   // 0..512
56    capacity_bytes: usize, // payload capacity for this section
57    written: usize,        // payload bytes written so far
58    buffer: Block,         // RMW buffer for current block
59}
60
61impl<BD: BlockDevice> SdBlockWriter<BD> {
62    pub fn new(bd: Arc<ForceSyncSend<BD>>, start_block: BlockIdx, capacity_bytes: usize) -> Self {
63        Self {
64            bd,
65            current_blk: start_block,
66            position_blk: 0,
67            capacity_bytes,
68            written: 0,
69            buffer: Block::new(),
70        }
71    }
72
73    #[inline]
74    fn flush_full(&mut self) -> Result<(), EncodeError> {
75        self.bd
76            .write(core::slice::from_ref(&self.buffer), self.current_blk)
77            .expect("write failed on full block");
78        self.current_blk += BlockCount(1);
79        self.position_blk = 0;
80        self.buffer = Block::new();
81        Ok(())
82    }
83
84    /// Force-flush the current tail block if partially filled.
85    pub fn flush_tail(&mut self) -> Result<(), EncodeError> {
86        if self.position_blk != 0 {
87            self.bd
88                .write(core::slice::from_ref(&self.buffer), self.current_blk)
89                .expect("write failed on flush");
90            // Advance to the next block, start fresh at the boundary.
91            self.current_blk += BlockCount(1);
92            self.position_blk = 0;
93            self.buffer = Block::new();
94        }
95        Ok(())
96    }
97}
98
99impl<BD: BlockDevice> BincodeWriter for SdBlockWriter<BD> {
100    fn write(&mut self, mut bytes: &[u8]) -> Result<(), EncodeError> {
101        if self
102            .written
103            .checked_add(bytes.len())
104            .is_none_or(|w| w > self.capacity_bytes)
105        {
106            return Err(EncodeError::UnexpectedEnd);
107        }
108
109        if self.position_blk != 0 {
110            let take = core::cmp::min(BLK - self.position_blk, bytes.len());
111            self.buffer.as_mut()[self.position_blk..self.position_blk + take]
112                .copy_from_slice(&bytes[..take]);
113            self.position_blk += take;
114            self.written += take;
115            bytes = &bytes[take..];
116            if self.position_blk == BLK {
117                self.flush_full()?;
118            }
119        }
120
121        while bytes.len() >= BLK {
122            let mut blk = Block::new();
123            blk.as_mut().copy_from_slice(&bytes[..BLK]);
124            self.bd
125                .write(core::slice::from_ref(&blk), self.current_blk)
126                .expect("write failed");
127            self.current_blk += BlockCount(1);
128            self.written += BLK;
129            bytes = &bytes[BLK..];
130        }
131
132        if !bytes.is_empty() {
133            let n = bytes.len();
134            self.buffer.as_mut()[self.position_blk..self.position_blk + n].copy_from_slice(bytes);
135            self.position_blk += n;
136            self.written += n;
137            if self.position_blk == BLK {
138                self.flush_full()?;
139            }
140        }
141
142        Ok(())
143    }
144}
145
146pub struct EMMCSectionStorage<BD: BlockDevice> {
147    bd: Arc<ForceSyncSend<BD>>,
148    start_block: BlockIdx,
149    content_writer: SdBlockWriter<BD>,
150}
151
152impl<BD: BlockDevice> EMMCSectionStorage<BD> {
153    fn new(bd: Arc<ForceSyncSend<BD>>, start_block: BlockIdx, data_capacity: usize) -> Self {
154        // data_capacity is the space left in that section minus the header.
155        let content_writer =
156            SdBlockWriter::new(bd.clone(), start_block + BlockCount(1), data_capacity); // +1 to skip the header
157        Self {
158            bd,
159            start_block,
160            content_writer,
161        }
162    }
163}
164
165impl<BD: BlockDevice> SectionStorage for EMMCSectionStorage<BD> {
166    fn initialize<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError> {
167        self.post_update_header(header)?;
168        Ok(SECTION_HEADER_COMPACT_SIZE as usize)
169    }
170
171    fn post_update_header<E: Encode>(&mut self, header: &E) -> Result<usize, EncodeError> {
172        // Re-encode header and write again to header blocks.
173        let mut block = Block::new();
174        let wrote = encode_into_slice(header, block.as_mut(), standard())?;
175        self.bd
176            .write(&[block], self.start_block)
177            .map_err(|_| EncodeError::UnexpectedEnd)?;
178        Ok(wrote)
179    }
180
181    fn append<E: Encode>(&mut self, entry: &E) -> Result<usize, EncodeError> {
182        let bf = self.content_writer.written;
183        encode_into_writer(entry, &mut self.content_writer, standard())
184            .map_err(|_| EncodeError::UnexpectedEnd)?;
185        Ok(self.content_writer.written - bf)
186    }
187
188    fn flush(&mut self) -> CuResult<usize> {
189        let bf = self.content_writer.written;
190        self.content_writer
191            .flush_tail()
192            .map_err(|_| CuError::from("flush failed"))?;
193        Ok(self.content_writer.written - bf)
194    }
195}
196
197pub struct EMMCLogger<BD: BlockDevice> {
198    bd: Arc<ForceSyncSend<BD>>,
199    next_block: BlockIdx,
200    last_block: BlockIdx,
201    temporary_end_marker: Option<BlockIdx>,
202}
203
204impl<BD: BlockDevice> EMMCLogger<BD> {
205    pub fn new(bd: BD, start: BlockIdx, size: BlockCount) -> CuResult<Self> {
206        let main_header = MainHeader {
207            magic: MAIN_MAGIC,
208            first_section_offset: BLK as u16,
209            page_size: BLK as u16,
210        };
211        let mut block: Block = Block::new();
212
213        encode_into_slice(&main_header, block.as_mut(), standard())
214            .map_err(|_| CuError::from("Could not encode the main header"))?;
215
216        bd.write(&[block], start)
217            .map_err(|_| CuError::from("Could not write main header"))?;
218
219        let next_block = start + BlockCount(1); // +1 to skip the main header
220        let last_block = start + size;
221
222        Ok(Self {
223            bd: Arc::new(ForceSyncSend::new(bd)),
224            next_block,
225            last_block,
226            temporary_end_marker: None,
227        })
228    }
229
230    // Allocate a section in this logger and return the start block index.
231    fn alloc_section(&mut self, size: BlockCount) -> CuResult<BlockIdx> {
232        let start = self.next_block;
233        self.next_block += size;
234        if self.next_block > self.last_block {
235            return Err(CuError::from("out of space"));
236        }
237        Ok(start)
238    }
239
240    fn clear_temporary_end_marker(&mut self) {
241        if let Some(marker) = self.temporary_end_marker.take() {
242            self.next_block = marker;
243        }
244    }
245
246    fn write_end_marker(&mut self, temporary: bool) -> CuResult<()> {
247        let block_size = SECTION_HEADER_COMPACT_SIZE as usize;
248        let blocks_needed = 1; // header only
249        let start_block = self.next_block;
250        let end_block = start_block + BlockCount(blocks_needed as u32);
251        if end_block > self.last_block {
252            return Err(CuError::from("out of space"));
253        }
254
255        let header = SectionHeader {
256            magic: SECTION_MAGIC,
257            block_size: SECTION_HEADER_COMPACT_SIZE,
258            entry_type: UnifiedLogType::LastEntry,
259            offset_to_next_section: (blocks_needed * block_size) as u32,
260            used: 0,
261            is_open: temporary,
262        };
263
264        let mut header_block = Block::new();
265        encode_into_slice(&header, header_block.as_mut(), standard())
266            .map_err(|_| CuError::from("Could not encode end-of-log header"))?;
267        self.bd
268            .write(&[header_block], start_block)
269            .map_err(|_| CuError::from("Could not write end-of-log header"))?;
270
271        self.temporary_end_marker = Some(start_block);
272        self.next_block = end_block;
273        Ok(())
274    }
275}
276
277impl<BD> UnifiedLogWrite<EMMCSectionStorage<BD>> for EMMCLogger<BD>
278where
279    BD: BlockDevice + Send + Sync + 'static,
280{
281    fn add_section(
282        &mut self,
283        entry_type: UnifiedLogType,
284        requested_section_size: usize,
285    ) -> CuResult<SectionHandle<EMMCSectionStorage<BD>>> {
286        self.clear_temporary_end_marker();
287        let block_size = SECTION_HEADER_COMPACT_SIZE; // 512
288        if block_size != 512 {
289            panic!("EMMC: only 512 byte blocks supported");
290        }
291
292        let section_header = SectionHeader {
293            magic: SECTION_MAGIC,
294            block_size,
295            entry_type,
296            offset_to_next_section: requested_section_size as u32,
297            used: 0,
298            is_open: true,
299        };
300
301        let section_size_in_blks: u32 = (requested_section_size / block_size as usize) as u32 + 1; // always round up
302        let start_block = self.alloc_section(BlockCount(section_size_in_blks))?;
303
304        let storage = EMMCSectionStorage::new(
305            Arc::clone(&self.bd),
306            start_block,
307            ((section_size_in_blks - 1) * block_size as u32) as usize,
308        );
309
310        // Create handle (this will call `storage.initialize(header)`).
311        let handle = SectionHandle::create(section_header, storage)?;
312        self.write_end_marker(true)?;
313        Ok(handle)
314    }
315
316    fn flush_section(&mut self, section: &mut SectionHandle<EMMCSectionStorage<BD>>) {
317        section.mark_closed();
318        // and the end of the stream is ok.
319        section
320            .get_storage_mut()
321            .flush()
322            .expect("EMMC: flush failed");
323        // and be sure the header is up-to-date
324        section
325            .post_update_header()
326            .expect("EMMC: post update header failed");
327    }
328
329    fn status(&self) -> UnifiedLogStatus {
330        UnifiedLogStatus {
331            total_used_space: (self.next_block.0 as usize) * BLK,
332            total_allocated_space: (self.next_block.0 as usize) * BLK,
333        }
334    }
335}
336
337impl<BD: BlockDevice> Drop for EMMCLogger<BD> {
338    fn drop(&mut self) {
339        self.clear_temporary_end_marker();
340        if let Err(e) = self.write_end_marker(false) {
341            panic!("Failed to flush the unified logger: {}", e);
342        }
343    }
344}