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