1use std::fmt;
2
3pub(crate) type Len = u8;
4
5#[derive(Copy, Clone)]
7pub struct StackString<const CAPACITY: usize> {
8 len: Len,
9 buffer: StrBuffer<CAPACITY>,
10}
11
12impl<const CAPACITY: usize> StackString<CAPACITY> {
13 pub const CAPACITY: usize = CAPACITY;
14 pub const EMPTY: Self = Self::empty();
15
16 const fn empty() -> Self {
17 Self {
18 len: 0,
19 buffer: StrBuffer::empty(),
20 }
21 }
22
23 #[inline]
36 #[must_use]
37 pub fn try_new(s: &str) -> Option<Self> {
38 let len = s.as_bytes().len();
39 if len <= Self::CAPACITY {
40 #[cfg(feature = "unsafe")]
41 let stack = {
42 unsafe {
43 Self::new_unchecked(s)
45 }
46 };
47 #[cfg(not(feature = "unsafe"))]
48 let stack = { Self::new(s) };
49 Some(stack)
50 } else {
51 None
52 }
53 }
54
55 #[inline]
70 #[must_use]
71 pub fn new(s: &str) -> Self {
72 let len = s.as_bytes().len() as u8;
73 debug_assert!(Self::CAPACITY <= Len::MAX.into());
74 let buffer = StrBuffer::new(s);
75 Self { len, buffer }
76 }
77
78 #[inline]
96 #[must_use]
97 #[cfg(feature = "unsafe")]
98 pub unsafe fn new_unchecked(s: &str) -> Self {
99 let len = s.as_bytes().len() as u8;
100 debug_assert!(Self::CAPACITY <= Len::MAX.into());
101 let buffer = StrBuffer::new_unchecked(s);
102 Self { len, buffer }
103 }
104
105 #[inline]
117 #[must_use]
118 pub fn as_str(&self) -> &str {
119 let len = self.len as usize;
120 #[cfg(feature = "unsafe")]
121 unsafe {
122 self.buffer.as_str_unchecked(len)
125 }
126 #[cfg(not(feature = "unsafe"))]
127 self.buffer.as_str(len)
128 }
129
130 #[inline]
145 #[must_use]
146 pub fn as_mut_str(&mut self) -> &mut str {
147 let len = self.len as usize;
148 #[cfg(feature = "unsafe")]
149 unsafe {
150 self.buffer.as_mut_str_unchecked(len)
153 }
154 #[cfg(not(feature = "unsafe"))]
155 self.buffer.as_mut_str(len)
156 }
157
158 #[inline]
175 #[must_use]
176 pub fn len(&self) -> usize {
177 self.len as usize
178 }
179
180 #[inline]
194 #[must_use]
195 pub fn is_empty(&self) -> bool {
196 self.len() == 0
197 }
198
199 #[inline]
214 pub fn clear(&mut self) {
215 self.len = 0;
216 }
217
218 #[inline]
242 pub fn truncate(&mut self, new_len: usize) {
243 if new_len <= self.len() {
244 assert!(self.is_char_boundary(new_len));
245 self.len = new_len as u8;
246 }
247 }
248}
249
250impl<const CAPACITY: usize> Default for StackString<CAPACITY> {
251 fn default() -> Self {
252 Self::empty()
253 }
254}
255
256impl<const CAPACITY: usize> std::ops::Deref for StackString<CAPACITY> {
257 type Target = str;
258
259 #[inline]
260 fn deref(&self) -> &str {
261 self.as_str()
262 }
263}
264
265impl<const CAPACITY: usize> Eq for StackString<CAPACITY> {}
266
267impl<const C1: usize, const C2: usize> PartialEq<StackString<C1>> for StackString<C2> {
268 #[inline]
269 fn eq(&self, other: &StackString<C1>) -> bool {
270 PartialEq::eq(self.as_str(), other.as_str())
271 }
272}
273
274impl<const CAPACITY: usize> PartialEq<str> for StackString<CAPACITY> {
275 #[inline]
276 fn eq(&self, other: &str) -> bool {
277 PartialEq::eq(self.as_str(), other)
278 }
279}
280
281impl<'s, const CAPACITY: usize> PartialEq<&'s str> for StackString<CAPACITY> {
282 #[inline]
283 fn eq(&self, other: &&str) -> bool {
284 PartialEq::eq(self.as_str(), *other)
285 }
286}
287
288impl<const CAPACITY: usize> PartialEq<String> for StackString<CAPACITY> {
289 #[inline]
290 fn eq(&self, other: &String) -> bool {
291 PartialEq::eq(self.as_str(), other.as_str())
292 }
293}
294
295impl<const CAPACITY: usize> Ord for StackString<CAPACITY> {
296 #[inline]
297 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
298 self.as_str().cmp(other.as_str())
299 }
300}
301
302impl<const C1: usize, const C2: usize> PartialOrd<StackString<C1>> for StackString<C2> {
303 #[inline]
304 fn partial_cmp(&self, other: &StackString<C1>) -> Option<std::cmp::Ordering> {
305 self.as_str().partial_cmp(other.as_str())
306 }
307}
308
309impl<const CAPACITY: usize> PartialOrd<str> for StackString<CAPACITY> {
310 #[inline]
311 fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
312 self.as_str().partial_cmp(other)
313 }
314}
315
316impl<'s, const CAPACITY: usize> PartialOrd<&'s str> for StackString<CAPACITY> {
317 #[inline]
318 fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
319 self.as_str().partial_cmp(other)
320 }
321}
322
323impl<const CAPACITY: usize> PartialOrd<String> for StackString<CAPACITY> {
324 #[inline]
325 fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
326 self.as_str().partial_cmp(other.as_str())
327 }
328}
329
330impl<const CAPACITY: usize> std::hash::Hash for StackString<CAPACITY> {
331 #[inline]
332 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
333 self.as_str().hash(state);
334 }
335}
336
337impl<const CAPACITY: usize> fmt::Debug for StackString<CAPACITY> {
338 #[inline]
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 fmt::Debug::fmt(self.as_str(), f)
341 }
342}
343
344impl<const CAPACITY: usize> fmt::Display for StackString<CAPACITY> {
345 #[inline]
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 fmt::Display::fmt(self.as_str(), f)
348 }
349}
350
351impl<const CAPACITY: usize> AsRef<str> for StackString<CAPACITY> {
352 #[inline]
353 fn as_ref(&self) -> &str {
354 self.as_str()
355 }
356}
357
358impl<const CAPACITY: usize> AsRef<[u8]> for StackString<CAPACITY> {
359 #[inline]
360 fn as_ref(&self) -> &[u8] {
361 self.as_bytes()
362 }
363}
364
365impl<const CAPACITY: usize> AsRef<std::ffi::OsStr> for StackString<CAPACITY> {
366 #[inline]
367 fn as_ref(&self) -> &std::ffi::OsStr {
368 (**self).as_ref()
369 }
370}
371
372impl<const CAPACITY: usize> AsRef<std::path::Path> for StackString<CAPACITY> {
373 #[inline]
374 fn as_ref(&self) -> &std::path::Path {
375 std::path::Path::new(self)
376 }
377}
378
379impl<const CAPACITY: usize> std::borrow::Borrow<str> for StackString<CAPACITY> {
380 #[inline]
381 fn borrow(&self) -> &str {
382 self.as_str()
383 }
384}
385
386#[derive(Copy, Clone)]
387#[repr(transparent)]
388pub(crate) struct StrBuffer<const CAPACITY: usize>([u8; CAPACITY]);
389
390impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
391 pub(crate) const fn empty() -> Self {
392 let array = [0; CAPACITY];
393 StrBuffer(array)
394 }
395
396 #[inline]
397 pub(crate) fn new(s: &str) -> Self {
398 let len = s.as_bytes().len();
399 debug_assert!(len <= CAPACITY);
400 let mut buffer = Self::default();
401 if let Some(buffer) = buffer.0.get_mut(..len) {
402 buffer.copy_from_slice(s.as_bytes());
403 } else {
404 panic!("`{}` is larger than capacity {}", s, CAPACITY);
405 }
406 buffer
407 }
408
409 #[inline]
410 #[cfg(not(feature = "unsafe"))]
411 pub(crate) fn as_str(&self, len: usize) -> &str {
412 let slice = self.0.get(..len).unwrap();
413 std::str::from_utf8(slice).unwrap()
414 }
415
416 #[inline]
417 #[cfg(not(feature = "unsafe"))]
418 pub(crate) fn as_mut_str(&mut self, len: usize) -> &mut str {
419 let slice = self.0.get_mut(..len).unwrap();
420 std::str::from_utf8_mut(slice).unwrap()
421 }
422}
423
424impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
425 #[inline]
426 #[cfg(feature = "unsafe")]
427 pub(crate) unsafe fn new_unchecked(s: &str) -> Self {
428 let len = s.as_bytes().len();
429 debug_assert!(len <= CAPACITY);
430 let mut buffer = Self::default();
431 buffer
432 .0
433 .get_unchecked_mut(..len)
434 .copy_from_slice(s.as_bytes());
435 buffer
436 }
437
438 #[inline]
439 #[cfg(feature = "unsafe")]
440 pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str {
441 let slice = self.0.get_unchecked(..len);
442 std::str::from_utf8_unchecked(slice)
443 }
444
445 #[inline]
446 #[cfg(feature = "unsafe")]
447 pub(crate) unsafe fn as_mut_str_unchecked(&mut self, len: usize) -> &mut str {
448 let slice = self.0.get_unchecked_mut(..len);
449 std::str::from_utf8_unchecked_mut(slice)
450 }
451}
452
453impl<const CAPACITY: usize> Default for StrBuffer<CAPACITY> {
454 fn default() -> Self {
455 Self::empty()
456 }
457}