stringlet/
lib.rs

1#![doc = include_str!("../README.md")]
2
3// todo more efficient starts_with, contains, combined Length&Utf8Error instead of panic
4mod fmt;
5mod impl_for;
6mod macros;
7mod methods;
8mod new;
9mod refs;
10
11/// Configure `Stringlet` to have only valid `SIZE` and `ALIGN`.
12#[diagnostic::on_unimplemented(
13    message = "`Stringlet<{SIZE}>` has excessive SIZE",
14    label = "SIZE must be `0..=64`",
15    note = "`Stringlet` cannot be longer than 64 bytes. Consider using `String`!",
16    note = "Also ALIGN is {ALIGN}. This must be one of 1, 2, 4, 8, 16, 32, or 64!"
17)]
18pub trait Config<const SIZE: usize, const ALIGN: u8 = 1> {
19    type Aligned: Copy + Eq;
20}
21
22macro_rules! config {
23    ($align:literal, $aligned:ident @ $($size:literal),+) => {
24        $(impl<const FIXED: bool> Config<$size, $align> for Stringlet<$size, FIXED, $align> { type Aligned = $aligned; })+
25    };
26    ($($aligned:ident, $fixed_stringlet:ident $(, $stringlet:ident)? => $align:literal;)+) => {
27        $(
28            #[doc(hidden)]
29            #[repr(align($align))]
30            #[derive(Copy, Clone, PartialEq, Eq)]
31            pub struct $aligned(());
32            config! {
33                $align, $aligned @
34                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
35                17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
36                33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
37                49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
38            }
39        )+
40        $(
41            $(
42                #[doc = concat!("Variable length Stringlet, aligned to ", $align, " bytes")]
43                pub type $stringlet<const SIZE: usize = 16> = Stringlet<SIZE, false, $align>;
44            )?
45            #[doc = concat!("Fixed length Stringlet, aligned to ", $align, " bytes")]
46            pub type $fixed_stringlet<const SIZE: usize = 16> = Stringlet<SIZE, true, $align>;
47        )+
48    };
49}
50config! {
51    Align1, FixedStringlet => 1;
52    Align2, FixedStringlet2, Stringlet2 => 2;
53    Align4, FixedStringlet4, Stringlet4 => 4;
54    Align8, FixedStringlet8, Stringlet8 => 8;
55    Align16, FixedStringlet16, Stringlet16 => 16;
56    Align32, FixedStringlet32, Stringlet32 => 32;
57    Align64, FixedStringlet64, Stringlet64 => 64;
58}
59
60/// An inline String 0 to 64 bytes, which can be handled like a primitive type.
61#[derive(Copy, Clone, Eq)]
62pub union Stringlet<const SIZE: usize = 16, const FIXED: bool = false, const ALIGN: u8 = 1>
63where
64    Self: Config<SIZE, ALIGN>,
65{
66    /// Zero size type that is aligned according to `ALIGN` and never touched.
67    _align: <Self as Config<SIZE, ALIGN>>::Aligned,
68    /// The actual payload, if it is shorter than SIZE, its last bytes will be tagged.
69    pub(crate) str: [u8; SIZE],
70}
71
72/// Safely get out what is in the inner circle of the Stringlet wrapper.
73macro_rules! o {
74    ($str:ident) => {
75        // SAFETY: We never touch the other union member and all bytes are guaranteed to to be initialized.
76        unsafe { $str.str }
77    };
78}
79pub(crate) use o;