1 //!Static string buffer
2 //!
3 //!Features:
4 //!
5 //!- `serde` Enables serde serialization. In case of overflow, deserialize fails.
6 #![warn(missing_docs)]
7 
8 #![no_std]
9 #![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
10 
11 use core::{mem, slice, ptr, cmp, ops, hash, borrow};
12 
13 #[cfg(feature = "serde")]
14 mod serde;
15 
16 ///Stack based string.
17 ///
18 ///It's size is `mem::size_of::<T>() + mem::size_of::<u8>()`, but remember that it can be padded.
19 ///Can store up to `u8::max_value()` as anything bigger makes a little sense.
20 ///
21 ///Storage `T` is always interpreted as array of bytes.
22 ///
23 ///When attempting to create new instance from `&str` it panics on overflow in debug mode.
24 ///
25 ///```
26 ///use str_buf::StrBuf;
27 ///use core::mem;
28 ///use core::fmt::Write;
29 ///
30 ///type MyStr = StrBuf::<String>;
31 ///
32 ///assert_eq!(MyStr::capacity(), mem::size_of::<String>());
33 /////If you want it to be equal to string you'll have to adjust storage accordingly
34 ///assert_ne!(mem::size_of::<MyStr>(), mem::size_of::<String>());
35 ///assert_eq!(mem::size_of::<StrBuf::<[u8; mem::size_of::<String>() - 1]>>(), mem::size_of::<String>());
36 ///
37 ///let text: MyStr = "test".into();
38 ///assert_eq!("test", text);
39 ///assert_eq!(text, "test");
40 ///let mut text = MyStr::new();
41 ///let _ = write!(text, "test {}", "hello world");
42 ///assert_eq!(text.as_str(), "test hello world");
43 ///assert_eq!(text.remaining(), MyStr::capacity() - "test hello world".len());
44 ///
45 ///assert_eq!(text.push_str(" or maybe not"), 8); //Overflow!
46 ///assert_eq!(text.as_str(), "test hello world or mayb");
47 ///assert_eq!(text.push_str(" or maybe not"), 0); //Overflow, damn
48 ///
49 ///text.clear();
50 ///assert_eq!(text.push_str(" or maybe not"), 13); //noice
51 ///assert_eq!(text.as_str(), " or maybe not");
52 ///
53 ///assert_eq!(text.clone().as_str(), text.as_str());
54 ///assert_eq!(text.clone(), text);
55 ///```
56 pub struct StrBuf<T: Sized> {
57     inner: mem::MaybeUninit<T>,
58     cursor: u8, //number of bytes written
59 }
60 
61 impl<S: Sized> StrBuf<S> {
62     #[inline]
63     ///Creates new instance
new() -> Self64     pub const fn new() -> Self {
65         Self {
66             inner: mem::MaybeUninit::uninit(),
67             cursor: 0,
68         }
69     }
70 
71     #[inline]
72     ///Creates new instance from supplied storage and written size.
73     ///
74     ///It is unsafe, because there is no guarantee that storage is correctly initialized with UTF-8
75     ///bytes.
from_storage(storage: S, cursor: u8) -> Self76     pub const unsafe fn from_storage(storage: S, cursor: u8) -> Self {
77         Self {
78             inner: mem::MaybeUninit::new(storage),
79             cursor,
80         }
81     }
82 
83     #[inline]
84     ///Creates new instance from existing slice with panic on overflow
from_str(text: &str) -> Self85     pub fn from_str(text: &str) -> Self {
86         debug_assert!(text.len() <= Self::capacity());
87         let mut result = Self::new();
88         result.push_str(text);
89         result
90     }
91 
92     #[inline]
93     ///Returns pointer  to the beginning of underlying buffer
as_ptr(&self) -> *const u894     pub const fn as_ptr(&self) -> *const u8 {
95         &self.inner as *const _ as *const u8
96     }
97 
98     #[inline]
99     ///Returns number of bytes left (not written yet)
remaining(&self) -> usize100     pub const fn remaining(&self) -> usize {
101         Self::capacity() - self.cursor as usize
102     }
103 
104     #[inline]
105     ///Returns slice to already written data.
as_slice(&self) -> &[u8]106     pub fn as_slice(&self) -> &[u8] {
107         unsafe {
108             slice::from_raw_parts(self.as_ptr(), self.cursor as usize)
109         }
110     }
111 
112     #[inline]
113     ///Returns mutable slice to already written data.
as_mut_slice(&mut self) -> &mut [u8]114     pub fn as_mut_slice(&mut self) -> &mut [u8] {
115         unsafe {
116             slice::from_raw_parts_mut(self.as_ptr() as *mut u8, self.cursor as usize)
117         }
118     }
119 
120     #[inline(always)]
121     ///Clears the content of buffer.
clear(&mut self)122     pub fn clear(&mut self) {
123         unsafe {
124             self.truncate(0);
125         }
126     }
127 
128     #[inline]
129     ///Shortens the buffer, keeping the first `cursor` elements.
130     ///
131     ///Does nothing if new `cursor` is after current position.
132     ///
133     ///Unsafe as it is up to user to consider character boundary
truncate(&mut self, cursor: u8)134     pub unsafe fn truncate(&mut self, cursor: u8) {
135         if cursor < self.cursor {
136             self.set_len(cursor);
137         }
138     }
139 
140     #[inline]
141     ///Returns buffer overall capacity.
capacity() -> usize142     pub const fn capacity() -> usize {
143         mem::size_of::<S>()
144     }
145 
146     #[inline]
147     ///Returns number of bytes written.
len(&self) -> usize148     pub const fn len(&self) -> usize {
149         self.cursor as usize
150     }
151 
152     #[inline(always)]
153     ///Sets new length of the string.
set_len(&mut self, len: u8)154     pub unsafe fn set_len(&mut self, len: u8) {
155         self.cursor = len
156     }
157 
158     #[inline]
159     ///Appends given string without any size checks
push_str_unchecked(&mut self, text: &str)160     pub unsafe fn push_str_unchecked(&mut self, text: &str) {
161         ptr::copy_nonoverlapping(text.as_ptr(), self.as_ptr().offset(self.cursor as isize) as *mut u8, text.len());
162         self.set_len(self.cursor.saturating_add(text.len() as u8));
163     }
164 
165     #[inline]
166     ///Appends given string, truncating on overflow, returning number of written bytes
push_str(&mut self, text: &str) -> usize167     pub fn push_str(&mut self, text: &str) -> usize {
168         let size = cmp::min(text.len(), self.remaining());
169         unsafe {
170             self.push_str_unchecked(&text[..size]);
171         }
172         size
173     }
174 
175     #[inline(always)]
176     ///Access str from underlying storage
177     ///
178     ///Returns empty if nothing has been written into buffer yet.
as_str(&self) -> &str179     pub fn as_str(&self) -> &str {
180         unsafe {
181             let slice = core::slice::from_raw_parts(self.as_ptr(), self.len());
182             core::str::from_utf8_unchecked(slice)
183         }
184     }
185 }
186 
187 impl<S: Sized> AsRef<str> for StrBuf<S> {
188     #[inline(always)]
as_ref(&self) -> &str189     fn as_ref(&self) -> &str {
190         self.as_str()
191     }
192 }
193 
194 impl<S: Sized> core::fmt::Write for StrBuf<S> {
195     #[inline(always)]
write_str(&mut self, s: &str) -> core::fmt::Result196     fn write_str(&mut self, s: &str) -> core::fmt::Result {
197         if self.push_str(s) == s.len() {
198             Ok(())
199         } else {
200             Err(core::fmt::Error)
201         }
202     }
203 }
204 
205 impl<S: Sized> core::fmt::Display for StrBuf<S> {
206     #[inline(always)]
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result207     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
208         f.write_str(self.as_str())
209     }
210 }
211 
212 impl<S: Sized> core::fmt::Debug for StrBuf<S> {
213     #[inline(always)]
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result214     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
215         f.write_str(self.as_str())
216     }
217 }
218 
219 impl<S: Sized> Clone for StrBuf<S> {
220     #[inline]
clone(&self) -> Self221     fn clone(&self) -> Self {
222         let mut result = Self::new();
223         unsafe {
224             result.push_str_unchecked(self.as_str())
225         }
226         result
227     }
228 
229     #[inline]
clone_from(&mut self, source: &Self)230     fn clone_from(&mut self, source: &Self) {
231         self.clear();
232         unsafe {
233             self.push_str_unchecked(source.as_str());
234         }
235     }
236 }
237 
238 impl<S: Sized> AsRef<[u8]> for StrBuf<S> {
239     #[inline(always)]
as_ref(&self) -> &[u8]240     fn as_ref(&self) -> &[u8] {
241         self.as_slice()
242     }
243 }
244 
245 impl<S: Sized> AsMut<[u8]> for StrBuf<S> {
246     #[inline(always)]
as_mut(&mut self) -> &mut [u8]247     fn as_mut(&mut self) -> &mut [u8] {
248         self.as_mut_slice()
249     }
250 }
251 
252 impl<S: Sized> borrow::Borrow<str> for StrBuf<S> {
253     #[inline(always)]
borrow(&self) -> &str254     fn borrow(&self) -> &str {
255         self.as_str()
256     }
257 }
258 
259 impl<S: Sized> ops::Deref for StrBuf<S> {
260     type Target = str;
261 
262     #[inline(always)]
deref(&self) -> &str263     fn deref(&self) -> &str {
264         self.as_str()
265     }
266 }
267 
268 impl<S: Sized> Eq for StrBuf<S> {}
269 
270 impl<S: Sized> PartialEq<StrBuf<S>> for StrBuf<S> {
271     #[inline(always)]
eq(&self, other: &Self) -> bool272     fn eq(&self, other: &Self) -> bool {
273         self.as_str() == other.as_str()
274     }
275 }
276 
277 impl<S: Sized> PartialEq<StrBuf<S>> for &str {
278     #[inline(always)]
eq(&self, other: &StrBuf<S>) -> bool279     fn eq(&self, other: &StrBuf<S>) -> bool {
280         *self == other.as_str()
281     }
282 }
283 
284 
285 impl<S: Sized> PartialEq<StrBuf<S>> for str {
286     #[inline(always)]
eq(&self, other: &StrBuf<S>) -> bool287     fn eq(&self, other: &StrBuf<S>) -> bool {
288         self == other.as_str()
289     }
290 }
291 
292 impl<S: Sized> PartialEq<str> for StrBuf<S> {
293     #[inline(always)]
eq(&self, other: &str) -> bool294     fn eq(&self, other: &str) -> bool {
295         self.as_str() == other
296     }
297 }
298 
299 impl<S: Sized> PartialEq<&str> for StrBuf<S> {
300     #[inline(always)]
eq(&self, other: &&str) -> bool301     fn eq(&self, other: &&str) -> bool {
302         self.as_str() == *other
303     }
304 }
305 
306 impl<S: Sized> cmp::Ord for StrBuf<S> {
cmp(&self, other: &Self) -> cmp::Ordering307     fn cmp(&self, other: &Self) -> cmp::Ordering {
308         self.as_str().cmp(other.as_str())
309     }
310 }
311 
312 impl<S: Sized> PartialOrd for StrBuf<S> {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>313     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
314         Some(self.cmp(other))
315     }
316 }
317 
318 impl<S: Sized> hash::Hash for StrBuf<S> {
hash<H: hash::Hasher>(&self, hasher: &mut H)319     fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
320         self.as_str().hash(hasher)
321     }
322 }
323 
324 impl<S: Sized> From<&str> for StrBuf<S> {
325     #[inline(always)]
from(text: &str) -> Self326     fn from(text: &str) -> Self {
327         Self::from_str(text)
328     }
329 }
330