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 panicking,
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 version 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>`, panicking on overflow
36//! - [`from_slice_mut`] converts `&mut [T] -> SlimSliceMut<T>`, panicking on overflow
37//! - [`from_str`] converts `&str -> SlimStr`, panicking on overflow
38//! - [`from_str_mut`] converts `&mut str -> SlimStrMut`, panicking on overflow
39//! - [`from_string`] converts `&str -> SlimStrBox`, panicking 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::{self, Debug, Display},
64    hash::{Hash, Hasher},
65    marker::PhantomData,
66    mem::{self, ManuallyDrop},
67    ops::{Deref, DerefMut},
68    ptr::{slice_from_raw_parts_mut, NonNull},
69    slice,
70    str::{from_utf8_unchecked, from_utf8_unchecked_mut},
71};
72use smallvec::SmallVec;
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(Rust, 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
577#[derive(Clone)]
578pub struct SlimSmallSliceBox<T, const N: usize>(SlimSmallSliceBoxData<T, N>);
579
580/// The representation of [`SlimSmallSliceBox<T>`].
581///
582/// The parameter `N` is the number of elements that can be inline.
583#[derive(Clone)]
584enum SlimSmallSliceBoxData<T, const N: usize> {
585    /// The data is inline, not using any indirections.
586    Inline([T; N]),
587    /// The data is boxed up.
588    Heap(SlimSliceBox<T>),
589}
590
591impl<T, const N: usize> From<[T; N]> for SlimSmallSliceBox<T, N> {
592    fn from(value: [T; N]) -> Self {
593        #[allow(clippy::let_unit_value)]
594        let () = AssertU32::<N>::OK;
595
596        Self(SlimSmallSliceBoxData::Inline(value))
597    }
598}
599
600impl<T, const N: usize> From<SlimSliceBox<T>> for SlimSmallSliceBox<T, N> {
601    fn from(value: SlimSliceBox<T>) -> Self {
602        Self(SlimSmallSliceBoxData::Heap(value))
603    }
604}
605
606impl<T, const N: usize> From<SlimSmallSliceBox<T, N>> for SlimSliceBox<T> {
607    fn from(SlimSmallSliceBox(value): SlimSmallSliceBox<T, N>) -> Self {
608        match value {
609            SlimSmallSliceBoxData::Inline(i) => i.into(),
610            SlimSmallSliceBoxData::Heap(h) => h,
611        }
612    }
613}
614
615impl<T, const N: usize> Deref for SlimSmallSliceBox<T, N> {
616    type Target = [T];
617    fn deref(&self) -> &Self::Target {
618        match &self.0 {
619            SlimSmallSliceBoxData::Inline(i) => i,
620            SlimSmallSliceBoxData::Heap(h) => h,
621        }
622    }
623}
624
625impl<T, const N: usize> DerefMut for SlimSmallSliceBox<T, N> {
626    fn deref_mut(&mut self) -> &mut Self::Target {
627        match &mut self.0 {
628            SlimSmallSliceBoxData::Inline(i) => i,
629            SlimSmallSliceBoxData::Heap(h) => h,
630        }
631    }
632}
633
634impl<T: PartialEq, const N: usize> PartialEq for SlimSmallSliceBox<T, N> {
635    fn eq(&self, other: &Self) -> bool {
636        self.deref().eq(other.deref())
637    }
638}
639
640impl<T: Eq, const N: usize> Eq for SlimSmallSliceBox<T, N> {}
641
642impl<T: Debug, const N: usize> fmt::Debug for SlimSmallSliceBox<T, N> {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644        Debug::fmt(self.deref(), f)
645    }
646}
647
648impl<T, const N: usize> From<SmallVec<[T; N]>> for SlimSmallSliceBox<T, N> {
649    fn from(value: SmallVec<[T; N]>) -> Self {
650        match value.into_inner() {
651            Ok(inline) => inline.into(),
652            Err(heap) => SlimSliceBox::from_boxed(heap.into_boxed_slice()).into(),
653        }
654    }
655}
656
657// =============================================================================
658// Owned boxed string slice
659// =============================================================================
660
661/// Provides a slimmer version of `Box<str>`
662/// using `u32` for its length instead of `usize`.
663#[repr(transparent)]
664pub struct SlimStrBox {
665    /// The underlying byte slice.
666    raw: SlimSliceBox<u8>,
667}
668
669#[cfg(feature = "serde")]
670impl serde::Serialize for SlimStrBox {
671    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
672        s.serialize_str(self.deref())
673    }
674}
675
676impl SlimStrBox {
677    /// Converts `boxed` to `Self` without checking `boxed.len() <= u32::MAX`.
678    ///
679    /// # Safety
680    ///
681    /// The caller must ensure that `boxed.len() <= u32::MAX`.
682    #[inline]
683    pub unsafe fn from_boxed_unchecked(boxed: Box<str>) -> Self {
684        // SAFETY: Caller has promised that `boxed.len() <= u32::MAX`.
685        let raw = unsafe { SlimSliceBox::from_boxed_unchecked(into_box(boxed)) };
686        Self { raw }
687    }
688
689    /// Converts `boxed: Box<str>` into `SlimStrBox`.
690    ///
691    /// Panics when `boxed.len() > u32::MAX`.
692    #[inline]
693    pub fn from_boxed(boxed: Box<str>) -> Self {
694        expect_fit(boxed)
695    }
696
697    /// Converts `str: String` into `SlimStrBox`.
698    ///
699    /// Panics when `str.len() > u32::MAX`.
700    #[inline]
701    pub fn from_string(str: String) -> Self {
702        Self::from_boxed(str.into())
703    }
704
705    /// Returns a limited shared string slice to this boxed string slice.
706    #[allow(clippy::needless_lifetimes)]
707    #[inline]
708    pub fn shared_ref<'a>(&'a self) -> &'a SlimStr<'a> {
709        // SAFETY: The reference lives as long as `self`,
710        // we have shared access already,
711        // and by construction we know it's UTF-8.
712        unsafe { mem::transmute(self.raw.shared_ref()) }
713    }
714
715    /// Returns a limited mutable string slice to this boxed string slice.
716    #[allow(clippy::needless_lifetimes)]
717    #[inline]
718    pub fn exclusive_ref<'a>(&'a mut self) -> &'a mut SlimStrMut<'a> {
719        // SAFETY: The reference lives as long as `self`,
720        // we had `&mut self`,
721        // and by construction we know it's UTF-8.
722        unsafe { mem::transmute(self.raw.exclusive_ref()) }
723    }
724}
725
726impl Deref for SlimStrBox {
727    type Target = str;
728
729    #[inline]
730    fn deref(&self) -> &Self::Target {
731        self.shared_ref().deref()
732    }
733}
734
735impl DerefMut for SlimStrBox {
736    #[inline]
737    fn deref_mut(&mut self) -> &mut Self::Target {
738        self.exclusive_ref().deref_mut()
739    }
740}
741
742impl<const N: usize> From<NStr<N>> for SlimStrBox {
743    #[inline]
744    fn from(arr: NStr<N>) -> Self {
745        (&arr).into()
746    }
747}
748
749impl<const N: usize> From<&NStr<N>> for SlimStrBox {
750    #[inline]
751    fn from(arr: &NStr<N>) -> Self {
752        <SlimStr<'_>>::from(arr).into()
753    }
754}
755
756impl TryFrom<Box<str>> for SlimStrBox {
757    type Error = LenTooLong<Box<str>>;
758
759    #[inline]
760    fn try_from(boxed: Box<str>) -> Result<Self, Self::Error> {
761        ensure_len_fits!(boxed);
762        // SAFETY: Checked above that `len <= u32::MAX`.
763        Ok(unsafe { Self::from_boxed_unchecked(boxed) })
764    }
765}
766
767impl TryFrom<String> for SlimStrBox {
768    type Error = LenTooLong<String>;
769
770    #[inline]
771    fn try_from(str: String) -> Result<Self, Self::Error> {
772        ensure_len_fits!(str);
773        // SAFETY: Checked above that `len <= u32::MAX`.
774        Ok(unsafe { Self::from_boxed_unchecked(str.into_boxed_str()) })
775    }
776}
777
778impl<'a> TryFrom<&'a str> for SlimStrBox {
779    type Error = LenTooLong<&'a str>;
780
781    #[inline]
782    fn try_from(str: &'a str) -> Result<Self, Self::Error> {
783        str.try_into().map(|s: SlimStr<'_>| s.into())
784    }
785}
786
787impl From<SlimStrBox> for Box<str> {
788    #[inline]
789    fn from(str: SlimStrBox) -> Self {
790        let raw_box = into_box(str.raw);
791        // SAFETY: By construction, `SlimStrBox` is valid UTF-8.
792        unsafe { Box::from_raw(Box::into_raw(raw_box) as *mut str) }
793    }
794}
795
796impl From<SlimStrBox> for String {
797    #[inline]
798    fn from(str: SlimStrBox) -> Self {
799        <Box<str>>::from(str).into()
800    }
801}
802
803impl Debug for SlimStrBox {
804    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805        Debug::fmt(self.deref(), f)
806    }
807}
808
809impl Display for SlimStrBox {
810    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
811        Display::fmt(self.deref(), f)
812    }
813}
814
815impl Clone for SlimStrBox {
816    #[inline]
817    fn clone(&self) -> Self {
818        Self { raw: self.raw.clone() }
819    }
820}
821
822impl<R: Deref> PartialEq<R> for SlimStrBox
823where
824    str: PartialEq<R::Target>,
825{
826    #[inline]
827    fn eq(&self, other: &R) -> bool {
828        self.deref() == other.deref()
829    }
830}
831
832impl Eq for SlimStrBox {}
833
834impl<R: Deref> PartialOrd<R> for SlimStrBox
835where
836    str: PartialOrd<R::Target>,
837{
838    #[inline]
839    fn partial_cmp(&self, other: &R) -> Option<Ordering> {
840        self.deref().partial_cmp(other.deref())
841    }
842}
843
844impl Ord for SlimStrBox {
845    #[inline]
846    fn cmp(&self, other: &Self) -> Ordering {
847        self.deref().cmp(other.deref())
848    }
849}
850
851impl Hash for SlimStrBox {
852    #[inline]
853    fn hash<H: Hasher>(&self, state: &mut H) {
854        Hash::hash(self.deref(), state)
855    }
856}
857
858impl Borrow<str> for SlimStrBox {
859    #[inline]
860    fn borrow(&self) -> &str {
861        self
862    }
863}
864
865// =============================================================================
866// Shared slice reference
867// =============================================================================
868
869#[allow(clippy::module_inception)]
870mod slim_slice {
871    use super::*;
872
873    /// A shared reference to `[T]` limited to `u32::MAX` in length.
874    #[repr(transparent)]
875    #[derive(Clone, Copy)]
876    pub struct SlimSlice<'a, T> {
877        /// The representation of this shared slice.
878        raw: SlimRawSlice<T>,
879        /// Marker to ensure covariance for `'a`.
880        covariant: PhantomData<&'a [T]>,
881    }
882
883    impl<'a, T> SlimSlice<'a, T> {
884        /// Converts a `&[T]` to the limited version without length checking.
885        ///
886        /// SAFETY: `slice.len() <= u32::MAX` must hold.
887        pub(super) const unsafe fn from_slice_unchecked(slice: &'a [T]) -> Self {
888            let len = slice.len();
889            let ptr = slice.as_ptr().cast_mut();
890            // SAFETY: `&mut [T]` implies that the pointer is non-null.
891            let raw = SlimRawSlice::from_len_ptr(len, ptr);
892            // SAFETY: Our length invariant is satisfied by the caller.
893            let covariant = PhantomData;
894            Self { raw, covariant }
895        }
896    }
897
898    impl<T> Deref for SlimSlice<'_, T> {
899        type Target = [T];
900
901        fn deref(&self) -> &Self::Target {
902            // SAFETY: `ptr` and `len` are either
903            // a) derived from a live `Box<[T]>` valid for `'self`
904            // b) derived from a live `&'self [T]`
905            // so we satisfy all safety requirements for `from_raw_parts`.
906            unsafe { self.raw.deref() }
907        }
908    }
909}
910pub use slim_slice::*;
911
912use crate::nstr::NStr;
913
914// SAFETY: Same rules as for `&[T]`.
915unsafe impl<T: Send + Sync> Send for SlimSlice<'_, T> {}
916
917// SAFETY: Same rules as for `&[T]`.
918unsafe impl<T: Sync> Sync for SlimSlice<'_, T> {}
919
920impl<T> SlimSlice<'_, T> {
921    /// Falibly maps all elements with `by` to `Result<U, E>`.
922    ///
923    /// Returns `Err(_)` if any call to `by` did.
924    #[inline]
925    pub fn try_map<U, E>(&self, by: impl FnMut(&T) -> Result<U, E>) -> Result<SlimSliceBox<U>, E> {
926        let mapped = self.iter().map(by).collect::<Result<_, _>>()?;
927        // SAFETY: Doing `.map(..)` can never change the length.
928        Ok(unsafe { SlimSliceBox::from_boxed_unchecked(mapped) })
929    }
930}
931
932impl<T: Debug> Debug for SlimSlice<'_, T> {
933    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
934        Debug::fmt(self.deref(), f)
935    }
936}
937
938impl<T: Hash> Hash for SlimSlice<'_, T> {
939    #[inline]
940    fn hash<H: Hasher>(&self, state: &mut H) {
941        Hash::hash(self.deref(), state)
942    }
943}
944
945impl<T: Eq> Eq for SlimSlice<'_, T> {}
946impl<T: PartialEq> PartialEq for SlimSlice<'_, T> {
947    #[inline]
948    fn eq(&self, other: &Self) -> bool {
949        self.deref() == other.deref()
950    }
951}
952impl<T: PartialEq> PartialEq<[T]> for SlimSlice<'_, T> {
953    #[inline]
954    fn eq(&self, other: &[T]) -> bool {
955        self.deref() == other
956    }
957}
958
959impl<T: Ord> Ord for SlimSlice<'_, T> {
960    #[inline]
961    fn cmp(&self, other: &Self) -> Ordering {
962        self.deref().cmp(other)
963    }
964}
965impl<T: PartialOrd> PartialOrd for SlimSlice<'_, T> {
966    #[inline]
967    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
968        self.deref().partial_cmp(other.deref())
969    }
970}
971impl<T: PartialOrd> PartialOrd<[T]> for SlimSlice<'_, T> {
972    #[inline]
973    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
974        self.deref().partial_cmp(other)
975    }
976}
977
978impl<T: Clone> From<&SlimSlice<'_, T>> for SlimSliceBox<T> {
979    #[inline]
980    fn from(slice: &SlimSlice<'_, T>) -> Self {
981        let boxed = into_box(slice.deref());
982        // SAFETY: `slice` is limited to `len: u32` by construction.
983        unsafe { Self::from_boxed_unchecked(boxed) }
984    }
985}
986impl<T: Clone> From<&SlimSlice<'_, T>> for Box<[T]> {
987    #[inline]
988    fn from(slice: &SlimSlice<'_, T>) -> Self {
989        slice.deref().into()
990    }
991}
992impl<T: Clone> From<&SlimSlice<'_, T>> for Vec<T> {
993    #[inline]
994    fn from(slice: &SlimSlice<'_, T>) -> Self {
995        slice.deref().into()
996    }
997}
998
999impl<T: Clone> From<SlimSlice<'_, T>> for SlimSliceBox<T> {
1000    #[inline]
1001    fn from(slice: SlimSlice<'_, T>) -> Self {
1002        (&slice).into()
1003    }
1004}
1005impl<T: Clone> From<SlimSlice<'_, T>> for Box<[T]> {
1006    #[inline]
1007    fn from(slice: SlimSlice<'_, T>) -> Self {
1008        slice.deref().into()
1009    }
1010}
1011impl<T: Clone> From<SlimSlice<'_, T>> for Vec<T> {
1012    #[inline]
1013    fn from(slice: SlimSlice<'_, T>) -> Self {
1014        slice.deref().into()
1015    }
1016}
1017
1018impl<'a, T> TryFrom<&'a [T]> for SlimSlice<'a, T> {
1019    type Error = LenTooLong<&'a [T]>;
1020
1021    #[inline]
1022    fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
1023        ensure_len_fits!(slice);
1024        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1025        Ok(unsafe { Self::from_slice_unchecked(slice) })
1026    }
1027}
1028
1029/// Converts `&[T]` into the slim limited version.
1030///
1031/// Panics when `slice.len() > u32::MAX`.
1032#[inline]
1033pub fn from_slice<T>(s: &[T]) -> SlimSlice<'_, T> {
1034    expect_fit(s)
1035}
1036
1037// =============================================================================
1038// Mutable slice reference
1039// =============================================================================
1040
1041/// A mutable reference to `[T]` limited to `u32::MAX` in length.
1042#[repr(transparent)]
1043pub struct SlimSliceMut<'a, T> {
1044    /// The representation of this mutable slice.
1045    raw: SlimRawSlice<T>,
1046    /// Marker to ensure invariance for `'a`.
1047    invariant: PhantomData<&'a mut [T]>,
1048}
1049
1050// SAFETY: Same rules as for `&mut [T]`.
1051unsafe impl<T: Send> Send for SlimSliceMut<'_, T> {}
1052
1053// SAFETY: Same rules as for `&mut [T]`.
1054unsafe impl<T: Sync> Sync for SlimSliceMut<'_, T> {}
1055
1056impl<'a, T> SlimSliceMut<'a, T> {
1057    /// Convert this mutable reference to a shared one.
1058    #[inline]
1059    pub fn shared(&'a self) -> &'a SlimSlice<'a, T> {
1060        // SAFETY: By virtue of `&'a mut X -> &'a X` being sound, this is also.
1061        // The types and references to them have the same layout as well.
1062        unsafe { mem::transmute(self) }
1063    }
1064
1065    /// Converts a `&mut [T]` to the limited version without length checking.
1066    ///
1067    /// SAFETY: `slice.len() <= u32::MAX` must hold.
1068    #[inline]
1069    unsafe fn from_slice_unchecked(slice: &'a mut [T]) -> Self {
1070        // SAFETY: `&mut [T]` implies that the pointer is non-null.
1071        let raw = SlimRawSlice::from_len_ptr(slice.len(), slice.as_mut_ptr());
1072        // SAFETY: Our invariants are satisfied by the caller
1073        // and that `&mut [T]` implies exclusive access to the data.
1074        let invariant = PhantomData;
1075        Self { raw, invariant }
1076    }
1077}
1078
1079impl<T> Deref for SlimSliceMut<'_, T> {
1080    type Target = [T];
1081
1082    #[inline]
1083    fn deref(&self) -> &Self::Target {
1084        self.shared().deref()
1085    }
1086}
1087
1088impl<T> DerefMut for SlimSliceMut<'_, T> {
1089    #[inline]
1090    fn deref_mut(&mut self) -> &mut Self::Target {
1091        // SAFETY: `ptr` and `len` are either
1092        // a) derived from a live `Box<[T]>` valid for `'self`
1093        // b) derived from a live `&'self [T]`
1094        // and additionally, we have the only pointer to the data.
1095        // so we satisfy all safety requirements for `from_raw_parts_mut`.
1096        unsafe { self.raw.deref_mut() }
1097    }
1098}
1099
1100impl<T: Debug> Debug for SlimSliceMut<'_, T> {
1101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1102        Debug::fmt(self.deref(), f)
1103    }
1104}
1105
1106impl<T: Hash> Hash for SlimSliceMut<'_, T> {
1107    #[inline]
1108    fn hash<H: Hasher>(&self, state: &mut H) {
1109        Hash::hash(self.deref(), state)
1110    }
1111}
1112
1113impl<T: Eq> Eq for SlimSliceMut<'_, T> {}
1114impl<T: PartialEq> PartialEq for SlimSliceMut<'_, T> {
1115    #[inline]
1116    fn eq(&self, other: &Self) -> bool {
1117        self.deref() == other.deref()
1118    }
1119}
1120impl<T: PartialEq> PartialEq<[T]> for SlimSliceMut<'_, T> {
1121    #[inline]
1122    fn eq(&self, other: &[T]) -> bool {
1123        self.deref() == other
1124    }
1125}
1126
1127impl<T: Ord> Ord for SlimSliceMut<'_, T> {
1128    #[inline]
1129    fn cmp(&self, other: &Self) -> Ordering {
1130        self.deref().cmp(other)
1131    }
1132}
1133impl<T: PartialOrd> PartialOrd for SlimSliceMut<'_, T> {
1134    #[inline]
1135    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1136        self.deref().partial_cmp(other.deref())
1137    }
1138}
1139impl<T: PartialOrd> PartialOrd<[T]> for SlimSliceMut<'_, T> {
1140    #[inline]
1141    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
1142        self.deref().partial_cmp(other)
1143    }
1144}
1145
1146impl<T: Clone> From<&SlimSliceMut<'_, T>> for SlimSliceBox<T> {
1147    #[inline]
1148    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1149        // SAFETY: `slice` is limited to `len: u32` by construction.
1150        unsafe { Self::from_boxed_unchecked(into_box(slice.deref())) }
1151    }
1152}
1153impl<T: Clone> From<&SlimSliceMut<'_, T>> for Box<[T]> {
1154    #[inline]
1155    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1156        slice.deref().into()
1157    }
1158}
1159impl<T: Clone> From<&SlimSliceMut<'_, T>> for Vec<T> {
1160    #[inline]
1161    fn from(slice: &SlimSliceMut<'_, T>) -> Self {
1162        slice.deref().into()
1163    }
1164}
1165
1166impl<T: Clone> From<SlimSliceMut<'_, T>> for SlimSliceBox<T> {
1167    #[inline]
1168    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1169        (&slice).into()
1170    }
1171}
1172impl<T: Clone> From<SlimSliceMut<'_, T>> for Box<[T]> {
1173    #[inline]
1174    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1175        slice.deref().into()
1176    }
1177}
1178impl<T: Clone> From<SlimSliceMut<'_, T>> for Vec<T> {
1179    #[inline]
1180    fn from(slice: SlimSliceMut<'_, T>) -> Self {
1181        slice.deref().into()
1182    }
1183}
1184
1185impl<'a, T> TryFrom<&'a mut [T]> for SlimSliceMut<'a, T> {
1186    type Error = LenTooLong<&'a mut [T]>;
1187
1188    #[inline]
1189    fn try_from(slice: &'a mut [T]) -> Result<Self, Self::Error> {
1190        ensure_len_fits!(slice);
1191        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1192        Ok(unsafe { Self::from_slice_unchecked(slice) })
1193    }
1194}
1195
1196/// Converts `&mut [T]` into the slim limited version.
1197///
1198/// Panics when `slice.len() > u32::MAX`.
1199#[inline]
1200pub fn from_slice_mut<T>(s: &mut [T]) -> SlimSliceMut<'_, T> {
1201    expect_fit(s)
1202}
1203
1204// =============================================================================
1205// Shared string slice reference
1206// =============================================================================
1207
1208/// A shared reference to `str` limited to `u32::MAX` in length.
1209#[repr(transparent)]
1210#[derive(Clone, Copy)]
1211pub struct SlimStr<'a> {
1212    /// The raw byte slice.
1213    raw: SlimSlice<'a, u8>,
1214}
1215
1216impl<'a> SlimStr<'a> {
1217    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.
1218    ///
1219    /// # Safety
1220    ///
1221    /// The caller must ensure that `s.len() <= u32::MAX`.
1222    #[inline]
1223    const unsafe fn from_str_unchecked(s: &'a str) -> Self {
1224        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.
1225        let raw = unsafe { SlimSlice::from_slice_unchecked(s.as_bytes()) };
1226        // SAFETY: `s: &str` is always UTF-8.
1227        Self { raw }
1228    }
1229}
1230
1231impl Deref for SlimStr<'_> {
1232    type Target = str;
1233
1234    #[inline]
1235    fn deref(&self) -> &Self::Target {
1236        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1237        unsafe { from_utf8_unchecked(self.raw.deref()) }
1238    }
1239}
1240
1241impl Debug for SlimStr<'_> {
1242    #[inline]
1243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1244        Debug::fmt(self.deref(), f)
1245    }
1246}
1247
1248impl Display for SlimStr<'_> {
1249    #[inline]
1250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1251        Display::fmt(self.deref(), f)
1252    }
1253}
1254
1255impl Hash for SlimStr<'_> {
1256    #[inline]
1257    fn hash<H: Hasher>(&self, state: &mut H) {
1258        Hash::hash(self.deref(), state)
1259    }
1260}
1261
1262impl Eq for SlimStr<'_> {}
1263impl PartialEq for SlimStr<'_> {
1264    #[inline]
1265    fn eq(&self, other: &Self) -> bool {
1266        self.deref() == other.deref()
1267    }
1268}
1269impl PartialEq<str> for SlimStr<'_> {
1270    #[inline]
1271    fn eq(&self, other: &str) -> bool {
1272        self.deref() == other
1273    }
1274}
1275
1276impl Ord for SlimStr<'_> {
1277    #[inline]
1278    fn cmp(&self, other: &Self) -> Ordering {
1279        self.deref().cmp(other)
1280    }
1281}
1282impl PartialOrd for SlimStr<'_> {
1283    #[inline]
1284    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1285        Some(self.cmp(other))
1286    }
1287}
1288impl PartialOrd<str> for SlimStr<'_> {
1289    #[inline]
1290    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1291        self.deref().partial_cmp(other)
1292    }
1293}
1294
1295impl From<&SlimStr<'_>> for SlimStrBox {
1296    #[inline]
1297    fn from(slice: &SlimStr<'_>) -> Self {
1298        (*slice).into()
1299    }
1300}
1301impl From<&SlimStr<'_>> for Box<str> {
1302    #[inline]
1303    fn from(slice: &SlimStr<'_>) -> Self {
1304        slice.deref().into()
1305    }
1306}
1307impl From<&SlimStr<'_>> for String {
1308    #[inline]
1309    fn from(slice: &SlimStr<'_>) -> Self {
1310        slice.deref().into()
1311    }
1312}
1313
1314impl From<SlimStr<'_>> for SlimStrBox {
1315    #[inline]
1316    fn from(slice: SlimStr<'_>) -> Self {
1317        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.
1318        Self { raw: slice.raw.into() }
1319    }
1320}
1321impl From<SlimStr<'_>> for Box<str> {
1322    #[inline]
1323    fn from(slice: SlimStr<'_>) -> Self {
1324        slice.deref().into()
1325    }
1326}
1327impl From<SlimStr<'_>> for String {
1328    #[inline]
1329    fn from(slice: SlimStr<'_>) -> Self {
1330        slice.deref().into()
1331    }
1332}
1333
1334impl<'a, const N: usize> From<&'a NStr<N>> for SlimStr<'a> {
1335    #[inline]
1336    fn from(arr: &'a NStr<N>) -> Self {
1337        #[allow(clippy::let_unit_value)]
1338        let () = AssertU32::<N>::OK;
1339
1340        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.
1341        unsafe { Self::from_str_unchecked(arr) }
1342    }
1343}
1344impl<'a> TryFrom<&'a str> for SlimStr<'a> {
1345    type Error = LenTooLong<&'a str>;
1346
1347    #[inline]
1348    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1349        ensure_len_fits!(s);
1350        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1351        Ok(unsafe { Self::from_str_unchecked(s) })
1352    }
1353}
1354
1355/// Converts `&str` into the slim limited version.
1356///
1357/// Panics when `str.len() > u32::MAX`.
1358#[inline]
1359pub const fn from_str(s: &str) -> SlimStr<'_> {
1360    if s.len() > u32::MAX as usize {
1361        panic!("length didn't fit in `u32`");
1362    }
1363
1364    // SAFETY: ^-- satisfies `len <= u32::MAX`.
1365    unsafe { SlimStr::from_str_unchecked(s) }
1366}
1367
1368/// Converts `&str` into the owned slim limited version.
1369///
1370/// Panics when `str.len() > u32::MAX`.
1371#[inline]
1372pub fn from_string(s: &str) -> SlimStrBox {
1373    from_str(s).into()
1374}
1375
1376// =============================================================================
1377// Mutable string slice reference
1378// =============================================================================
1379
1380/// A mutable reference to `str` limited to `u32::MAX` in length.
1381#[repr(transparent)]
1382pub struct SlimStrMut<'a> {
1383    /// The raw byte slice.
1384    raw: SlimSliceMut<'a, u8>,
1385}
1386
1387impl<'a> SlimStrMut<'a> {
1388    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.
1389    ///
1390    /// # Safety
1391    ///
1392    /// The caller must ensure that `s.len() <= u32::MAX`.
1393    #[inline]
1394    unsafe fn from_str_unchecked(s: &'a mut str) -> Self {
1395        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.
1396        let raw = unsafe { SlimSliceMut::from_slice_unchecked(s.as_bytes_mut()) };
1397        // SAFETY: `s: &mut str` is always UTF-8.
1398        Self { raw }
1399    }
1400}
1401
1402impl Deref for SlimStrMut<'_> {
1403    type Target = str;
1404
1405    #[inline]
1406    fn deref(&self) -> &Self::Target {
1407        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1408        unsafe { from_utf8_unchecked(self.raw.deref()) }
1409    }
1410}
1411
1412impl DerefMut for SlimStrMut<'_> {
1413    #[inline]
1414    fn deref_mut(&mut self) -> &mut Self::Target {
1415        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.
1416        unsafe { from_utf8_unchecked_mut(self.raw.deref_mut()) }
1417    }
1418}
1419
1420impl Debug for SlimStrMut<'_> {
1421    #[inline]
1422    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1423        Debug::fmt(self.deref(), f)
1424    }
1425}
1426
1427impl Display for SlimStrMut<'_> {
1428    #[inline]
1429    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1430        Display::fmt(self.deref(), f)
1431    }
1432}
1433
1434impl Hash for SlimStrMut<'_> {
1435    #[inline]
1436    fn hash<H: Hasher>(&self, state: &mut H) {
1437        Hash::hash(self.deref(), state)
1438    }
1439}
1440
1441impl Eq for SlimStrMut<'_> {}
1442impl PartialEq for SlimStrMut<'_> {
1443    #[inline]
1444    fn eq(&self, other: &Self) -> bool {
1445        self.deref() == other.deref()
1446    }
1447}
1448impl PartialEq<str> for SlimStrMut<'_> {
1449    #[inline]
1450    fn eq(&self, other: &str) -> bool {
1451        self.deref() == other
1452    }
1453}
1454
1455impl Ord for SlimStrMut<'_> {
1456    #[inline]
1457    fn cmp(&self, other: &Self) -> Ordering {
1458        self.deref().cmp(other)
1459    }
1460}
1461impl PartialOrd for SlimStrMut<'_> {
1462    #[inline]
1463    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1464        Some(self.cmp(other))
1465    }
1466}
1467impl PartialOrd<str> for SlimStrMut<'_> {
1468    #[inline]
1469    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1470        self.deref().partial_cmp(other)
1471    }
1472}
1473
1474impl From<&SlimStrMut<'_>> for SlimStrBox {
1475    #[inline]
1476    fn from(slice: &SlimStrMut<'_>) -> Self {
1477        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.
1478        Self {
1479            raw: (&slice.raw).into(),
1480        }
1481    }
1482}
1483impl From<&SlimStrMut<'_>> for Box<str> {
1484    #[inline]
1485    fn from(slice: &SlimStrMut<'_>) -> Self {
1486        slice.deref().into()
1487    }
1488}
1489impl From<&SlimStrMut<'_>> for String {
1490    #[inline]
1491    fn from(slice: &SlimStrMut<'_>) -> Self {
1492        slice.deref().into()
1493    }
1494}
1495
1496impl From<SlimStrMut<'_>> for SlimStrBox {
1497    #[inline]
1498    fn from(slice: SlimStrMut<'_>) -> Self {
1499        (&slice).into()
1500    }
1501}
1502impl From<SlimStrMut<'_>> for Box<str> {
1503    #[inline]
1504    fn from(slice: SlimStrMut<'_>) -> Self {
1505        slice.deref().into()
1506    }
1507}
1508impl From<SlimStrMut<'_>> for String {
1509    #[inline]
1510    fn from(slice: SlimStrMut<'_>) -> Self {
1511        slice.deref().into()
1512    }
1513}
1514
1515impl<'a, const N: usize> From<&'a mut NStr<N>> for SlimStrMut<'a> {
1516    #[inline]
1517    fn from(arr: &'a mut NStr<N>) -> Self {
1518        #[allow(clippy::let_unit_value)]
1519        let () = AssertU32::<N>::OK;
1520
1521        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.
1522        unsafe { Self::from_str_unchecked(arr) }
1523    }
1524}
1525impl<'a> TryFrom<&'a mut str> for SlimStrMut<'a> {
1526    type Error = LenTooLong<&'a mut str>;
1527
1528    #[inline]
1529    fn try_from(slice: &'a mut str) -> Result<Self, Self::Error> {
1530        ensure_len_fits!(slice);
1531        // SAFETY: ^-- satisfies `len <= u32::MAX`.
1532        Ok(unsafe { Self::from_str_unchecked(slice) })
1533    }
1534}
1535
1536/// Converts `&mut str` into the slim limited version.
1537///
1538/// Panics when `str.len() > u32::MAX`.
1539#[inline]
1540pub fn from_str_mut(s: &mut str) -> SlimStrMut<'_> {
1541    expect_fit(s)
1542}
1543
1544#[cfg(test)]
1545mod tests {
1546    use std::hash::BuildHasher;
1547
1548    use super::*;
1549    use crate::map::DefaultHashBuilder;
1550    use crate::nstr;
1551
1552    fn hash_of<T: Hash>(x: T) -> u64 {
1553        DefaultHashBuilder::default().hash_one(&x)
1554    }
1555
1556    fn hash_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1557    where
1558        T: Hash + Debug + Deref,
1559        <T as Deref>::Target: Hash,
1560    {
1561        assert_eq!(hash_of(a), hash_of(a_deref));
1562        assert_eq!(hash_of(b), hash_of(b_deref));
1563        assert_ne!(hash_of(a), hash_of(b));
1564    }
1565
1566    fn ord_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1567    where
1568        T: Ord + Debug + Deref + PartialOrd<<T as Deref>::Target>,
1569    {
1570        assert_eq!(a.partial_cmp(b), Some(Ordering::Less));
1571        assert_eq!(b.partial_cmp(a), Some(Ordering::Greater));
1572        assert_eq!(a.partial_cmp(a), Some(Ordering::Equal));
1573        assert_eq!(b.partial_cmp(b), Some(Ordering::Equal));
1574        assert_eq!(a.partial_cmp(b_deref), Some(Ordering::Less));
1575        assert_eq!(b.partial_cmp(a_deref), Some(Ordering::Greater));
1576        assert_eq!(a.partial_cmp(a_deref), Some(Ordering::Equal));
1577        assert_eq!(b.partial_cmp(b_deref), Some(Ordering::Equal));
1578
1579        assert_eq!(a.cmp(b), Ordering::Less);
1580        assert_eq!(b.cmp(a), Ordering::Greater);
1581        assert_eq!(a.cmp(a), Ordering::Equal);
1582        assert_eq!(b.cmp(b), Ordering::Equal);
1583    }
1584
1585    #[allow(clippy::eq_op)]
1586    fn eq_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)
1587    where
1588        T: Eq + Debug + Deref + PartialEq<<T as Deref>::Target>,
1589    {
1590        assert!(a != b);
1591        assert!(b != a);
1592        assert_eq!(a, a);
1593        assert!(a != b_deref);
1594        assert!(a == a_deref);
1595        assert!(b != a_deref);
1596        assert!(b == b_deref);
1597    }
1598
1599    fn debug_properties<T: Debug, U: ?Sized + Debug>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {
1600        assert_eq!(format!("{a:?}"), format!("{:?}", a_cmp));
1601        assert_eq!(format!("{b:?}"), format!("{:?}", b_cmp));
1602    }
1603
1604    fn display_properties<T: Debug + Display, U: ?Sized + Display>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {
1605        assert_eq!(a.to_string(), a_cmp.to_string());
1606        assert_eq!(b.to_string(), b_cmp.to_string());
1607    }
1608
1609    fn general_properties<T, U>(a: &T, b: &T, a_deref: &U, b_deref: &U)
1610    where
1611        T: Deref<Target = U> + Debug + Eq + PartialEq<U> + PartialOrd<U> + Ord + Hash,
1612        U: ?Sized + Debug + Eq + Ord + Hash,
1613    {
1614        eq_properties(a, b, a_deref, b_deref);
1615        ord_properties(a, b, a_deref, b_deref);
1616        hash_properties(a, b, a_deref, b_deref);
1617        debug_properties(a, b, a_deref, b_deref);
1618    }
1619
1620    const TEST_STR: &str = "foo";
1621    const TEST_STR2: &str = "fop";
1622    const TEST_SLICE: &[u8] = TEST_STR.as_bytes();
1623    const TEST_SLICE2: &[u8] = TEST_STR2.as_bytes();
1624
1625    fn test_strings() -> [String; 2] {
1626        [TEST_STR.to_string(), TEST_STR2.to_string()]
1627    }
1628
1629    fn test_slices() -> [Vec<u8>; 2] {
1630        [TEST_SLICE.to_owned(), TEST_SLICE2.to_owned()]
1631    }
1632
1633    fn various_boxed_slices() -> [[SlimSliceBox<u8>; 2]; 5] {
1634        [
1635            test_slices().map(SlimSliceBox::from_vec),
1636            test_slices().map(Box::from).map(SlimSliceBox::from_boxed),
1637            test_slices().map(|s| SlimSliceBox::try_from(s).unwrap()),
1638            test_slices().map(|s| SlimSliceBox::try_from(s.into_boxed_slice()).unwrap()),
1639            test_slices().map(|s| SlimSliceBox::from(<[u8; 3]>::try_from(s).unwrap())),
1640        ]
1641    }
1642
1643    fn various_boxed_strs() -> [[SlimStrBox; 2]; 7] {
1644        [
1645            [nstr!("foo"), nstr!("fop")],
1646            test_strings().map(|s| from_string(&s)),
1647            test_strings().map(SlimStrBox::from_string),
1648            test_strings().map(Box::from).map(SlimStrBox::from_boxed),
1649            test_strings().map(|s| SlimStrBox::try_from(s).unwrap()),
1650            test_strings().map(|s| SlimStrBox::try_from(s.into_boxed_str()).unwrap()),
1651            test_strings().map(|s| SlimStrBox::try_from(s.deref()).unwrap()),
1652        ]
1653    }
1654
1655    fn assert_str_mut_properties(s1: &mut SlimStrMut<'_>, s2: &mut SlimStrMut<'_>) {
1656        let a: &SlimStrMut<'_> = s1;
1657        let b: &SlimStrMut<'_> = s2;
1658
1659        assert_eq!(a.deref(), TEST_STR);
1660        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);
1661        assert_eq!(b.deref(), TEST_STR2);
1662
1663        assert_eq!(String::from(a), TEST_STR);
1664        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);
1665        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);
1666        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);
1667
1668        general_properties(a, b, TEST_STR, TEST_STR2);
1669        display_properties(a, b, TEST_STR, TEST_STR2);
1670
1671        s1.deref_mut().make_ascii_uppercase();
1672        assert_eq!(&**s1, TEST_STR.to_uppercase());
1673    }
1674
1675    #[test]
1676    fn str_mut_call() {
1677        let [mut s1, mut s2] = test_strings();
1678        let s1 = &mut from_str_mut(s1.as_mut_str());
1679        let s2 = &mut from_str_mut(s2.as_mut_str());
1680        assert_str_mut_properties(s1, s2);
1681    }
1682
1683    #[test]
1684    fn str_mut_try_into() {
1685        let [mut s1, mut s2] = test_strings();
1686        let s1: &mut SlimStrMut = &mut s1.as_mut().try_into().unwrap();
1687        let s2: &mut SlimStrMut = &mut s2.as_mut().try_into().unwrap();
1688        assert_str_mut_properties(s1, s2);
1689    }
1690
1691    #[test]
1692    fn str_mut_exclusive_ref_various() {
1693        for [mut a, mut b] in various_boxed_strs() {
1694            assert_str_mut_properties(a.exclusive_ref(), b.exclusive_ref())
1695        }
1696    }
1697
1698    fn assert_str_properties(a: &SlimStr<'_>, b: &SlimStr<'_>) {
1699        assert_eq!(a.deref(), TEST_STR);
1700        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);
1701        assert_eq!(b.deref(), TEST_STR2);
1702
1703        assert_eq!(String::from(a), TEST_STR);
1704        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);
1705        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);
1706        assert_eq!(String::from(SlimStrBox::from(a)).deref(), TEST_STR);
1707        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);
1708
1709        general_properties(a, b, TEST_STR, TEST_STR2);
1710        display_properties(a, b, TEST_STR, TEST_STR2);
1711    }
1712
1713    #[test]
1714    fn str_call() {
1715        let [s1, s2] = test_strings();
1716        assert_str_properties(&from_str(&s1), &from_str(&s2));
1717    }
1718
1719    #[test]
1720    fn str_try_into() {
1721        let [s1, s2] = test_strings();
1722        let s1: &SlimStr = &mut s1.deref().try_into().unwrap();
1723        let s2: &SlimStr = &mut s2.deref().try_into().unwrap();
1724        assert_str_properties(s1, s2);
1725    }
1726
1727    #[test]
1728    fn str_shared_ref_various() {
1729        for [a, b] in various_boxed_strs() {
1730            assert_str_properties(a.shared_ref(), b.shared_ref())
1731        }
1732    }
1733
1734    fn assert_slice_mut_properties(s1: &mut SlimSliceMut<'_, u8>, s2: &mut SlimSliceMut<'_, u8>) {
1735        let a: &SlimSliceMut<'_, u8> = s1;
1736        let b: &SlimSliceMut<'_, u8> = s2;
1737
1738        assert_eq!(a.deref(), TEST_SLICE);
1739        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);
1740        assert_eq!(b.deref(), TEST_SLICE2);
1741
1742        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);
1743        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);
1744        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);
1745        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);
1746
1747        general_properties(a, b, TEST_SLICE, TEST_SLICE2);
1748
1749        s1.deref_mut().make_ascii_uppercase();
1750        let mut upper = TEST_SLICE.to_owned();
1751        upper.iter_mut().for_each(|x| x.make_ascii_uppercase());
1752        assert_eq!(&**s1, upper);
1753    }
1754
1755    #[test]
1756    fn slice_mut_call() {
1757        let [mut s1, mut s2] = test_slices();
1758        let s1 = &mut from_slice_mut(s1.as_mut());
1759        let s2 = &mut from_slice_mut(s2.as_mut());
1760        assert_slice_mut_properties(s1, s2);
1761    }
1762
1763    #[test]
1764    fn slice_mut_try_into() {
1765        let [mut s1, mut s2] = test_slices();
1766        let s1: &mut SlimSliceMut<u8> = &mut s1.deref_mut().try_into().unwrap();
1767        let s2: &mut SlimSliceMut<u8> = &mut s2.deref_mut().try_into().unwrap();
1768        assert_slice_mut_properties(s1, s2);
1769    }
1770
1771    #[test]
1772    fn slice_mut_exclusive_ref_various() {
1773        for [mut a, mut b] in various_boxed_slices() {
1774            assert_slice_mut_properties(a.exclusive_ref(), b.exclusive_ref());
1775        }
1776    }
1777
1778    fn assert_slice_properties(a: &SlimSlice<'_, u8>, b: &SlimSlice<'_, u8>) {
1779        assert_eq!(a.deref(), TEST_SLICE);
1780        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);
1781        assert_eq!(b.deref(), TEST_SLICE2);
1782
1783        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);
1784        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);
1785        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);
1786        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);
1787
1788        general_properties(a, b, TEST_SLICE, TEST_SLICE2);
1789    }
1790
1791    #[test]
1792    fn slice_call() {
1793        let [s1, s2] = test_slices();
1794        assert_slice_properties(&from_slice(&s1), &from_slice(&s2));
1795    }
1796
1797    #[test]
1798    fn slice_try_into() {
1799        let [s1, s2] = test_slices();
1800        let s1: &SlimSlice<u8> = &s1.deref().try_into().unwrap();
1801        let s2: &SlimSlice<u8> = &s2.deref().try_into().unwrap();
1802        assert_slice_properties(s1, s2);
1803    }
1804
1805    #[test]
1806    fn slice_shared_ref_various() {
1807        for [a, b] in various_boxed_slices() {
1808            assert_slice_properties(a.shared_ref(), b.shared_ref())
1809        }
1810    }
1811}