e_write_buffer/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![deny(missing_docs)]
3
4//! A `no_std`, no allocation, `core::fmt::Write`able buffer.
5//!
6//! Usage:
7//!
8//! ```rs
9//! use e_write_buffer::WriteBuffer;
10//! use std::fmt::Write as _;
11//!
12//! fn main() {
13//!     let mut buffer: WriteBuffer<20> = WriteBuffer::new();
14//!     let x = 12;
15//!     write!(buffer, "{}", x).unwrap();
16//!     assert_eq!(buffer.as_str(), "12");
17//! }
18//! ```
19use core::fmt::{self, Display, Formatter};
20
21/// A write buffer
22#[derive(Debug, Clone, Copy)]
23pub struct WriteBuffer<const N: usize> {
24    buffer: [u8; N],
25    cursor: usize,
26}
27
28impl<const N: usize> WriteBuffer<N> {
29    /// Creates a write buffer
30    pub fn new() -> Self {
31        let buf = [0u8; N];
32        WriteBuffer {
33            buffer: buf,
34            cursor: 0,
35        }
36    }
37
38    /// Returns a slice containing the already written bytes in the buffer
39    pub fn as_slice(&self) -> &[u8] {
40        &self.buffer[..self.cursor]
41    }
42
43    /// Returns a mutable slice containing the already written bytes in the
44    /// buffer
45    ///
46    /// Dev Note: This should _not_ be `pub`, since otherwise the user might
47    /// mess with the bytes, violating the guarantee that the safety of
48    /// [`as_str`] and [`as_str_mut`] depend on!
49    fn as_slice_mut(&mut self) -> &mut [u8] {
50        &mut self.buffer[..self.cursor]
51    }
52
53    /// Reset the buffer such that it can be reused.
54    ///
55    /// Note: This does _not_ overwrite any data in memory, it only sets the
56    /// internal cursor back to the start of the buffer.
57    pub fn reset(&mut self) {
58        self.cursor = 0;
59    }
60
61    /// Converts the buffer into `&str`.
62    pub fn as_str(&self) -> &str {
63        // SAFETY: The only way to write into `self.buf` is via
64        // `Write::write_str`. Therefore it is always guaranteed that the buffer
65        // contains valid UTF-8.
66        unsafe { core::str::from_utf8_unchecked(self.as_slice()) }
67    }
68
69    /// Converts the buffer into `&mut str`.
70    pub fn as_str_mut(&mut self) -> &mut str {
71        // SAFETY: The only way to write into `self.buf` is via
72        // `Write::write_str`. Therefore it is always guaranteed that the buffer
73        // contains valid UTF-8.
74        unsafe { core::str::from_utf8_unchecked_mut(self.as_slice_mut()) }
75    }
76
77    /// Returns how many bytes in the buffer have already been written.
78    pub fn len(&self) -> usize {
79        self.cursor
80    }
81
82    /// Returns true if zero bytes in the buffer are written.
83    pub fn is_empty(&self) -> bool {
84        self.len() == 0
85    }
86
87    /// Returns how many bytes in the buffer remain for writing.
88    pub fn remaining(&self) -> usize {
89        N - self.len()
90    }
91
92    /// Returns true if the buffer is full.
93    pub fn is_full(&self) -> bool {
94        self.remaining() == 0
95    }
96}
97
98impl<const N: usize> Default for WriteBuffer<N> {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl<const N: usize> PartialEq for WriteBuffer<N> {
105    fn eq(&self, other: &Self) -> bool {
106        self.as_slice() == other.as_slice()
107    }
108}
109
110impl<const N: usize> core::hash::Hash for WriteBuffer<N> {
111    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
112        self.as_slice().hash(state);
113    }
114}
115
116impl<const N: usize> Eq for WriteBuffer<N> {}
117
118impl<const N: usize> fmt::Write for WriteBuffer<N> {
119    fn write_str(&mut self, s: &str) -> fmt::Result {
120        let bytes = s.as_bytes();
121
122        // New cursor after write
123        let new_cursor = self.cursor + bytes.len();
124
125        // If we would exceed the capacity of the buffer, we fail
126        if new_cursor > N {
127            return Err(fmt::Error);
128        }
129
130        // Efficiently copy the bytes into the bufffer
131        self.buffer[self.cursor..new_cursor].copy_from_slice(bytes);
132
133        // Update the cursor
134        self.cursor = new_cursor;
135
136        Ok(())
137    }
138}
139
140impl<const N: usize> Display for WriteBuffer<N> {
141    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
142        write!(f, "{}", self.as_str())
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use super::WriteBuffer;
149    use core::fmt::Write;
150
151    #[test]
152    fn test_write_wrapper() {
153        let x = 123;
154
155        let mut buffer: WriteBuffer<20> = WriteBuffer::new();
156        write!(buffer, "{}", x).unwrap();
157        assert_eq!(&buffer.as_slice()[0..3], b"123");
158
159        buffer.reset();
160        let x = 2.242424;
161        write!(buffer, "{:.2}", x).unwrap();
162        assert_eq!(buffer.as_slice(), b"2.24");
163
164        buffer.reset();
165        let x = 20;
166        write!(buffer, "Longer than {} characters sentence", x).unwrap_err();
167
168        buffer.reset();
169        write!(buffer, "{}", "1").unwrap();
170        write!(buffer, "{}", "2").unwrap();
171        assert_eq!(buffer.as_slice(), b"12");
172    }
173
174    #[test]
175    fn test_display() {
176        let x = 123;
177
178        let mut buffer: WriteBuffer<20> = WriteBuffer::new();
179        write!(buffer, "{}", x).unwrap();
180        assert_eq!("123", format!("{}", buffer));
181    }
182
183    #[test]
184    fn test_as_str_mut() {
185        let mut buffer: WriteBuffer<20> = WriteBuffer::new();
186        write!(buffer, "hello world").unwrap();
187        buffer.as_str_mut().make_ascii_uppercase();
188
189        assert_eq!(buffer.as_str(), "HELLO WORLD");
190    }
191
192    #[test]
193    fn test_is_empty_is_full_and_overflow() {
194        let mut buffer: WriteBuffer<10> = WriteBuffer::new();
195        assert!(buffer.is_empty());
196        write!(buffer, "0123456789").unwrap();
197        assert!(buffer.is_full());
198        assert_eq!(write!(buffer, "!"), Err(core::fmt::Error));
199    }
200}