Skip to main content

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