compact_str/repr/
static_str.rs

1use core::{
2    mem,
3    ptr,
4    slice,
5    str,
6};
7
8use super::{
9    Repr,
10    MAX_SIZE,
11    STATIC_STR_MASK,
12};
13
14pub(super) const DISCRIMINANT_SIZE: usize = MAX_SIZE - mem::size_of::<&'static str>();
15
16/// A buffer stored on the stack whose size is equal to the stack size of `String`
17/// The last byte is set to 0.
18#[derive(Copy, Clone)]
19#[repr(C)]
20pub struct StaticStr {
21    ptr: ptr::NonNull<u8>,
22    len: usize,
23    #[allow(unused)]
24    discriminant: [u8; DISCRIMINANT_SIZE],
25}
26static_assertions::assert_eq_size!(StaticStr, Repr);
27static_assertions::assert_eq_align!(StaticStr, Repr);
28static_assertions::assert_eq_size!(&'static str, (*const u8, usize));
29
30impl StaticStr {
31    #[inline]
32    pub const fn new(text: &'static str) -> Self {
33        let mut discriminant = [0; DISCRIMINANT_SIZE];
34        discriminant[DISCRIMINANT_SIZE - 1] = STATIC_STR_MASK;
35
36        Self {
37            // SAFETY: `&'static str` must have a non-null, properly aligned
38            // address
39            ptr: unsafe { ptr::NonNull::new_unchecked(text.as_ptr() as *mut _) },
40            len: text.len(),
41            discriminant,
42        }
43    }
44
45    #[rustversion::attr(since(1.64), const)]
46    pub(super) fn get_text(&self) -> &'static str {
47        // SAFETY: `StaticStr` invariants requires it to be a valid str
48        unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.ptr.as_ptr(), self.len)) }
49    }
50
51    /// # Safety
52    /// * `len` bytes in the buffer must be valid UTF-8 and
53    /// * `len` must be <= `self.get_text().len()`
54    pub(super) unsafe fn set_len(&mut self, len: usize) {
55        *self = Self::new(&self.get_text()[..len]);
56    }
57}