1use core::ffi::{CStr, FromBytesWithNulError};
8use core::ops::{Deref, DerefMut};
9
10#[repr(C)]
24pub struct StringBuffer<const N: usize> {
25 length: usize,
29
30 data: [u8; N],
35}
36
37zr::static_assert!(core::mem::size_of::<StringBuffer<8>>() == 16);
39zr::static_assert!(core::mem::align_of::<StringBuffer<8>>() == core::mem::align_of::<usize>());
40
41impl<const N: usize> StringBuffer<N> {
42 const ASSERT_N_POSITIVE: () = assert!(N > 0);
43
44 pub const fn new() -> Self {
46 let _ = Self::ASSERT_N_POSITIVE;
47
48 let data = [0; N];
49 Self { length: 0, data }
50 }
51
52 pub const fn with_char(c: u8) -> Self {
55 assert!(N >= 2, "N must be at least 2 to hold a char and null terminator");
56 let mut data = [0; N];
57 data[0] = c;
58 Self { length: 1, data }
59 }
60
61 pub const fn capacity(&self) -> usize {
65 N - 1
66 }
67
68 pub fn as_cstr(&self) -> Result<&CStr, FromBytesWithNulError> {
74 CStr::from_bytes_with_nul(&self.data[..=self.length])
75 }
76
77 pub fn clear(&mut self) {
79 self.length = 0;
80 self.data[0] = 0;
81 }
82
83 pub fn set(&mut self, data: &[u8]) {
93 let len = data.len();
94 assert!(len < N, "The data and a null terminator must fit within the array.");
95 self.data[..len].copy_from_slice(data);
96 self.length = len;
97 self.data[self.length] = 0;
98 }
99
100 pub fn resize(&mut self, count: usize, ch: u8) {
111 assert!(count < N, "Must have room for count bytes an a null terminator within the array.");
112 if self.length < count {
113 self.data[self.length..count].fill(ch);
114 }
115 self.length = count;
116 self.data[self.length] = 0;
117 }
118
119 pub fn remove_prefix(&mut self, count: usize) {
125 assert!(count <= self.length, "Cannot remove more than current length");
126 self.length -= count;
127 self.data.copy_within(count..count + self.length, 0);
128 self.data[self.length] = 0;
129 }
130
131 pub fn append_char(&mut self, ch: u8) -> &mut Self {
135 if self.length < self.capacity() {
136 self.data[self.length] = ch;
137 self.length += 1;
138 self.data[self.length] = 0;
139 }
140 self
141 }
142
143 pub fn append(&mut self, data: &[u8]) -> &mut Self {
147 let remaining = self.capacity() - self.length;
148 let len = core::cmp::min(data.len(), remaining);
149 self.data[self.length..self.length + len].copy_from_slice(&data[..len]);
150 self.length += len;
151 self.data[self.length] = 0;
152 self
153 }
154}
155
156impl<const N: usize> Default for StringBuffer<N> {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162impl<const N: usize> core::fmt::Write for StringBuffer<N> {
163 fn write_str(&mut self, s: &str) -> core::fmt::Result {
167 self.append(s.as_bytes());
168 Ok(())
169 }
170}
171
172impl<const N: usize> Deref for StringBuffer<N> {
173 type Target = [u8];
174
175 fn deref(&self) -> &Self::Target {
176 &self.data[..self.length]
177 }
178}
179
180impl<const N: usize> DerefMut for StringBuffer<N> {
181 fn deref_mut(&mut self) -> &mut Self::Target {
182 &mut self.data[..self.length]
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use core::fmt::Write;
190
191 #[test]
192 fn test_empty() {
193 let sb: StringBuffer<11> = StringBuffer::new(); assert_eq!(sb.len(), 0);
195 assert_eq!(sb.capacity(), 10);
196 assert!(sb.is_empty());
197 assert_eq!(sb.data[0], 0);
198 }
199
200 #[test]
201 fn test_with_char() {
202 let sb: StringBuffer<11> = StringBuffer::with_char(b'a');
203 assert_eq!(sb.len(), 1);
204 assert_eq!(&sb[..], b"a");
205 }
206
207 #[test]
208 fn test_append() {
209 let mut sb: StringBuffer<11> = StringBuffer::new();
210 sb.append(b"hello");
211 assert_eq!(sb.len(), 5);
212 assert_eq!(&sb[..], b"hello");
213
214 sb.append(b" world");
215 assert_eq!(sb.len(), 10);
216 assert_eq!(&sb[..], b"hello worl"); }
218
219 #[test]
220 fn test_append_char() {
221 let mut sb: StringBuffer<6> = StringBuffer::new(); sb.append_char(b'a').append_char(b'b');
223 assert_eq!(&sb[..], b"ab");
224 }
225
226 #[test]
227 fn test_clear() {
228 let mut sb: StringBuffer<11> = StringBuffer::new();
229 sb.append(b"hello");
230 sb.clear();
231 assert_eq!(sb.len(), 0);
232 assert!(sb.is_empty());
233 }
234
235 #[test]
236 fn test_set() {
237 let mut sb: StringBuffer<11> = StringBuffer::new();
238 sb.set(b"hello");
239 assert_eq!(&sb[..], b"hello");
240 }
241
242 #[test]
243 fn test_resize() {
244 let mut sb: StringBuffer<11> = StringBuffer::new();
245 sb.append(b"hello");
246 sb.resize(3, b' ');
247 assert_eq!(&sb[..], b"hel");
248
249 sb.resize(6, b'x');
250 assert_eq!(&sb[..], b"helxxx");
251 }
252
253 #[test]
254 fn test_remove_prefix() {
255 let mut sb: StringBuffer<11> = StringBuffer::new();
256 sb.append(b"hello");
257 sb.remove_prefix(2);
258 assert_eq!(&sb[..], b"llo");
259 }
260
261 #[test]
262 fn test_write_macro() {
263 let mut sb: StringBuffer<11> = StringBuffer::new();
264 write!(sb, "test {}", 123).unwrap();
265 assert_eq!(&sb[..], b"test 123");
266
267 write!(sb, "more").unwrap();
268 assert_eq!(&sb[..], b"test 123mo"); }
270
271 #[test]
272 fn test_index() {
273 let mut sb: StringBuffer<11> = StringBuffer::new();
274 sb.append(b"hello");
275 assert_eq!(sb[0], b'h');
276 assert_eq!(sb[4], b'o');
277 }
278
279 #[test]
280 fn test_default() {
281 let sb: StringBuffer<11> = Default::default();
282 assert_eq!(sb.len(), 0);
283 assert_eq!(sb.capacity(), 10);
284 }
285
286 #[test]
287 fn test_constructor_zero() {
288 let sb: StringBuffer<1> = StringBuffer::new(); assert_eq!(sb.len(), 0);
290 assert_eq!(sb.capacity(), 0);
291 assert!(sb.is_empty());
292 }
293
294 #[test]
295 fn test_modify() {
296 let mut sb: StringBuffer<11> = StringBuffer::new();
297 sb.append(b"hello");
298 sb[0] = b'j';
299 assert_eq!(&sb[..], b"jello");
300 }
301
302 #[test]
303 fn test_deref() {
304 let mut sb: StringBuffer<11> = StringBuffer::new();
305 sb.append(b"hello");
306 let slice: &[u8] = &sb;
307 assert_eq!(slice, b"hello");
308
309 let slice_mut: &mut [u8] = &mut sb;
310 slice_mut[0] = b'H';
311 assert_eq!(&sb[..], b"Hello");
312 }
313
314 #[test]
315 fn test_resize_to_max() {
316 let mut sb: StringBuffer<11> = StringBuffer::new();
317 sb.resize(10, b'x');
318 assert_eq!(sb.len(), 10);
319 assert_eq!(&sb[..], b"xxxxxxxxxx");
320 assert_eq!(sb.data[10], 0);
322 }
323
324 #[test]
325 fn test_as_cstr_success() {
326 let mut sb: StringBuffer<11> = StringBuffer::new();
327 sb.set(b"hello");
328 let cstr = sb.as_cstr().unwrap();
329 assert_eq!(cstr.to_bytes(), b"hello");
330 }
331
332 #[test]
333 fn test_as_cstr_interior_null() {
334 let mut sb: StringBuffer<11> = StringBuffer::new();
335 sb.set(b"a\0b");
336 assert!(sb.as_cstr().is_err());
337 }
338
339 #[test]
340 fn test_append_chaining() {
341 let mut sb: StringBuffer<11> = StringBuffer::new();
342 sb.append(b"hello").append(b" world");
343 assert_eq!(&sb[..], b"hello worl"); }
345}