spacetimedb_data_structures/
slim_slice.rs

1//! Defines slimmer versions of slices, both shared, mutable, and owned.
2//!
3//! They are slimmer in the sense that whereas e.g.,
4//! `size_of::<Box<[T]>>() == 16`, on a 64-bit machine,
5//! a `SlimSliceBox<T>` only takes up 12 bytes.
6//! These 4 bytes in difference can help
7//! when these types are stored in enum variants
8//! due to alignment and the space needed for storing tags.
9//!
10//! The difference size (4 bytes), is due to storing the length as a `u32`
11//! rather than storing the length as a `usize` (`u64` on 64-bit machine).
12//! This implies that the length can be at most `u32::MAX`,
13//! so no more elements than that can be stored or pointed to with these types.
14//!
15//! Because hitting `u32::MAX` is substantially more likely than `u64::MAX`,
16//! the risk of overflow is greater.
17//! To mitigate this issue, rather than default to panicing,
18//! this module tries, for the most part,
19//! to force its user to handle any overflow
20//! when converting to the slimmer types.
21//!
22//! The slimmer types include:
23//!
24//! - [`SlimSliceBox<T>`], a slimmer version of `Box<[T]>`
25//! - [`SlimSmallSliceBox<T, N>`], a slimmer version of `SmallVec<[T; N]>`
26//!   but without the growing functionality.
27//! - [`SlimStrBox`], a slimmer version of `Box<str>`
28//! - [`SlimSlice<'a, T>`], a slimmer verion of `&'a [T]`
29//! - [`SlimSliceMut<'a, T>`], a slimmer version of `&'a mut [T]`
30//! - [`SlimStr<'a>`], a slimmer version of `&'a str`
31//! - [`SlimStrMut<'a>`], a slimmer version of `&'a mut str`
32//!
33//! The following convenience conversion functions are provided:
34//!
35//! - [`from_slice`] converts `&[T] -> SlimSlice<T>`, panicing on overflow
36//! - [`from_slice_mut`] converts `&mut [T] -> SlimSliceMut<T>`, panicing on overflow
37//! - [`from_str`] converts `&str -> SlimStr`, panicing on overflow
38//! - [`from_str_mut`] converts `&mut str -> SlimStrMut`, panicing on overflow
39//! - [`from_string`] converts `&str -> SlimStrBox`, panicing on overflow
40//!
41//! These conversions should be reserved for cases where it is known
42//! that the length `<= u32::MAX` and should be used sparingly.
43//!
44//! Some auxiliary and utility functionality is provided:
45//!
46//! - [`SlimSliceBoxCollected<T>`] exists to indirectly provide `FromIterator<A>`
47//!   for [`SlimSliceBox<T>`]
48//!
49//! - [`LenTooLong<T>`], the error type when a conversion to a slimmer type
50//!   would result in a length overflow.
51//!   Optionally, the to-convert object is provided back to the user
52//!   for handling
53//!
54//! - [`try_into`] tries to convert the input to a slim type
55//!   and forgets the input if an error occurred
56//!
57//! - [`SafelyExchangeable<T>`] is implemented to assert that `Self`
58//!   is safely transmutable, including when stored in a collection, to type `T`
59
60use core::{
61    borrow::Borrow,
62    cmp::Ordering,
63    fmt::{Debug, Display},
64    hash::{Hash, Hasher},
65    marker::PhantomData,
66    mem,
67    mem::ManuallyDrop,
68    ops::{Deref, DerefMut},
69    ptr::{slice_from_raw_parts_mut, NonNull},
70    slice,
71    str::{from_utf8_unchecked, from_utf8_unchecked_mut},
72};
73use thiserror::Error;
74
75// =============================================================================
76// Errors
77// =============================================================================
78
79/// An error signifying that a container's size was over `u32::MAX`.
80///
81/// Optionally, the to-convert object is provided back to the user for handling.
82/// This is what the generic parameter `T` is for.
83#[derive(Error, Debug)]
84#[error("The length `{len}` was too long")]
85pub struct LenTooLong<T = ()> {
86    /// The size of the container that was too large.
87    pub len: usize,
88    /// The container that was too large for into-slim conversion.
89    pub too_long: T,
90}
91
92impl<T> LenTooLong<T> {
93    /// Forgets the container part of the error.
94    pub fn forget(self) -> LenTooLong {
95        self.map(drop)
96    }
97
98    /// Maps the container part of the error.
99    pub fn map<U>(self, with: impl FnOnce(T) -> U) -> LenTooLong<U> {
100        LenTooLong {
101            len: self.len,
102            too_long: with(self.too_long),
103        }
104    }
105}
106
107/// Try to convert `x` into `B` and forget the container part of the error if any.
108#[inline]
109pub fn try_into<A, B: TryFrom<A, Error = LenTooLong<A>>>(x: A) -> Result<B, LenTooLong> {
110    x.try_into().map_err(|e: LenTooLong<A>| e.forget())
111}
112
113// =============================================================================
114// Utils
115// =============================================================================
116
117/// Ensures that `$thing.len() <= u32::MAX`.
118macro_rules! ensure_len_fits {
119    ($thing:expr) => {
120        let Ok(_) = u32::try_from($thing.len()) else {
121            return Err(LenTooLong {
122                len: $thing.len(),
123                too_long: $thing,
124            });
125        };
126    };
127}
128
129/// Convert to `A` but panic if `x.len() > u32::MAX`.
130#[inline]
131fn expect_fit<A, E, B: TryInto<A, Error = LenTooLong<E>>>(x: B) -> A {
132    x.try_into().map_err(|e| e.len).expect("length didn't fit in `u32`")
133}
134
135#[inline]
136fn into_box<T, U: Into<Box<[T]>>>(x: U) -> Box<[T]> {
137    x.into()
138}
139
140// Asserts, in the type system, that `N <= u32::MAX`.
141struct AssertU32<const N: usize>;
142impl<const N: usize> AssertU32<N> {
143    const OK: () = assert!(N <= u32::MAX as usize);
144}
145
146// =============================================================================
147// Raw slice utility type
148// =============================================================================
149
150/// Implementors decree that `Self` can be *safely* transmuted to `T`,
151/// including covariantly under a pointer.
152///
153/// # Safety
154///
155/// It is not sufficient that ´Self` and `T` have the same representation.
156/// That is, validity requirements are not enough.
157/// The safety requirements must also be the same.
158pub unsafe trait SafelyExchangeable<T> {}
159
160/// Implementation detail of the other types.
161/// Provides some convenience but users of the type are responsible
162/// for safety, invariants and variance.
163#[repr(packed)]
164struct SlimRawSlice<T> {
165    /// A valid pointer to the slice data.
166    ptr: NonNull<T>,
167    /// The length of the slice.
168    len: u32,
169}
170
171impl<T> SlimRawSlice<T> {
172    /// Returns a dangling slim raw slice.
173    #[inline]
174    fn dangling() -> Self {
175        let ptr = NonNull::dangling();
176        Self { len: 0, ptr }
177    }
178
179    /// Casts this raw slice `SlimRawSlice<T>` to `SlimRawSlice<U>`.
180    ///
181    /// That is, a cast from elements of `T` to elements of `U`.
182    /// The caller has ensured by `U: SafelyExchangeable<T>`
183    /// that `T` is safely exchangeable for `U`.
184    #[inline]
185    fn cast<U: SafelyExchangeable<T>>(self) -> SlimRawSlice<U> {
186        SlimRawSlice {
187            ptr: self.ptr.cast(),
188            len: self.len,
189        }
190    }
191
192    /// Split `self` into a raw pointer and the slice length.
193    #[inline]
194    fn split(self) -> (*mut T, usize) {
195        (self.ptr.as_ptr(), self.len as usize)
196    }
197
198    /// Dereferences this raw slice into a shared slice.
199    ///
200    /// SAFETY: `self.ptr` and `self.len`
201    /// must satisfy [`std::slice::from_raw_parts`]'s requirements.
202    /// That is,
203    /// * `self.ptr` must be valid for reads
204    ///    for `self.len * size_of::<T>` many bytes and must be aligned.
205    ///
206    /// * `self.ptr` must point to `self.len`
207    ///    consecutive properly initialized values of type `T`.
208    ///
209    /// * The memory referenced by the returned slice
210    ///   must not be mutated for the duration of lifetime `'a`,
211    ///   except inside an `UnsafeCell`.
212    ///
213    /// * The total size `self.len * mem::size_of::<T>()`
214    ///   of the slice must be no larger than `isize::MAX`,
215    ///   and adding that size to `data`
216    ///   must not "wrap around" the address space.
217    #[allow(clippy::needless_lifetimes)]
218    #[inline]
219    unsafe fn deref<'a>(&'a self) -> &'a [T] {
220        let (ptr, len) = self.split();
221        // SAFETY: caller is responsible for these.
222        unsafe { slice::from_raw_parts(ptr, len) }
223    }
224
225    /// Dereferences this raw slice into a mutable slice.
226    ///
227    /// SAFETY: `self.ptr` and `self.len`
228    /// must satisfy [`std::slice::from_raw_parts_mut`]'s requirements.
229    /// That is,
230    /// * `self.ptr` must be [valid] for both reads and writes
231    ///    for `self.len * mem::size_of::<T>()` many bytes,
232    ///   and it must be properly aligned.
233    ///
234    /// * `self.ptr` must point to `self.len`
235    ///   consecutive properly initialized values of type `T`.
236    ///
237    /// * The memory referenced by the returned slice
238    ///   must not be accessed through any other pointer
239    ///   (not derived from the return value) for the duration of lifetime `'a`.
240    ///   Both read and write accesses are forbidden.
241    ///
242    /// * The total size `self.len * mem::size_of::<T>()`
243    ///   of the slice must be no larger than `isize::MAX`,
244    ///   and adding that size to `data` must not "wrap around" the address space.
245    #[allow(clippy::needless_lifetimes)]
246    #[inline]
247    unsafe fn deref_mut<'a>(&'a mut self) -> &'a mut [T] {
248        let (ptr, len) = self.split();
249        // SAFETY: caller is responsible for these.
250        unsafe { slice::from_raw_parts_mut(ptr, len) }
251    }
252
253    /// Creates the raw slice from a pointer to the data and a length.
254    ///
255    /// It is assumed that `len <= u32::MAX`.
256    /// The caller must ensure that `ptr != NULL`.
257    #[inline]
258    const unsafe fn from_len_ptr(len: usize, ptr: *mut T) -> Self {
259        // SAFETY: caller ensured that `!ptr.is_null()`.
260        let ptr = NonNull::new_unchecked(ptr);
261        let len = len as u32;
262        Self { ptr, len }
263    }
264}
265
266impl<T> Copy for SlimRawSlice<T> {}
267impl<T> Clone for SlimRawSlice<T> {
268    #[inline]
269    fn clone(&self) -> Self {
270        *self
271    }
272}
273
274// =============================================================================
275// Owned boxed slice
276// =============================================================================
277
278pub use slim_slice_box::*;
279mod slim_slice_box {
280    // ^-- In the interest of soundness,
281    // this module exists to limit access to the private fields
282    // of `SlimSliceBox<T>` to a few key functions.
283    use super::*;
284
285    /// Provides a slimmer version of `Box<[T]>`
286    /// using `u32` for its length instead of `usize`.
287    #[repr(transparent)]
288    pub struct SlimSliceBox<T> {
289        /// The representation of this boxed slice.
290        ///
291        /// To convert to a `SlimSliceBox<T>` we must first have a `Box<[T]>`.
292        raw: SlimRawSlice<T>,
293        /// Marker to ensure covariance and dropck ownership.
294        owned: PhantomData<T>,
295    }
296
297    impl<T> Drop for SlimSliceBox<T> {
298        #[inline]
299        fn drop(&mut self) {
300            // Get us an owned `SlimSliceBox<T>`
301            // by replacing `self` with garbage that won't be dropped
302            // as the drop glue for the constituent fields does nothing.
303            let raw = SlimRawSlice::dangling();
304            let owned = PhantomData;
305            let this = mem::replace(self, Self { raw, owned });
306
307            // Convert into `Box<[T]>` and let it deal with dropping.
308            drop(into_box(this));
309        }
310    }
311
312    impl<T> SlimSliceBox<T> {
313        /// Converts `boxed` to `Self` without checking `boxed.len() <= u32::MAX`.
314        ///
315        /// # Safety
316        ///
317        /// The caller must ensure that `boxed.len() <= u32::MAX`.
318        #[allow(clippy::boxed_local)]
319        #[inline]
320        // Clippy doesn't seem to consider unsafe code here.
321        pub unsafe fn from_boxed_unchecked(boxed: Box<[T]>) -> Self {
322            let len = boxed.len();
323            let ptr = Box::into_raw(boxed) as *mut T;
324            // SAFETY: `Box<T>`'s ptr was a `NonNull<T>` already.
325            // and our caller has promised that `boxed.len() <= u32::MAX`.
326            let raw = SlimRawSlice::from_len_ptr(len, ptr);
327            let owned = PhantomData;
328            Self { raw, owned }
329        }
330
331        /// Returns a limited shared slice to this boxed slice.
332        #[allow(clippy::needless_lifetimes)]
333        #[inline]
334        pub fn shared_ref<'a>(&'a self) -> &'a SlimSlice<'a, T> {
335            // SAFETY: The reference lives as long as `self`.
336            // By virtue of `repr(transparent)` we're also allowed these reference casts.
337            unsafe { mem::transmute(self) }
338        }
339
340        /// Returns a limited mutable slice to this boxed slice.
341        #[allow(clippy::needless_lifetimes)]
342        #[inline]
343        pub fn exclusive_ref<'a>(&'a mut self) -> &'a mut SlimSliceMut<'a, T> {
344            // SAFETY: The reference lives as long as `self`
345            // and we have exclusive access to the heap data thanks to `&'a mut self`.
346            // By virtue of `repr(transparent)` we're also allowed these reference casts.
347            unsafe { mem::transmute(self) }
348        }
349
350        /// Map every element `x: T` to `U` by transmuting.
351        ///
352        /// This will not reallocate.
353        #[inline]
354        pub fn map_safely_exchangeable<U: SafelyExchangeable<T>>(self) -> SlimSliceBox<U> {
355            // SAFETY: By `U: SafelyExchangeable<T>`,
356            // the caller has proven that we can exchange `T -> U`
357            // even under an owned covariant pointer.
358            SlimSliceBox {
359                raw: self.raw.cast(),
360                owned: PhantomData,
361            }
362        }
363    }
364
365    impl<T> From<SlimSliceBox<T>> for Box<[T]> {
366        #[inline]
367        fn from(slice: SlimSliceBox<T>) -> Self {
368            let slice = ManuallyDrop::new(slice);
369            let (ptr, len) = slice.raw.split();
370            // SAFETY: All paths to creating a `SlimSliceBox`
371            // go through `SlimSliceBox::from_boxed_unchecked`
372            // which requires a valid `Box<[T]>`.
373            // The function also uses `Box::into_raw`
374            // and the original length is kept.
375            //
376            // It therefore follows that if we reuse the same
377            // pointer and length as given to us by a valid `Box<[T]>`,
378            // we can use `Box::from_raw` to reconstruct the `Box<[T]>`.
379            //
380            // We also no longer claim ownership of the data pointed to by `ptr`
381            // by virtue of `ManuallyDrop` preventing `Drop for SlimSliceBox<T>`.
382            unsafe { Box::from_raw(slice_from_raw_parts_mut(ptr, len)) }
383        }
384    }
385}
386
387/// `SlimSliceBox<T>` is `Send` if `T` is `Send` because the data is owned.
388unsafe impl<T: Send> Send for SlimSliceBox<T> {}
389
390/// `SlimSliceBox<T>` is `Sync` if `T` is `Sync` because the data is owned.
391unsafe impl<T: Sync> Sync for SlimSliceBox<T> {}
392
393impl<T> Deref for SlimSliceBox<T> {
394    type Target = [T];
395
396    #[inline]
397    fn deref(&self) -> &Self::Target {
398        self.shared_ref().deref()
399    }
400}
401
402impl<T> DerefMut for SlimSliceBox<T> {
403    #[inline]
404    fn deref_mut(&mut self) -> &mut Self::Target {
405        self.exclusive_ref().deref_mut()
406    }
407}
408
409impl<T> SlimSliceBox<T> {
410    /// Converts `boxed: Box<[T]>` into `SlimSliceBox<T>`.
411    ///
412    /// Panics when `boxed.len() > u32::MAX`.
413    #[inline]
414    pub fn from_boxed(boxed: Box<[T]>) -> Self {
415        expect_fit(boxed)
416    }
417
418    /// Converts `vec: Vec<T>` into `SlimSliceBox<T>`.
419    ///
420    /// Panics when `vec.len() > u32::MAX`.
421    #[inline]
422    pub fn from_vec(vec: Vec<T>) -> Self {
423        Self::from_boxed(vec.into())
424    }
425
426    /// Maps all elements with `by` from `T` to `U`.
427    ///
428    /// Will allocate once for the new boxed slice.
429    #[inline]
430    pub fn map<U>(self, by: impl FnMut(T) -> U) -> SlimSliceBox<U> {
431        let mapped = self.into_iter().map(by).collect::<Box<_>>();
432        // SAFETY: Doing `.map(..)` can never change the length.
433        unsafe { SlimSliceBox::from_boxed_unchecked(mapped) }
434    }
435
436    /// Maps all elements with `by` from `&T` to `U`.
437    ///
438    /// Will allocate once for the new boxed slice.
439    #[inline]
440    pub fn map_borrowed<U>(&self, by: impl FnMut(&T) -> U) -> SlimSliceBox<U> {
441        let mapped = self.iter().map(by).collect::<Box<_>>();
442        // SAFETY: Doing `.map(..)` can never change the length.
443        unsafe { SlimSliceBox::from_boxed_unchecked(mapped) }
444    }
445}
446
447impl<T> TryFrom<Box<[T]>> for SlimSliceBox<T> {
448    type Error = LenTooLong<Box<[T]>>;
449
450    #[inline]
451    fn try_from(boxed: Box<[T]>) -> Result<Self, Self::Error> {
452        ensure_len_fits!(boxed);
453        // SAFETY: Checked above that `len <= u32::MAX`.
454        Ok(unsafe { Self::from_boxed_unchecked(boxed) })
455    }
456}
457
458impl<T> TryFrom<Vec<T>> for SlimSliceBox<T> {
459    type Error = LenTooLong<Vec<T>>;
460
461    #[inline]
462    fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
463        ensure_len_fits!(vec);
464        // SAFETY: Checked above that `len <= u32::MAX`.
465        Ok(unsafe { Self::from_boxed_unchecked(vec.into_boxed_slice()) })
466    }
467}
468
469impl<T, const N: usize> From<[T; N]> for SlimSliceBox<T> {
470    #[inline]
471    fn from(arr: [T; N]) -> Self {
472        #[allow(clippy::let_unit_value)]
473        let () = AssertU32::<N>::OK;
474
475        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.
476        unsafe { Self::from_boxed_unchecked(into_box(arr)) }
477    }
478}
479
480impl<T> From<SlimSliceBox<T>> for Vec<T> {
481    #[inline]
482    fn from(slice: SlimSliceBox<T>) -> Self {
483        into_box(slice).into()
484    }
485}
486
487impl<T: Debug> Debug for SlimSliceBox<T> {
488    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489        Debug::fmt(self.deref(), f)
490    }
491}
492
493impl<T: Clone> Clone for SlimSliceBox<T> {
494    #[inline]
495    fn clone(&self) -> Self {
496        // Allocate exactly the right amount
497        // so we later don't reallocate due to excess capacity.
498        let mut vec = Vec::with_capacity(self.len());
499        vec.extend_from_slice(self);
500        // SAFETY: We know `self.len() <= u32::MAX`.
501        unsafe { Self::from_boxed_unchecked(into_box(vec)) }
502    }
503}
504
505impl<R: Deref, T> PartialEq<R> for SlimSliceBox<T>
506where
507    [T]: PartialEq<R::Target>,
508{
509    #[inline]
510    fn eq(&self, other: &R) -> bool {
511        **self == **other
512    }
513}
514
515impl<T: Eq> Eq for SlimSliceBox<T> {}
516
517impl<R: Deref, T> PartialOrd<R> for SlimSliceBox<T>
518where
519    [T]: PartialOrd<R::Target>,
520{
521    #[inline]
522    fn partial_cmp(&self, other: &R) -> Option<Ordering> {
523        (**self).partial_cmp(&**other)
524    }
525}
526
527impl<T: Ord> Ord for SlimSliceBox<T> {
528    #[inline]
529    fn cmp(&self, other: &Self) -> Ordering {
530        (**self).cmp(&**other)
531    }
532}
533
534impl<T: Hash> Hash for SlimSliceBox<T> {
535    #[inline]
536    fn hash<H: Hasher>(&self, state: &mut H) {
537        Hash::hash(&**self, state)
538    }
539}
540
541impl<T> IntoIterator for SlimSliceBox<T> {
542    type Item = T;
543    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
544    #[inline]
545    fn into_iter(self) -> Self::IntoIter {
546        Vec::from(self).into_iter()
547    }
548}
549
550/// A wrapper to achieve `FromIterator<T> for Result<SlimSliceBox<T>, Vec<T>>`.
551///
552/// We cannot do this directly due to orphan rules.
553pub struct SlimSliceBoxCollected<T> {
554    /// The result of `from_iter`.
555    pub inner: Result<SlimSliceBox<T>, LenTooLong<Vec<T>>>,
556}
557
558impl<T: Debug> SlimSliceBoxCollected<T> {
559    #[inline]
560    pub fn unwrap(self) -> SlimSliceBox<T> {
561        self.inner.expect("number of elements overflowed `u32::MAX`")
562    }
563}
564
565impl<A> FromIterator<A> for SlimSliceBoxCollected<A> {
566    #[inline]
567    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
568        let inner = iter.into_iter().collect::<Vec<_>>().try_into();
569        SlimSliceBoxCollected { inner }
570    }
571}
572
573// =============================================================================
574// Owned boxed slice with SSO
575// =============================================================================
576
577pub struct SlimSmallSliceBox<T, const N: usize>(SlimSmallSliceBoxData<T, N>);
578
579/// The representation of [`SlimSmallSliceBox<T>`].
580///
581/// The parameter `N` is the number of elements that can be inline.
582enum SlimSmallSliceBoxData<T, const N: usize> {
583    /// The data is inline, not using any indirections.
584    Inline([T; N]),
585    /// The data is boxed up.
586    Heap(SlimSliceBox<T>),
587}
588
589impl<T, const N: usize> From<[T; N]> for SlimSmallSliceBox<T, N> {
590    fn from(value: [T; N]) -> Self {
591        #[allow(clippy::let_unit_value)]
592        let () = AssertU32::<N>::OK;
593
594        Self(SlimSmallSliceBoxData::Inline(value))
595    }
596}
597
598impl<T, const N: usize> From<SlimSliceBox<T>> for SlimSmallSliceBox<T, N> {
599    fn from(value: SlimSliceBox<T>) -> Self {
600        Self(SlimSmallSliceBoxData::Heap(value))
601    }
602}
603
604impl<T, const N: usize> From<SlimSmallSliceBox<T, N>> for SlimSliceBox<T> {
605    fn from(SlimSmallSliceBox(value): SlimSmallSliceBox<T, N>) -> Self {
606        match value {
607            SlimSmallSliceBoxData::Inline(i) => i.into(),
608            SlimSmallSliceBoxData::Heap(h) => h,
609        }
610    }
611}
612
613impl<T, const N: usize> Deref for SlimSmallSliceBox<T, N> {
614    type Target = [T];
615    fn deref(&self) -> &Self::Target {
616        match &self.0 {
617            SlimSmallSliceBoxData::Inline(i) => i,
618            SlimSmallSliceBoxData::Heap(h) => h,
619        }
620    }
621}
622
623impl<T, const N: usize> DerefMut for SlimSmallSliceBox<T, N> {
624    fn deref_mut(&mut self) -> &mut Self::Target {
625        match &mut self.0 {
626            SlimSmallSliceBoxData::Inline(i) => i,
627            SlimSmallSliceBoxData::Heap(h) => h,
628        }
629    }
630}
631
632// =============================================================================
633// Owned boxed string slice
634// =============================================================================
635
636/// Provides a slimmer version of `Box<str>`
637/// using `u32` for its length instead of `usize`.
638#[repr(transparent)]
639pub struct SlimStrBox {
640    /// The underlying byte slice.
641    raw: SlimSliceBox<u8>,
642}
643
644#[cfg(feature = "serde")]
645impl serde::Serialize for SlimStrBox {
646    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
647        s.serialize_str(self.deref())
648    }
649}
650
651impl SlimStrBox {
652    /// Converts `boxed` to `Self` without checking `boxed.len() <= u32::MAX`.
653    ///
654    /// # Safety
655    ///
656    /// The caller must ensure that `boxed.len() <= u32::MAX`.
657    #[inline]
658    pub unsafe fn from_boxed_unchecked(boxed: Box<str>) -> Self {
659        // SAFETY: Caller has promised that `boxed.len() <= u32::MAX`.
660        let raw = unsafe { SlimSliceBox::from_boxed_unchecked(into_box(boxed)) };
661        Self { raw }
662    }
663
664    /// Converts `boxed: Box<str>` into `SlimStrBox`.
665    ///
666    /// Panics when `boxed.len() > u32::MAX`.
667    #[inline]
668    pub fn from_boxed(boxed: Box<str>) -> Self {
669        expect_fit(boxed)
670    }
671
672    /// Converts `str: String` into `SlimStrBox`.
673    ///
674    /// Panics when `str.len() > u32::MAX`.
675    #[inline]
676    pub fn from_string(str: String) -> Self {
677        Self::from_boxed(str.into())
678    }
679
680    /// Returns a limited shared string slice to this boxed string slice.
681    #[allow(clippy::needless_lifetimes)]
682    #[inline]
683    pub fn shared_ref<'a>(&'a self) -> &'a SlimStr<'a> {
684        // SAFETY: The reference lives as long as `self`,
685        // we have shared access already,
686        // and by construction we know it's UTF-8.
687        unsafe { mem::transmute(self.raw.shared_ref()) }
688    }
689
690    /// Returns a limited mutable string slice to this boxed string slice.
691    #[allow(clippy::needless_lifetimes)]
692    #[inline]
693    pub fn exclusive_ref<'a>(&'a mut self) -> &'a mut SlimStrMut<'a> {
694        // SAFETY: The reference lives as long as `self`,
695        // we had `&mut self`,
696        // and by construction we know it's UTF-8.
697        unsafe { mem::transmute(self.raw.exclusive_ref()) }
698    }
699}
700
701impl Deref for SlimStrBox {
702    type Target = str;
703
704    #[inline]
705    fn deref(&self) -> &Self::Target {
706        self.shared_ref().deref()
707    }
708}
709
710impl DerefMut for SlimStrBox {
711    #[inline]
712    fn deref_mut(&mut self) -> &mut Self::Target {
713        self.exclusive_ref().deref_mut()
714    }
715}
716
717impl<const N: usize> From<NStr<N>> for SlimStrBox {
718    #[inline]
719    fn from(arr: NStr<N>) -> Self {
720        (&arr).into()
721    }
722}
723
724impl<const N: usize> From<&NStr<N>> for SlimStrBox {
725    #[inline]
726    fn from(arr: &NStr<N>) -> Self {
727        <SlimStr<'_>>::from(arr).into()
728    }
729}
730
731impl TryFrom<Box<str>> for SlimStrBox {
732    type Error = LenTooLong<Box<str>>;
733
734    #[inline]
735    fn try_from(boxed: Box<str>) -> Result<Self, Self::Error> {
736        ensure_len_fits!(boxed);
737        // SAFETY: Checked above that `len <= u32::MAX`.
738        Ok(unsafe { Self::from_boxed_unchecked(boxed) })
739    }
740}
741
742impl TryFrom<String> for SlimStrBox {
743    type Error = LenTooLong<String>;
744
745    #[inline]
746    fn try_from(str: String) -> Result<Self, Self::Error> {
747        ensure_len_fits!(str);
748        // SAFETY: Checked above that `len <= u32::MAX`.
749        Ok(unsafe { Self::from_boxed_unchecked(str.into_boxed_str()) })
750    }
751}
752
753impl<'a> TryFrom<&'a str> for SlimStrBox {
754    type Error = LenTooLong<&'a str>;
755
756    #[inline]
757    fn try_from(str: &'a str) -> Result<Self, Self::Error> {
758        str.try_into().map(|s: SlimStr<'_>| s.into())
759    }
760}
761
762impl From<SlimStrBox> for Box<str> {
763    #[inline]
764    fn from(str: SlimStrBox) -> Self {
765        let raw_box = into_box(str.raw);
766        // SAFETY: By construction, `SlimStrBox` is valid UTF-8.
767        unsafe { Box::from_raw(Box::into_raw(raw_box) as *mut str) }
768    }
769}
770
771impl From<SlimStrBox> for String {
772    #[inline]
773    fn from(str: SlimStrBox) -> Self {
774        <Box<str>>::from(str).into()
775    }
776}
777
778impl Debug for SlimStrBox {
779    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
780        Debug::fmt(self.deref(), f)
781    }
782}
783
784impl Display for SlimStrBox {
785    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
786        Display::fmt(self.deref(), f)
787    }
788}
789
790impl Clone for SlimStrBox {
791    #[inline]
792    fn clone(&self) -> Self {
793        Self { raw: self.raw.clone() }
794    }
795}
796
797impl<R: Deref> PartialEq<R> for SlimStrBox
798where
799    str: PartialEq<R::Target>,
800{
801    #[inline]
802    fn eq(&self, other: &R) -> bool {
803        self.deref() == other.deref()
804    }
805}
806
807impl Eq for SlimStrBox {}
808
809impl<R: Deref> PartialOrd<R> for SlimStrBox
810where
811    str: PartialOrd<R::Target>,
812{
813    #[inline]
814    fn partial_cmp(&self, other: &R) -> Option<Ordering> {
815        self.deref().partial_cmp(other.deref())
816    }
817}
818
819impl Ord for SlimStrBox {
820    #[inline]
821    fn cmp(&self, other: &Self) -> Ordering {
822        self.deref().cmp(other.deref())
823    }
824}
825
826impl Hash for SlimStrBox {
827    #[inline]
828    fn hash<H: Hasher>(&self, state: &mut H) {
829        Hash::hash(self.deref(), state)
830    }
831}
832
833impl Borrow<str> for SlimStrBox {
834    #[inline]
835    fn borrow(&self) -> &str {
836        self
837    }
838}
839
840// =============================================================================
841// Shared slice reference
842// =============================================================================
843
844#[allow(clippy::module_inception)]
845mod slim_slice {
846    use super::*;
847
848    /// A shared reference to `[T]` limited to `u32::MAX` in length.
849    #[repr(transparent)]
850    #[derive(Clone, Copy)]
851    pub struct SlimSlice<'a, T> {
852        /// The representation of this shared slice.
853        raw: SlimRawSlice<T>,
854        /// Marker to ensure covariance for `'a`.
855        covariant: PhantomData<&'a [T]>,
856    }
857
858    impl<'a, T> SlimSlice<'a, T> {
859        /// Converts a `&[T]` to the limited version without length checking.
860        ///
861        /// SAFETY: `slice.len() <= u32::MAX` must hold.
862        pub(super) const unsafe fn from_slice_unchecked(slice: &'a [T]) -> Self {
863            let len = slice.len();
864            let ptr = slice.as_ptr().cast_mut();
865            // SAFETY: `&mut [T]` implies that the pointer is non-null.
866            let raw = SlimRawSlice::from_len_ptr(len, ptr);
867            // SAFETY: Our length invariant is satisfied by the caller.
868            let covariant = PhantomData;
869            Self { raw, covariant }
870        }
871    }
872
873    impl<T> Deref for SlimSlice<'_, T> {
874        type Target = [T];
875
876        fn deref(&self) -> &Self::Target {
877            // SAFETY: `ptr` and `len` are either
878            // a) derived from a live `Box<[T]>` valid for `'self`
879            // b) derived from a live `&'self [T]`
880            // so we satisfy all safety requirements for `from_raw_parts`.
881            unsafe { self.raw.deref() }
882        }
883    }
884}
885pub use slim_slice::*;
886
887use crate::nstr::NStr;
888
889// SAFETY: Same rules as for `&[T]`.
890unsafe impl<T: Send + Sync> Send for SlimSlice<'_, T> {}
891
892// SAFETY: Same rules as for `&[T]`.
893unsafe impl<T: Sync> Sync for SlimSlice<'_, T> {}
894
895impl<T> SlimSlice<'_, T> {
896    /// Falibly maps all elements with `by` to `Result<U, E>`.
897    ///
898    /// Returns `Err(_)` if any call to `by` did.
899    #[inline]
900    pub fn try_map<U, E>(&self, by: impl FnMut(&T) -> Result<U, E>) -> Result<SlimSliceBox<U>, E> {
901        let mapped = self.iter().map(by).collect::<Result<_, _>>()?;
902        // SAFETY: Doing `.map(..)` can never change the length.
903        Ok(unsafe { SlimSliceBox::from_boxed_unchecked(mapped) })
904    }
905}
906
907impl<T: Debug> Debug for SlimSlice<'_, T> {
908    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
909        Debug::fmt(self.deref(), f)
910    }
911}
912
913impl<T: Hash> Hash for SlimSlice<'_, T> {
914    #[inline]
915    fn hash<H: Hasher>(&self, state: &mut H) {
916        Hash::hash(self.deref(), state)
917    }
918}
919
920impl<T: Eq> Eq for SlimSlice<'_, T> {}
921impl<T: PartialEq> PartialEq for SlimSlice<'_, T> {
922    #[inline]
923    fn eq(&self, other: &Self) -> bool {
924        self.deref() == other.deref()
925    }
926}
927impl<T: PartialEq> PartialEq<[T]> for SlimSlice<'_, T> {
928    #[inline]
929    fn eq(&self, other: &[T]) -> bool {
930        self.deref() == other
931    }
932}
933
934impl<T: Ord> Ord for SlimSlice<'_, T> {
935    #[inline]
936    fn cmp(&self, other: &Self) -> Ordering {
937        self.deref().cmp(other)
938    }
939}
940impl<T: PartialOrd> PartialOrd for SlimSlice<'_, T> {
941    #[inline]
942    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
943        self.deref().partial_cmp(other.deref())
944    }
945}
946impl<T: PartialOrd> PartialOrd<[T]> for SlimSlice<'_, T> {
947    #[inline]
948    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
949        self.deref().partial_cmp(other)
950    }
951}
952
953impl<T: Clone> From<&SlimSlice<'_, T>> for SlimSliceBox<T> {
954    #[inline]
955    fn from(slice: &SlimSlice<'_, T>) -> Self {
956        let boxed = into_box(slice.deref());
957        // SAFETY: `slice` is limited to `len: u32` by construction.
958        unsafe { Self::from_boxed_unchecked(boxed) }
959    }
960}
961impl<T: Clone> From<&SlimSlice<'_, T>> for Box<[T]> {
962    #[inline]
963    fn from(slice: &SlimSlice<'_, T>) -> Self {
964        slice.deref().into()
965    }
966}
967impl<T: Clone> From<&SlimSlice<'_, T>> for Vec<T> {
968    #[inline]
969    fn from(slice: &SlimSlice<'_, T>) -> Self {
970        slice.deref().into()
971    }
972}
973
974impl<T: Clone> From<SlimSlice<'_, T>> for SlimSliceBox<T> {
975    #[inline]
976    fn from(slice: SlimSlice<'_, T>) -> Self {
977        (&slice).into()
978    }
979}
980impl<T: Clone> From<SlimSlice<'_, T>> for Box<[T]> {
981    #[inline]
982    fn from(slice: SlimSlice<'_, T>) -> Self {
983        slice.deref().into()
984    }
985}
986impl<T: Clone> From<SlimSlice<'_, T>> for Vec<T> {
987    #[inline]
988    fn from(slice: SlimSlice<'_, T>) -> Self {
989        slice.deref().into()
990    }
991}
992
993impl<'a, T> TryFrom<&'a [T]> for SlimSlice<'a, T> {
994    type Error = LenTooLong<&'a [T]>;
995
996    #[inline]
997    fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
998        ensure_len_fits!(slice);
999        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1000        Ok(unsafe { Self::from_slice_unchecked(slice) })
1001    }
1002}
1003
1004/// Converts `&[T]` into the slim limited version.
1005///
1006/// Panics when `slice.len() > u32::MAX`.
1007#[inline]
1008pub fn from_slice<T>(s: &[T]) -> SlimSlice<'_, T> {
1009    expect_fit(s)
1010}
1011
1012// =============================================================================
1013// Mutable slice reference
1014// =============================================================================
1015
1016/// A mutable reference to `[T]` limited to `u32::MAX` in length.
1017#[repr(transparent)]
1018pub struct SlimSliceMut<'a, T> {
1019    /// The representation of this mutable slice.
1020    raw: SlimRawSlice<T>,
1021    /// Marker to ensure invariance for `'a`.
1022    invariant: PhantomData<&'a mut [T]>,
1023}
1024
1025// SAFETY: Same rules as for `&mut [T]`.
1026unsafe impl<T: Send> Send for SlimSliceMut<'_, T> {}
1027
1028// SAFETY: Same rules as for `&mut [T]`.
1029unsafe impl<T: Sync> Sync for SlimSliceMut<'_, T> {}
1030
1031impl<'a, T> SlimSliceMut<'a, T> {
1032    /// Convert this mutable reference to a shared one.
1033    #[inline]
1034    pub fn shared(&'a self) -> &'a SlimSlice<'a, T> {
1035        // SAFETY: By virtue of `&'a mut X -> &'a X` being sound, this is also.
1036        // The types and references to them have the same layout as well.
1037        unsafe { mem::transmute(self) }
1038    }
1039
1040    /// Converts a `&mut [T]` to the limited version without length checking.
1041    ///
1042    /// SAFETY: `slice.len() <= u32::MAX` must hold.
1043    #[inline]
1044    unsafe fn from_slice_unchecked(slice: &'a mut [T]) -> Self {
1045        // SAFETY: `&mut [T]` implies that the pointer is non-null.
1046        let raw = SlimRawSlice::from_len_ptr(slice.len(), slice.as_mut_ptr());
1047        // SAFETY: Our invariants are satisfied by the caller
1048        // and that `&mut [T]` implies exclusive access to the data.
1049        let invariant = PhantomData;
1050        Self { raw, invariant }
1051    }
1052}
1053
1054impl<T> Deref for SlimSliceMut<'_, T> {
1055    type Target = [T];
1056
1057    #[inline]
1058    fn deref(&self) -> &Self::Target {
1059        self.shared().deref()
1060    }
1061}
1062
1063impl<T> DerefMut for SlimSliceMut<'_, T> {
1064    #[inline]
1065    fn deref_mut(&mut self) -> &mut Self::Target {
1066        // SAFETY: `ptr` and `len` are either
1067        // a) derived from a live `Box<[T]>` valid for `'self`
1068        // b) derived from a live `&'self [T]`
1069        // and additionally, we have the only pointer to the data.
1070        // so we satisfy all safety requirements for `from_raw_parts_mut`.
1071        unsafe { self.raw.deref_mut() }
1072    }
1073}
1074
1075impl<T: Debug> Debug for SlimSliceMut<'_, T> {
1076    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1077        Debug::fmt(self.deref(), f)
1078    }
1079}
1080
1081impl<T: Hash> Hash for SlimSliceMut<'_, T> {
1082    #[inline]
1083    fn hash<H: Hasher>(&self, state: &mut H) {
1084        Hash::hash(self.deref(), state)
1085    }
1086}
1087
1088impl<T: Eq> Eq for SlimSliceMut<'_, T> {}
1089impl<T: PartialEq> PartialEq for SlimSliceMut<'_, T> {
1090    #[inline]
1091    fn eq(&self, other: &Self) -> bool {
1092        self.deref() == other.deref()
1093    }
1094}
1095impl<T: PartialEq> PartialEq<[T]> for SlimSliceMut<'_, T> {
1096    #[inline]
1097    fn eq(&self, other: &[T]) -> bool {
1098        self.deref() == other
1099    }
1100}
1101
1102impl<T: Ord> Ord for SlimSliceMut<'_, T> {
1103    #[inline]
1104    fn cmp(&self, other: &Self) -> Ordering {
1105        self.deref().cmp(other)
1106    }
1107}
1108impl<T: PartialOrd> PartialOrd for SlimSliceMut<'_, T> {
1109    #[inline]
1110    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1111        self.deref().partial_cmp(other.deref())
1112    }
1113}
1114impl<T: PartialOrd> PartialOrd<[T]> for SlimSliceMut<'_, T> {
1115    #[inline]
1116    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
1117        self.deref().partial_cmp(other)
1118    }
1119}
1120
1121impl<T: Clone> From<&SlimSliceMut<'_, T>> for SlimSliceBox<T> {
1122    #[inline]
1123    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1124        // SAFETY: `slice` is limited to `len: u32` by construction.
1125        unsafe { Self::from_boxed_unchecked(into_box(slice.deref())) }
1126    }
1127}
1128impl<T: Clone> From<&SlimSliceMut<'_, T>> for Box<[T]> {
1129    #[inline]
1130    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1131        slice.deref().into()
1132    }
1133}
1134impl<T: Clone> From<&SlimSliceMut<'_, T>> for Vec<T> {
1135    #[inline]
1136    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1137        slice.deref().into()
1138    }
1139}
1140
1141impl<T: Clone> From<SlimSliceMut<'_, T>> for SlimSliceBox<T> {
1142    #[inline]
1143    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1144        (&slice).into()
1145    }
1146}
1147impl<T: Clone> From<SlimSliceMut<'_, T>> for Box<[T]> {
1148    #[inline]
1149    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1150        slice.deref().into()
1151    }
1152}
1153impl<T: Clone> From<SlimSliceMut<'_, T>> for Vec<T> {
1154    #[inline]
1155    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1156        slice.deref().into()
1157    }
1158}
1159
1160impl<'a, T> TryFrom<&'a mut [T]> for SlimSliceMut<'a, T> {
1161    type Error = LenTooLong<&'a mut [T]>;
1162
1163    #[inline]
1164    fn try_from(slice: &'a mut [T]) -> Result<Self, Self::Error> {
1165        ensure_len_fits!(slice);
1166        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1167        Ok(unsafe { Self::from_slice_unchecked(slice) })
1168    }
1169}
1170
1171/// Converts `&mut [T]` into the slim limited version.
1172///
1173/// Panics when `slice.len() > u32::MAX`.
1174#[inline]
1175pub fn from_slice_mut<T>(s: &mut [T]) -> SlimSliceMut<'_, T> {
1176    expect_fit(s)
1177}
1178
1179// =============================================================================
1180// Shared string slice reference
1181// =============================================================================
1182
1183/// A shared reference to `str` limited to `u32::MAX` in length.
1184#[repr(transparent)]
1185#[derive(Clone, Copy)]
1186pub struct SlimStr<'a> {
1187    /// The raw byte slice.
1188    raw: SlimSlice<'a, u8>,
1189}
1190
1191impl<'a> SlimStr<'a> {
1192    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.
1193    ///
1194    /// # Safety
1195    ///
1196    /// The caller must ensure that `s.len() <= u32::MAX`.
1197    #[inline]
1198    const unsafe fn from_str_unchecked(s: &'a str) -> Self {
1199        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.
1200        let raw = unsafe { SlimSlice::from_slice_unchecked(s.as_bytes()) };
1201        // SAFETY: `s: &str` is always UTF-8.
1202        Self { raw }
1203    }
1204}
1205
1206impl Deref for SlimStr<'_> {
1207    type Target = str;
1208
1209    #[inline]
1210    fn deref(&self) -> &Self::Target {
1211        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1212        unsafe { from_utf8_unchecked(self.raw.deref()) }
1213    }
1214}
1215
1216impl Debug for SlimStr<'_> {
1217    #[inline]
1218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1219        Debug::fmt(self.deref(), f)
1220    }
1221}
1222
1223impl Display for SlimStr<'_> {
1224    #[inline]
1225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1226        Display::fmt(self.deref(), f)
1227    }
1228}
1229
1230impl Hash for SlimStr<'_> {
1231    #[inline]
1232    fn hash<H: Hasher>(&self, state: &mut H) {
1233        Hash::hash(self.deref(), state)
1234    }
1235}
1236
1237impl Eq for SlimStr<'_> {}
1238impl PartialEq for SlimStr<'_> {
1239    #[inline]
1240    fn eq(&self, other: &Self) -> bool {
1241        self.deref() == other.deref()
1242    }
1243}
1244impl PartialEq<str> for SlimStr<'_> {
1245    #[inline]
1246    fn eq(&self, other: &str) -> bool {
1247        self.deref() == other
1248    }
1249}
1250
1251impl Ord for SlimStr<'_> {
1252    #[inline]
1253    fn cmp(&self, other: &Self) -> Ordering {
1254        self.deref().cmp(other)
1255    }
1256}
1257impl PartialOrd for SlimStr<'_> {
1258    #[inline]
1259    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1260        Some(self.cmp(other))
1261    }
1262}
1263impl PartialOrd<str> for SlimStr<'_> {
1264    #[inline]
1265    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1266        self.deref().partial_cmp(other)
1267    }
1268}
1269
1270impl From<&SlimStr<'_>> for SlimStrBox {
1271    #[inline]
1272    fn from(slice: &SlimStr<'_>) -> Self {
1273        (*slice).into()
1274    }
1275}
1276impl From<&SlimStr<'_>> for Box<str> {
1277    #[inline]
1278    fn from(slice: &SlimStr<'_>) -> Self {
1279        slice.deref().into()
1280    }
1281}
1282impl From<&SlimStr<'_>> for String {
1283    #[inline]
1284    fn from(slice: &SlimStr<'_>) -> Self {
1285        slice.deref().into()
1286    }
1287}
1288
1289impl From<SlimStr<'_>> for SlimStrBox {
1290    #[inline]
1291    fn from(slice: SlimStr<'_>) -> Self {
1292        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.
1293        Self { raw: slice.raw.into() }
1294    }
1295}
1296impl From<SlimStr<'_>> for Box<str> {
1297    #[inline]
1298    fn from(slice: SlimStr<'_>) -> Self {
1299        slice.deref().into()
1300    }
1301}
1302impl From<SlimStr<'_>> for String {
1303    #[inline]
1304    fn from(slice: SlimStr<'_>) -> Self {
1305        slice.deref().into()
1306    }
1307}
1308
1309impl<'a, const N: usize> From<&'a NStr<N>> for SlimStr<'a> {
1310    #[inline]
1311    fn from(arr: &'a NStr<N>) -> Self {
1312        #[allow(clippy::let_unit_value)]
1313        let () = AssertU32::<N>::OK;
1314
1315        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.
1316        unsafe { Self::from_str_unchecked(arr) }
1317    }
1318}
1319impl<'a> TryFrom<&'a str> for SlimStr<'a> {
1320    type Error = LenTooLong<&'a str>;
1321
1322    #[inline]
1323    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1324        ensure_len_fits!(s);
1325        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1326        Ok(unsafe { Self::from_str_unchecked(s) })
1327    }
1328}
1329
1330/// Converts `&str` into the slim limited version.
1331///
1332/// Panics when `str.len() > u32::MAX`.
1333#[inline]
1334pub const fn from_str(s: &str) -> SlimStr<'_> {
1335    if s.len() > u32::MAX as usize {
1336        panic!("length didn't fit in `u32`");
1337    }
1338
1339    // SAFETY: ^-- satisfies `len <= u32::MAX`.
1340    unsafe { SlimStr::from_str_unchecked(s) }
1341}
1342
1343/// Converts `&str` into the owned slim limited version.
1344///
1345/// Panics when `str.len() > u32::MAX`.
1346#[inline]
1347pub fn from_string(s: &str) -> SlimStrBox {
1348    from_str(s).into()
1349}
1350
1351// =============================================================================
1352// Mutable string slice reference
1353// =============================================================================
1354
1355/// A mutable reference to `str` limited to `u32::MAX` in length.
1356#[repr(transparent)]
1357pub struct SlimStrMut<'a> {
1358    /// The raw byte slice.
1359    raw: SlimSliceMut<'a, u8>,
1360}
1361
1362impl<'a> SlimStrMut<'a> {
1363    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.
1364    ///
1365    /// # Safety
1366    ///
1367    /// The caller must ensure that `s.len() <= u32::MAX`.
1368    #[inline]
1369    unsafe fn from_str_unchecked(s: &'a mut str) -> Self {
1370        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.
1371        let raw = unsafe { SlimSliceMut::from_slice_unchecked(s.as_bytes_mut()) };
1372        // SAFETY: `s: &mut str` is always UTF-8.
1373        Self { raw }
1374    }
1375}
1376
1377impl Deref for SlimStrMut<'_> {
1378    type Target = str;
1379
1380    #[inline]
1381    fn deref(&self) -> &Self::Target {
1382        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1383        unsafe { from_utf8_unchecked(self.raw.deref()) }
1384    }
1385}
1386
1387impl DerefMut for SlimStrMut<'_> {
1388    #[inline]
1389    fn deref_mut(&mut self) -> &mut Self::Target {
1390        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1391        unsafe { from_utf8_unchecked_mut(self.raw.deref_mut()) }
1392    }
1393}
1394
1395impl Debug for SlimStrMut<'_> {
1396    #[inline]
1397    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1398        Debug::fmt(self.deref(), f)
1399    }
1400}
1401
1402impl Display for SlimStrMut<'_> {
1403    #[inline]
1404    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1405        Display::fmt(self.deref(), f)
1406    }
1407}
1408
1409impl Hash for SlimStrMut<'_> {
1410    #[inline]
1411    fn hash<H: Hasher>(&self, state: &mut H) {
1412        Hash::hash(self.deref(), state)
1413    }
1414}
1415
1416impl Eq for SlimStrMut<'_> {}
1417impl PartialEq for SlimStrMut<'_> {
1418    #[inline]
1419    fn eq(&self, other: &Self) -> bool {
1420        self.deref() == other.deref()
1421    }
1422}
1423impl PartialEq<str> for SlimStrMut<'_> {
1424    #[inline]
1425    fn eq(&self, other: &str) -> bool {
1426        self.deref() == other
1427    }
1428}
1429
1430impl Ord for SlimStrMut<'_> {
1431    #[inline]
1432    fn cmp(&self, other: &Self) -> Ordering {
1433        self.deref().cmp(other)
1434    }
1435}
1436impl PartialOrd for SlimStrMut<'_> {
1437    #[inline]
1438    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1439        Some(self.cmp(other))
1440    }
1441}
1442impl PartialOrd<str> for SlimStrMut<'_> {
1443    #[inline]
1444    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1445        self.deref().partial_cmp(other)
1446    }
1447}
1448
1449impl From<&SlimStrMut<'_>> for SlimStrBox {
1450    #[inline]
1451    fn from(slice: &SlimStrMut<'_>) -> Self {
1452        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.
1453        Self {
1454            raw: (&slice.raw).into(),
1455        }
1456    }
1457}
1458impl From<&SlimStrMut<'_>> for Box<str> {
1459    #[inline]
1460    fn from(slice: &SlimStrMut<'_>) -> Self {
1461        slice.deref().into()
1462    }
1463}
1464impl From<&SlimStrMut<'_>> for String {
1465    #[inline]
1466    fn from(slice: &SlimStrMut<'_>) -> Self {
1467        slice.deref().into()
1468    }
1469}
1470
1471impl From<SlimStrMut<'_>> for SlimStrBox {
1472    #[inline]
1473    fn from(slice: SlimStrMut<'_>) -> Self {
1474        (&slice).into()
1475    }
1476}
1477impl From<SlimStrMut<'_>> for Box<str> {
1478    #[inline]
1479    fn from(slice: SlimStrMut<'_>) -> Self {
1480        slice.deref().into()
1481    }
1482}
1483impl From<SlimStrMut<'_>> for String {
1484    #[inline]
1485    fn from(slice: SlimStrMut<'_>) -> Self {
1486        slice.deref().into()
1487    }
1488}
1489
1490impl<'a, const N: usize> From<&'a mut NStr<N>> for SlimStrMut<'a> {
1491    #[inline]
1492    fn from(arr: &'a mut NStr<N>) -> Self {
1493        #[allow(clippy::let_unit_value)]
1494        let () = AssertU32::<N>::OK;
1495
1496        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.
1497        unsafe { Self::from_str_unchecked(arr) }
1498    }
1499}
1500impl<'a> TryFrom<&'a mut str> for SlimStrMut<'a> {
1501    type Error = LenTooLong<&'a mut str>;
1502
1503    #[inline]
1504    fn try_from(slice: &'a mut str) -> Result<Self, Self::Error> {
1505        ensure_len_fits!(slice);
1506        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1507        Ok(unsafe { Self::from_str_unchecked(slice) })
1508    }
1509}
1510
1511/// Converts `&mut str` into the slim limited version.
1512///
1513/// Panics when `str.len() > u32::MAX`.
1514#[inline]
1515pub fn from_str_mut(s: &mut str) -> SlimStrMut<'_> {
1516    expect_fit(s)
1517}
1518
1519#[cfg(test)]
1520mod tests {
1521    use std::hash::BuildHasher;
1522
1523    use super::*;
1524    use crate::map::DefaultHashBuilder;
1525    use crate::nstr;
1526
1527    fn hash_of<T: Hash>(x: T) -> u64 {
1528        DefaultHashBuilder::default().hash_one(&x)
1529    }
1530
1531    fn hash_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1532    where
1533        T: Hash + Debug + Deref,
1534        <T as Deref>::Target: Hash,
1535    {
1536        assert_eq!(hash_of(a), hash_of(a_deref));
1537        assert_eq!(hash_of(b), hash_of(b_deref));
1538        assert_ne!(hash_of(a), hash_of(b));
1539    }
1540
1541    fn ord_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1542    where
1543        T: Ord + Debug + Deref + PartialOrd<<T as Deref>::Target>,
1544    {
1545        assert_eq!(a.partial_cmp(b), Some(Ordering::Less));
1546        assert_eq!(b.partial_cmp(a), Some(Ordering::Greater));
1547        assert_eq!(a.partial_cmp(a), Some(Ordering::Equal));
1548        assert_eq!(b.partial_cmp(b), Some(Ordering::Equal));
1549        assert_eq!(a.partial_cmp(b_deref), Some(Ordering::Less));
1550        assert_eq!(b.partial_cmp(a_deref), Some(Ordering::Greater));
1551        assert_eq!(a.partial_cmp(a_deref), Some(Ordering::Equal));
1552        assert_eq!(b.partial_cmp(b_deref), Some(Ordering::Equal));
1553
1554        assert_eq!(a.cmp(b), Ordering::Less);
1555        assert_eq!(b.cmp(a), Ordering::Greater);
1556        assert_eq!(a.cmp(a), Ordering::Equal);
1557        assert_eq!(b.cmp(b), Ordering::Equal);
1558    }
1559
1560    #[allow(clippy::eq_op)]
1561    fn eq_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1562    where
1563        T: Eq + Debug + Deref + PartialEq<<T as Deref>::Target>,
1564    {
1565        assert!(a != b);
1566        assert!(b != a);
1567        assert_eq!(a, a);
1568        assert!(a != b_deref);
1569        assert!(a == a_deref);
1570        assert!(b != a_deref);
1571        assert!(b == b_deref);
1572    }
1573
1574    fn debug_properties<T: Debug, U: ?Sized + Debug>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {
1575        assert_eq!(format!("{:?}", a), format!("{:?}", a_cmp));
1576        assert_eq!(format!("{:?}", b), format!("{:?}", b_cmp));
1577    }
1578
1579    fn display_properties<T: Debug + Display, U: ?Sized + Display>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {
1580        assert_eq!(a.to_string(), a_cmp.to_string());
1581        assert_eq!(b.to_string(), b_cmp.to_string());
1582    }
1583
1584    fn general_properties<T, U>(a: &T, b: &T, a_deref: &U, b_deref: &U)
1585    where
1586        T: Deref<Target = U> + Debug + Eq + PartialEq<U> + PartialOrd<U> + Ord + Hash,
1587        U: ?Sized + Debug + Eq + Ord + Hash,
1588    {
1589        eq_properties(a, b, a_deref, b_deref);
1590        ord_properties(a, b, a_deref, b_deref);
1591        hash_properties(a, b, a_deref, b_deref);
1592        debug_properties(a, b, a_deref, b_deref);
1593    }
1594
1595    const TEST_STR: &str = "foo";
1596    const TEST_STR2: &str = "fop";
1597    const TEST_SLICE: &[u8] = TEST_STR.as_bytes();
1598    const TEST_SLICE2: &[u8] = TEST_STR2.as_bytes();
1599
1600    fn test_strings() -> [String; 2] {
1601        [TEST_STR.to_string(), TEST_STR2.to_string()]
1602    }
1603
1604    fn test_slices() -> [Vec<u8>; 2] {
1605        [TEST_SLICE.to_owned(), TEST_SLICE2.to_owned()]
1606    }
1607
1608    fn various_boxed_slices() -> [[SlimSliceBox<u8>; 2]; 5] {
1609        [
1610            test_slices().map(SlimSliceBox::from_vec),
1611            test_slices().map(Box::from).map(SlimSliceBox::from_boxed),
1612            test_slices().map(|s| SlimSliceBox::try_from(s).unwrap()),
1613            test_slices().map(|s| SlimSliceBox::try_from(s.into_boxed_slice()).unwrap()),
1614            test_slices().map(|s| SlimSliceBox::from(<[u8; 3]>::try_from(s).unwrap())),
1615        ]
1616    }
1617
1618    fn various_boxed_strs() -> [[SlimStrBox; 2]; 7] {
1619        [
1620            [nstr!("foo"), nstr!("fop")],
1621            test_strings().map(|s| from_string(&s)),
1622            test_strings().map(SlimStrBox::from_string),
1623            test_strings().map(Box::from).map(SlimStrBox::from_boxed),
1624            test_strings().map(|s| SlimStrBox::try_from(s).unwrap()),
1625            test_strings().map(|s| SlimStrBox::try_from(s.into_boxed_str()).unwrap()),
1626            test_strings().map(|s| SlimStrBox::try_from(s.deref()).unwrap()),
1627        ]
1628    }
1629
1630    fn assert_str_mut_properties(s1: &mut SlimStrMut<'_>, s2: &mut SlimStrMut<'_>) {
1631        let a: &SlimStrMut<'_> = s1;
1632        let b: &SlimStrMut<'_> = s2;
1633
1634        assert_eq!(a.deref(), TEST_STR);
1635        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);
1636        assert_eq!(b.deref(), TEST_STR2);
1637
1638        assert_eq!(String::from(a), TEST_STR);
1639        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);
1640        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);
1641        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);
1642
1643        general_properties(a, b, TEST_STR, TEST_STR2);
1644        display_properties(a, b, TEST_STR, TEST_STR2);
1645
1646        s1.deref_mut().make_ascii_uppercase();
1647        assert_eq!(&**s1, TEST_STR.to_uppercase());
1648    }
1649
1650    #[test]
1651    fn str_mut_call() {
1652        let [mut s1, mut s2] = test_strings();
1653        let s1 = &mut from_str_mut(s1.as_mut_str());
1654        let s2 = &mut from_str_mut(s2.as_mut_str());
1655        assert_str_mut_properties(s1, s2);
1656    }
1657
1658    #[test]
1659    fn str_mut_try_into() {
1660        let [mut s1, mut s2] = test_strings();
1661        let s1: &mut SlimStrMut = &mut s1.as_mut().try_into().unwrap();
1662        let s2: &mut SlimStrMut = &mut s2.as_mut().try_into().unwrap();
1663        assert_str_mut_properties(s1, s2);
1664    }
1665
1666    #[test]
1667    fn str_mut_exclusive_ref_various() {
1668        for [mut a, mut b] in various_boxed_strs() {
1669            assert_str_mut_properties(a.exclusive_ref(), b.exclusive_ref())
1670        }
1671    }
1672
1673    fn assert_str_properties(a: &SlimStr<'_>, b: &SlimStr<'_>) {
1674        assert_eq!(a.deref(), TEST_STR);
1675        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);
1676        assert_eq!(b.deref(), TEST_STR2);
1677
1678        assert_eq!(String::from(a), TEST_STR);
1679        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);
1680        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);
1681        assert_eq!(String::from(SlimStrBox::from(a)).deref(), TEST_STR);
1682        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);
1683
1684        general_properties(a, b, TEST_STR, TEST_STR2);
1685        display_properties(a, b, TEST_STR, TEST_STR2);
1686    }
1687
1688    #[test]
1689    fn str_call() {
1690        let [s1, s2] = test_strings();
1691        assert_str_properties(&from_str(&s1), &from_str(&s2));
1692    }
1693
1694    #[test]
1695    fn str_try_into() {
1696        let [s1, s2] = test_strings();
1697        let s1: &SlimStr = &mut s1.deref().try_into().unwrap();
1698        let s2: &SlimStr = &mut s2.deref().try_into().unwrap();
1699        assert_str_properties(s1, s2);
1700    }
1701
1702    #[test]
1703    fn str_shared_ref_various() {
1704        for [a, b] in various_boxed_strs() {
1705            assert_str_properties(a.shared_ref(), b.shared_ref())
1706        }
1707    }
1708
1709    fn assert_slice_mut_properties(s1: &mut SlimSliceMut<'_, u8>, s2: &mut SlimSliceMut<'_, u8>) {
1710        let a: &SlimSliceMut<'_, u8> = s1;
1711        let b: &SlimSliceMut<'_, u8> = s2;
1712
1713        assert_eq!(a.deref(), TEST_SLICE);
1714        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);
1715        assert_eq!(b.deref(), TEST_SLICE2);
1716
1717        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);
1718        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);
1719        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);
1720        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);
1721
1722        general_properties(a, b, TEST_SLICE, TEST_SLICE2);
1723
1724        s1.deref_mut().make_ascii_uppercase();
1725        let mut upper = TEST_SLICE.to_owned();
1726        upper.iter_mut().for_each(|x| x.make_ascii_uppercase());
1727        assert_eq!(&**s1, upper);
1728    }
1729
1730    #[test]
1731    fn slice_mut_call() {
1732        let [mut s1, mut s2] = test_slices();
1733        let s1 = &mut from_slice_mut(s1.as_mut());
1734        let s2 = &mut from_slice_mut(s2.as_mut());
1735        assert_slice_mut_properties(s1, s2);
1736    }
1737
1738    #[test]
1739    fn slice_mut_try_into() {
1740        let [mut s1, mut s2] = test_slices();
1741        let s1: &mut SlimSliceMut<u8> = &mut s1.deref_mut().try_into().unwrap();
1742        let s2: &mut SlimSliceMut<u8> = &mut s2.deref_mut().try_into().unwrap();
1743        assert_slice_mut_properties(s1, s2);
1744    }
1745
1746    #[test]
1747    fn slice_mut_exclusive_ref_various() {
1748        for [mut a, mut b] in various_boxed_slices() {
1749            assert_slice_mut_properties(a.exclusive_ref(), b.exclusive_ref());
1750        }
1751    }
1752
1753    fn assert_slice_properties(a: &SlimSlice<'_, u8>, b: &SlimSlice<'_, u8>) {
1754        assert_eq!(a.deref(), TEST_SLICE);
1755        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);
1756        assert_eq!(b.deref(), TEST_SLICE2);
1757
1758        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);
1759        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);
1760        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);
1761        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);
1762
1763        general_properties(a, b, TEST_SLICE, TEST_SLICE2);
1764    }
1765
1766    #[test]
1767    fn slice_call() {
1768        let [s1, s2] = test_slices();
1769        assert_slice_properties(&from_slice(&s1), &from_slice(&s2));
1770    }
1771
1772    #[test]
1773    fn slice_try_into() {
1774        let [s1, s2] = test_slices();
1775        let s1: &SlimSlice<u8> = &s1.deref().try_into().unwrap();
1776        let s2: &SlimSlice<u8> = &s2.deref().try_into().unwrap();
1777        assert_slice_properties(s1, s2);
1778    }
1779
1780    #[test]
1781    fn slice_shared_ref_various() {
1782        for [a, b] in various_boxed_slices() {
1783            assert_slice_properties(a.shared_ref(), b.shared_ref())
1784        }
1785    }
1786}