sphinx/runtime/strings/
buffer.rs1use core::ptr;
8use core::str;
9use core::fmt;
10use core::mem::{self, MaybeUninit};
11use core::borrow::Borrow;
12
13
14#[derive(Clone, Copy)]
20pub struct StrBuffer<const N: usize> {
21 data: [MaybeUninit<u8>; N],
22 len: u8,
23}
24
25impl<'s, const N: usize> TryFrom<&'s String> for StrBuffer<N> {
27 type Error = &'s String;
28 #[inline]
29 fn try_from(value: &'s String) -> Result<Self, Self::Error> {
30 Self::try_from(value)
31 }
32}
33
34impl<'s, const N: usize> TryFrom<&'s str> for StrBuffer<N> {
35 type Error = &'s str;
36 #[inline]
37 fn try_from(value: &'s str) -> Result<Self, Self::Error> {
38 Self::try_from(value)
39 }
40}
41
42impl<const N: usize> StrBuffer<N> {
43 #[inline]
45 pub fn new() -> Self {
46 Self {
47 len: 0,
48 data: [MaybeUninit::<u8>::uninit(); N]
49 }
50 }
51
52 #[inline]
55 fn try_from<S: AsRef<str>>(s: S) -> Result<Self, S> {
56 let s_ref = s.as_ref();
57
58 if s_ref.len() <= Self::capacity() {
59 unsafe { Ok(Self::new_unchecked(s_ref)) }
60 } else {
61 Err(s)
62 }
63 }
64
65 #[inline]
66 unsafe fn new_unchecked(s: &str) -> Self {
67 let mut data = [MaybeUninit::<u8>::uninit(); N];
73 ptr::copy_nonoverlapping(s.as_ptr(), data.as_mut_ptr().cast::<u8>(), s.len());
75
76 Self {
77 len: s.len() as u8,
78 data,
79 }
80 }
81
82 #[inline]
83 fn from_array(data: [MaybeUninit<u8>; N], len: u8) -> Self {
84 Self { data, len }
85 }
86
87 #[inline]
89 pub fn capacity() -> usize { N }
90
91 #[inline]
93 pub fn len(&self) -> usize { self.len as usize }
94
95 #[inline]
97 pub fn is_empty(&self) -> bool { self.len == 0 }
98
99 pub fn try_push(&mut self, ch: char) -> fmt::Result {
100 let mut buf = [0u8; 4];
101 self.try_push_str(ch.encode_utf8(&mut buf))
102 }
103
104 #[inline]
106 pub fn try_push_str<S: AsRef<str>>(&mut self, s: S) -> fmt::Result {
107 let s_ref = s.as_ref();
108
109 if self.len() + s_ref.len() <= Self::capacity() {
110 let data = self.data[self.len as usize..].as_mut_ptr().cast::<u8>();
112
113 unsafe {
114 ptr::copy_nonoverlapping(s_ref.as_ptr(), data, s_ref.len());
118 }
119 self.len += s_ref.len() as u8;
120 Ok(())
121 } else {
122 Err(fmt::Error::default())
123 }
124 }
125}
126
127
128impl<const N: usize> core::ops::Deref for StrBuffer<N> {
129 type Target = str;
130
131 #[inline]
132 fn deref(&self) -> &Self::Target {
133 let data = &self.data[..self.len()];
134
135 unsafe {
136 let data = &*(data as *const [mem::MaybeUninit<u8>] as *const [u8]);
139 str::from_utf8_unchecked(data)
140 }
141 }
142}
143
144impl<const N: usize> AsRef<str> for StrBuffer<N> {
145 fn as_ref(&self) -> &str { &*self }
146}
147
148impl<const N: usize> Borrow<str> for StrBuffer<N> {
149 fn borrow(&self) -> &str { &*self }
150}
151
152impl<const N: usize> fmt::Write for StrBuffer<N> {
153 fn write_str(&mut self, s: &str) -> fmt::Result {
154 self.try_push_str(s)
155 }
156
157 fn write_char(&mut self, c: char) -> fmt::Result {
158 self.try_push(c)
159 }
160}
161
162impl<const N: usize> fmt::Debug for StrBuffer<N> {
163 #[inline]
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 <str as fmt::Debug>::fmt(self, f)
166 }
167}
168
169impl<const N: usize> fmt::Display for StrBuffer<N> {
170 #[inline]
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 <str as fmt::Display>::fmt(self, f)
173 }
174}
175
176
177
178
179#[cfg(test)]
180mod tests {
181 use crate::runtime::strings::buffer::StrBuffer;
182
183 #[test]
184 fn empty() {
185 let lit = "";
186 let s: StrBuffer<22> = lit.try_into().expect("bad inline str");
187 assert_eq!(&*s, lit);
188 assert_eq!(s.len(), lit.len())
189 }
190
191 #[test]
192 fn good_init() {
193 let lit = "inline";
194 let s: StrBuffer<22> = lit.try_into().expect("bad inline str");
195 assert_eq!(&*s, lit);
196 assert_eq!(s.len(), lit.len())
197 }
198
199 #[test]
200 fn bad_init() {
201 let lit = "This is way too long to be an inline string!!!";
202 let s = <StrBuffer<22>>::try_from(lit).unwrap_err();
203 assert_eq!(s, lit);
204 assert_eq!(s.len(), lit.len())
205 }
206
207 #[test]
208 fn good_concat() {
209 let lit = "Inline";
210 let lit2 = " me";
211 let mut s = <StrBuffer<22>>::try_from(lit).expect("bad inline str");
212 assert!(s.try_push_str(lit2).is_ok());
213 assert_eq!(&*s, lit.to_string() + lit2);
214 }
215
216 #[test]
217 fn bad_concat() {
218 let lit = "This is";
219 let lit2 = " way too long to be an inline string!!!";
220 let mut s = <StrBuffer<22>>::try_from(lit).expect("bad inline str");
221 assert!(s.try_push_str(lit2).is_err());
222 assert_eq!(&*s, lit);
223 }
224}