ove/
fmt.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Stack-allocated formatting buffer for `no_std` environments.
8//!
9//! [`FmtBuf`] implements [`core::fmt::Write`] so you can use `write!` macros
10//! without heap allocation, then pass the result to C APIs as a null-terminated
11//! byte slice via [`FmtBuf::as_cstr`].
12
13/// Zero-allocation stack buffer that implements [`core::fmt::Write`].
14///
15/// One byte is always reserved for a null terminator so that [`as_cstr`](FmtBuf::as_cstr)
16/// can return a C-compatible string without a mutable borrow.
17pub struct FmtBuf<'a> {
18    buf: &'a mut [u8],
19    pos: usize,
20}
21
22impl<'a> FmtBuf<'a> {
23    /// Create a new `FmtBuf` backed by `buf`.
24    ///
25    /// The usable capacity is `buf.len() - 1` because one byte is reserved for
26    /// the null terminator.
27    pub fn new(buf: &'a mut [u8]) -> Self {
28        if !buf.is_empty() {
29            buf[0] = 0;
30        }
31        Self { buf, pos: 0 }
32    }
33
34    /// Returns the formatted content as a byte slice (no null terminator).
35    pub fn as_bytes(&self) -> &[u8] {
36        &self.buf[..self.pos]
37    }
38
39    /// Returns the formatted content as a null-terminated byte slice,
40    /// suitable for passing to C APIs like LVGL.
41    pub fn as_cstr(&self) -> &[u8] {
42        // Null terminator is maintained by write_str after every write.
43        &self.buf[..self.pos + 1]
44    }
45}
46
47impl core::fmt::Write for FmtBuf<'_> {
48    fn write_str(&mut self, s: &str) -> core::fmt::Result {
49        let bytes = s.as_bytes();
50        let cap = self.buf.len().saturating_sub(1); // reserve 1 for null
51        let avail = cap.saturating_sub(self.pos);
52        let to_copy = bytes.len().min(avail);
53        self.buf[self.pos..self.pos + to_copy].copy_from_slice(&bytes[..to_copy]);
54        self.pos += to_copy;
55        self.buf[self.pos] = 0; // maintain null invariant
56        Ok(())
57    }
58}