Skip to main content

phc/
string_buf.rs

1use crate::{Error, Result};
2use core::{
3    fmt,
4    ops::Deref,
5    str::{self, FromStr},
6};
7
8/// Buffer for storing short stack-allocated strings.
9#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
10pub(super) struct StringBuf<const N: usize> {
11    /// Length of the string in ASCII characters (i.e. bytes).
12    pub(super) length: u8,
13
14    /// Byte array containing an ASCII-encoded string.
15    pub(super) bytes: [u8; N],
16}
17
18impl<const N: usize> StringBuf<N> {
19    /// Create a new string buffer containing the given string
20    pub(super) const fn new(s: &str) -> Result<Self> {
21        if s.len() > N || s.len() > u8::MAX as usize {
22            return Err(Error::ValueTooLong);
23        }
24
25        let mut bytes = [0u8; N];
26        let mut i = 0;
27
28        while i < s.len() {
29            bytes[i] = s.as_bytes()[i];
30            i += 1;
31        }
32
33        Ok(Self {
34            bytes,
35            length: s.len() as u8,
36        })
37    }
38}
39
40impl<const N: usize> AsRef<str> for StringBuf<N> {
41    fn as_ref(&self) -> &str {
42        str::from_utf8(&self.bytes[..(self.length as usize)]).expect("should be valid UTF-8")
43    }
44}
45
46impl<const N: usize> Default for StringBuf<N> {
47    fn default() -> Self {
48        StringBuf {
49            bytes: [0u8; N],
50            length: 0,
51        }
52    }
53}
54
55impl<const N: usize> Deref for StringBuf<N> {
56    type Target = str;
57
58    fn deref(&self) -> &str {
59        self.as_ref()
60    }
61}
62
63impl<const N: usize> FromStr for StringBuf<N> {
64    type Err = Error;
65
66    fn from_str(s: &str) -> Result<Self> {
67        Self::new(s)
68    }
69}
70
71impl<const N: usize> TryFrom<&str> for StringBuf<N> {
72    type Error = Error;
73
74    fn try_from(s: &str) -> Result<Self> {
75        Self::new(s)
76    }
77}
78
79impl<const N: usize> fmt::Debug for StringBuf<N> {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        f.write_str(self.as_ref())
82    }
83}
84
85impl<const N: usize> fmt::Display for StringBuf<N> {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.write_str(self.as_ref())
88    }
89}
90
91impl<const N: usize> fmt::Write for StringBuf<N> {
92    fn write_str(&mut self, input: &str) -> fmt::Result {
93        const { debug_assert!(N <= u8::MAX as usize) }
94
95        let bytes = input.as_bytes();
96        let length = self.length as usize;
97        let new_length = length.checked_add(bytes.len()).ok_or(fmt::Error)?;
98
99        if new_length > N {
100            return Err(fmt::Error);
101        }
102
103        self.bytes[length..new_length].copy_from_slice(bytes);
104        self.length = new_length.try_into().map_err(|_| fmt::Error)?;
105
106        Ok(())
107    }
108}