Skip to main content

atmos/kernel/
uart_line_buf.rs

1//! ログ 1 行をスタック上で組み立てるバッファ(純粋モジュール)。
2//!
3//! `uart.rs` から切り出した。MMIO にもグローバル状態にも触れないので、
4//! QEMU 抜きで単体試験できる(`tests/src/test_line_buf.rs`)。
5//!
6//! 仕様は `spec/logging_policy.md`。
7
8/// 1 行ぶんを組み立てるスタック上のバッファ。
9///
10/// ログ経路では**ヒープを使わない**。`String` を組むとアロケータのロックを
11/// 取ることになり、確保まわりの不具合を調べるためのログ自身が
12/// その経路に依存してしまう。
13///
14/// 入り切らない分は捨てる(切り詰めた印として末尾に `…` を付ける)。
15/// ログが切れるのは困るが、行が混ざって読めなくなるよりはよい。
16pub struct LineBuf {
17    buf: [u8; LINE_BUF_LEN],
18    len: usize,
19    truncated: bool,
20}
21
22/// 1 行の最大長。URL を含む行があるので余裕を持たせる。
23pub const LINE_BUF_LEN: usize = 512;
24
25impl LineBuf {
26    pub const fn new() -> Self {
27        Self {
28            buf: [0u8; LINE_BUF_LEN],
29            len: 0,
30            truncated: false,
31        }
32    }
33
34    pub fn as_bytes(&self) -> &[u8] {
35        &self.buf[..self.len]
36    }
37
38    pub fn was_truncated(&self) -> bool {
39        self.truncated
40    }
41
42    /// 末尾が UTF-8 の途中で切れていたら、直前の文字境界まで戻す。
43    ///
44    /// さらに印(`...`)を書けるだけの余地も空ける。
45    /// 不正なバイト列を UART へ流さないための後始末。
46    pub fn trim_to_char_boundary(&mut self) {
47        // 印のぶんの余地を先に確保する。
48        self.len = self.len.saturating_sub(3);
49        // 継続バイト(0b10xxxxxx)の上に立っている間は戻る。
50        while self.len > 0 && (self.buf[self.len] & 0xC0) == 0x80 {
51            self.len -= 1;
52        }
53    }
54}
55
56impl Default for LineBuf {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl core::fmt::Write for LineBuf {
63    fn write_str(&mut self, s: &str) -> core::fmt::Result {
64        let src = s.as_bytes();
65        let room = LINE_BUF_LEN - self.len;
66        if src.len() > room {
67            self.buf[self.len..].copy_from_slice(&src[..room]);
68            self.len = LINE_BUF_LEN;
69            self.truncated = true;
70            // 切り詰めても Ok を返す。ログの都合で呼び出し元を失敗させない。
71            return Ok(());
72        }
73        self.buf[self.len..self.len + src.len()].copy_from_slice(src);
74        self.len += src.len();
75        Ok(())
76    }
77}