1#[cfg(feature = "alloc")]
6pub(crate) mod crc32;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct MaybeStaticSlice<T: 'static> {
24 kind: MaybeStaticSliceKind<T>,
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
28enum MaybeStaticSliceKind<T: 'static> {
29 Static(&'static [T]),
30 #[cfg(feature = "alloc")]
31 Heap(alloc::boxed::Box<[T]>),
32}
33
34impl<T: 'static> MaybeStaticSlice<T> {
35 #[inline]
37 pub const fn statik(data: &'static [T]) -> MaybeStaticSlice<T> {
38 let kind = MaybeStaticSliceKind::Static(data);
39 MaybeStaticSlice { kind }
40 }
41
42 #[cfg(feature = "alloc")]
44 #[inline]
45 pub const fn heap(data: alloc::boxed::Box<[T]>) -> MaybeStaticSlice<T> {
46 let kind = MaybeStaticSliceKind::Heap(data);
47 MaybeStaticSlice { kind }
48 }
49
50 #[inline]
52 pub const fn as_slice(&self) -> &[T] {
53 match self.kind {
54 MaybeStaticSliceKind::Static(slice) => slice,
55 #[cfg(feature = "alloc")]
56 MaybeStaticSliceKind::Heap(ref slice) => slice,
57 }
58 }
59}
60
61impl<T: 'static> From<&'static [T]> for MaybeStaticSlice<T> {
62 #[inline]
63 fn from(data: &'static [T]) -> MaybeStaticSlice<T> {
64 MaybeStaticSlice::statik(data)
65 }
66}
67
68#[cfg(feature = "alloc")]
69impl<T: 'static> From<alloc::boxed::Box<[T]>> for MaybeStaticSlice<T> {
70 #[inline]
71 fn from(data: alloc::boxed::Box<[T]>) -> MaybeStaticSlice<T> {
72 MaybeStaticSlice::heap(data)
73 }
74}
75
76impl<T: 'static> core::ops::Deref for MaybeStaticSlice<T> {
77 type Target = [T];
78
79 #[inline]
80 fn deref(&self) -> &[T] {
81 self.as_slice()
82 }
83}
84
85#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
101pub struct SmallStr<const ARRAY_CAPACITY_MAX: usize> {
102 kind: SmallStrKind<ARRAY_CAPACITY_MAX>,
103}
104
105#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
106enum SmallStrKind<const ARRAY_CAPACITY_MAX: usize> {
107 Array(ArrayStr<ARRAY_CAPACITY_MAX>),
108 Static(&'static str),
111 #[cfg(feature = "alloc")]
112 Heap(alloc::boxed::Box<str>),
113}
114
115impl<const ARRAY_CAPACITY_MAX: usize> SmallStr<ARRAY_CAPACITY_MAX> {
116 #[inline]
122 pub fn new(s: &str) -> Option<SmallStr<ARRAY_CAPACITY_MAX>> {
123 SmallStr::try_array(s).or_else(|| {
124 #[cfg(not(feature = "alloc"))]
125 {
126 None
127 }
128 #[cfg(feature = "alloc")]
129 {
130 Some(SmallStr::from(alloc::boxed::Box::<str>::from(s)))
131 }
132 })
133 }
134
135 #[cfg(feature = "alloc")]
140 #[inline]
141 pub fn new_or_heap(s: &str) -> SmallStr<ARRAY_CAPACITY_MAX> {
142 SmallStr::try_array(s).unwrap_or_else(|| {
143 SmallStr::from(alloc::boxed::Box::<str>::from(s))
144 })
145 }
146
147 #[inline]
154 pub const fn try_array(s: &str) -> Option<SmallStr<ARRAY_CAPACITY_MAX>> {
155 let Some(astr) = ArrayStr::new(s) else { return None };
156 let kind = SmallStrKind::Array(astr);
157 Some(SmallStr { kind })
158 }
159
160 #[inline]
169 pub const fn array(s: &str) -> SmallStr<ARRAY_CAPACITY_MAX> {
170 let Some(astr) = ArrayStr::new(s) else { panic!("string too big") };
173 let kind = SmallStrKind::Array(astr);
174 SmallStr { kind }
175 }
176
177 #[inline]
181 pub const fn statik(s: &'static str) -> SmallStr<ARRAY_CAPACITY_MAX> {
182 let kind = SmallStrKind::Static(s);
183 SmallStr { kind }
184 }
185
186 #[inline]
188 pub const fn array_capacity_max() -> usize {
189 ARRAY_CAPACITY_MAX
190 }
191
192 #[inline]
194 pub const fn as_str(&self) -> &str {
195 match self.kind {
196 SmallStrKind::Array(ref astr) => astr.as_str(),
197 SmallStrKind::Static(s) => s,
198 #[cfg(feature = "alloc")]
199 SmallStrKind::Heap(ref s) => s,
200 }
201 }
202}
203
204impl<const ARRAY_CAPACITY_MAX: usize> From<ArrayStr<ARRAY_CAPACITY_MAX>>
206 for SmallStr<ARRAY_CAPACITY_MAX>
207{
208 #[inline]
209 fn from(s: ArrayStr<ARRAY_CAPACITY_MAX>) -> SmallStr<ARRAY_CAPACITY_MAX> {
210 let kind = SmallStrKind::Array(s);
211 SmallStr { kind }
212 }
213}
214
215impl<const ARRAY_CAPACITY_MAX: usize> From<&'static str>
217 for SmallStr<ARRAY_CAPACITY_MAX>
218{
219 #[inline]
220 fn from(s: &'static str) -> SmallStr<ARRAY_CAPACITY_MAX> {
221 SmallStr::statik(s)
222 }
223}
224
225#[cfg(feature = "alloc")]
227impl<const ARRAY_CAPACITY_MAX: usize> From<alloc::boxed::Box<str>>
228 for SmallStr<ARRAY_CAPACITY_MAX>
229{
230 #[inline]
231 fn from(s: alloc::boxed::Box<str>) -> SmallStr<ARRAY_CAPACITY_MAX> {
232 let kind = SmallStrKind::Heap(s);
233 SmallStr { kind }
234 }
235}
236
237impl<const ARRAY_CAPACITY_MAX: usize> core::ops::Deref
238 for SmallStr<ARRAY_CAPACITY_MAX>
239{
240 type Target = str;
241
242 #[inline]
243 fn deref(&self) -> &str {
244 SmallStr::<ARRAY_CAPACITY_MAX>::as_str(self)
245 }
246}
247
248impl<const ARRAY_CAPACITY_MAX: usize> AsRef<str>
249 for SmallStr<ARRAY_CAPACITY_MAX>
250{
251 #[inline]
252 fn as_ref(&self) -> &str {
253 self.as_str()
254 }
255}
256
257impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<str>
258 for SmallStr<ARRAY_CAPACITY_MAX>
259{
260 #[inline]
261 fn eq(&self, rhs: &str) -> bool {
262 self.as_str() == rhs
263 }
264}
265
266impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<&str>
267 for SmallStr<ARRAY_CAPACITY_MAX>
268{
269 #[inline]
270 fn eq(&self, rhs: &&str) -> bool {
271 self.as_str() == *rhs
272 }
273}
274
275impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<SmallStr<ARRAY_CAPACITY_MAX>>
276 for str
277{
278 #[inline]
279 fn eq(&self, rhs: &SmallStr<ARRAY_CAPACITY_MAX>) -> bool {
280 self == rhs.as_str()
281 }
282}
283
284impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<str>
285 for SmallStr<ARRAY_CAPACITY_MAX>
286{
287 #[inline]
288 fn partial_cmp(&self, rhs: &str) -> Option<core::cmp::Ordering> {
289 self.as_str().partial_cmp(rhs)
290 }
291}
292
293impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<&str>
294 for SmallStr<ARRAY_CAPACITY_MAX>
295{
296 #[inline]
297 fn partial_cmp(&self, rhs: &&str) -> Option<core::cmp::Ordering> {
298 self.as_str().partial_cmp(*rhs)
299 }
300}
301
302impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<SmallStr<ARRAY_CAPACITY_MAX>>
303 for str
304{
305 #[inline]
306 fn partial_cmp(
307 &self,
308 rhs: &SmallStr<ARRAY_CAPACITY_MAX>,
309 ) -> Option<core::cmp::Ordering> {
310 self.partial_cmp(rhs.as_str())
311 }
312}
313
314impl<const ARRAY_CAPACITY_MAX: usize> core::fmt::Debug
315 for SmallStr<ARRAY_CAPACITY_MAX>
316{
317 #[inline]
318 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
319 core::fmt::Debug::fmt(self.as_str(), f)
320 }
321}
322
323impl<const ARRAY_CAPACITY_MAX: usize> core::fmt::Display
324 for SmallStr<ARRAY_CAPACITY_MAX>
325{
326 #[inline]
327 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
328 core::fmt::Display::fmt(self.as_str(), f)
329 }
330}
331
332#[cfg(feature = "defmt")]
333impl<const ARRAY_CAPACITY_MAX: usize> defmt::Format
334 for SmallStr<ARRAY_CAPACITY_MAX>
335{
336 #[inline]
337 fn format(&self, f: defmt::Formatter) {
338 defmt::write!(f, "{=str}", self.as_str())
339 }
340}
341
342#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
353pub struct ArrayStr<const N: usize> {
354 len: u8,
365 bytes: [u8; N],
370}
371
372impl<const N: usize> ArrayStr<N> {
373 #[inline]
378 pub const fn new(s: &str) -> Option<ArrayStr<N>> {
379 let len = s.len();
380 if len > N {
381 return None;
382 }
383 let mut bytes = [0; N];
384 let mut i = 0;
385 while i < s.as_bytes().len() {
386 bytes[i] = s.as_bytes()[i];
387 i += 1;
388 }
389 debug_assert!(N <= u8::MAX as usize, "size of ArrayStr is too big");
393 Some(ArrayStr { len: len as u8, bytes })
394 }
395
396 #[inline]
398 pub const fn capacity() -> usize {
399 N
400 }
401
402 #[inline]
408 pub fn push_str(&mut self, s: &str) -> bool {
409 let len = self.len as usize;
410 let Some(new_len) = len.checked_add(s.len()) else { return false };
411 if new_len > N {
412 return false;
413 }
414
415 let mut i = len;
416 while i < new_len {
417 self.bytes[i] = s.as_bytes()[i - len];
418 i += 1;
419 }
420 debug_assert!(N <= u8::MAX as usize, "size of ArrayStr is too big");
424 self.len = new_len as u8;
425 true
426 }
427
428 #[inline]
430 pub const fn as_str(&self) -> &str {
431 unsafe {
441 core::str::from_utf8_unchecked(core::slice::from_raw_parts(
442 self.bytes.as_ptr(),
443 self.len as usize,
444 ))
445 }
446 }
447}
448
449impl<const N: usize> core::ops::Deref for ArrayStr<N> {
450 type Target = str;
451
452 #[inline]
453 fn deref(&self) -> &str {
454 ArrayStr::<N>::as_str(self)
455 }
456}
457
458impl<const N: usize> AsRef<str> for ArrayStr<N> {
459 #[inline]
460 fn as_ref(&self) -> &str {
461 self.as_str()
462 }
463}
464
465impl<const N: usize> PartialEq<str> for ArrayStr<N> {
466 #[inline]
467 fn eq(&self, rhs: &str) -> bool {
468 self.as_str() == rhs
469 }
470}
471
472impl<const N: usize> PartialEq<&str> for ArrayStr<N> {
473 #[inline]
474 fn eq(&self, rhs: &&str) -> bool {
475 self.as_str() == *rhs
476 }
477}
478
479impl<const N: usize> PartialEq<ArrayStr<N>> for str {
480 #[inline]
481 fn eq(&self, rhs: &ArrayStr<N>) -> bool {
482 self == rhs.as_str()
483 }
484}
485
486impl<const N: usize> PartialOrd<str> for ArrayStr<N> {
487 #[inline]
488 fn partial_cmp(&self, rhs: &str) -> Option<core::cmp::Ordering> {
489 self.as_str().partial_cmp(rhs)
490 }
491}
492
493impl<const N: usize> PartialOrd<&str> for ArrayStr<N> {
494 #[inline]
495 fn partial_cmp(&self, rhs: &&str) -> Option<core::cmp::Ordering> {
496 self.as_str().partial_cmp(*rhs)
497 }
498}
499
500impl<const N: usize> PartialOrd<ArrayStr<N>> for str {
501 #[inline]
502 fn partial_cmp(&self, rhs: &ArrayStr<N>) -> Option<core::cmp::Ordering> {
503 self.partial_cmp(rhs.as_str())
504 }
505}
506
507impl<const N: usize> core::fmt::Debug for ArrayStr<N> {
508 #[inline]
509 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
510 core::fmt::Debug::fmt(self.as_str(), f)
511 }
512}
513
514impl<const N: usize> core::fmt::Display for ArrayStr<N> {
515 #[inline]
516 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
517 core::fmt::Display::fmt(self.as_str(), f)
518 }
519}
520
521impl<const N: usize> core::fmt::Write for ArrayStr<N> {
522 #[inline]
523 fn write_str(&mut self, s: &str) -> core::fmt::Result {
524 if self.push_str(s) {
525 Ok(())
526 } else {
527 Err(core::fmt::Error)
528 }
529 }
530}
531
532#[cfg(feature = "defmt")]
533impl<const N: usize> defmt::Format for ArrayStr<N> {
534 #[inline]
535 fn format(&self, f: defmt::Formatter) {
536 defmt::write!(f, "{=str}", self.as_str())
537 }
538}
539
540#[cfg(feature = "std")]
547pub(crate) fn os_str_bytes<'o, O>(os_str: &'o O) -> Option<&'o [u8]>
548where
549 O: ?Sized + AsRef<std::ffi::OsStr>,
550{
551 let os_str = os_str.as_ref();
552 #[cfg(unix)]
553 {
554 use std::os::unix::ffi::OsStrExt;
555 Some(os_str.as_bytes())
556 }
557 #[cfg(not(unix))]
558 {
559 os_str.to_str().map(|s| s.as_bytes())
566 }
567}
568
569#[cfg(test)]
570mod tests {
571 use core::fmt::Write;
572
573 use super::*;
574
575 #[test]
576 fn fmt_write() {
577 let mut dst = ArrayStr::<5>::new("").unwrap();
578 assert!(write!(&mut dst, "abcd").is_ok());
579 assert!(write!(&mut dst, "e").is_ok());
580 assert!(write!(&mut dst, "f").is_err());
581 }
582
583 #[test]
584 fn array_str_size() {
585 assert_eq!(7, core::mem::size_of::<ArrayStr::<6>>());
586 }
587}