Skip to main content

lite_strtab/types/
offset.rs

1//! Backing integer types for byte offsets in [`crate::StringTable`].
2//!
3//! Offsets index into the contiguous UTF-8 byte buffer and include a final
4//! sentinel equal to the total byte length. The chosen [`Offset`] type
5//! controls offset-table memory use and maximum total byte size.
6//!
7//! Default [`u32`] supports up to 4 GiB of string bytes.
8
9/// Contract for integer types used as byte offsets.
10///
11/// Unlike [`crate::StringIndex`] (which bounds string count), [`Offset`]
12/// bounds total UTF-8 byte size. Builders and validators use
13/// [`Self::try_from_usize`] for checked growth, while lookup code uses
14/// [`Self::to_usize`] for infallible slicing.
15///
16/// # Implementing this trait
17///
18/// This trait is already implemented for primitive unsigned integers (`u8`, `u16`, `u32`,
19/// `u64`, `usize`). To implement it for custom wrapper types, use the
20/// [`impl_offset`](crate::impl_offset) macro:
21///
22/// ```
23/// use lite_strtab::impl_offset;
24///
25/// #[derive(Clone, Copy)]
26/// #[repr(transparent)]
27/// struct ByteOffset(u32);
28///
29/// impl_offset!(ByteOffset: u32);
30/// ```
31pub trait Offset: Copy + Send + Sync + 'static {
32    /// Human-readable type name.
33    const TYPE_NAME: &'static str;
34
35    /// Converts a byte length/offset into this type.
36    fn try_from_usize(value: usize) -> Option<Self>;
37
38    /// Converts this offset to [`usize`].
39    fn to_usize(self) -> usize;
40}
41
42/// Implements [`Offset`] for one or more types.
43///
44/// # Examples
45///
46/// For wrapper types with an inner integer type:
47///
48/// ```
49/// use lite_strtab::impl_offset;
50///
51/// #[derive(Clone, Copy)]
52/// #[repr(transparent)]
53/// struct ByteOffset(u32);
54///
55/// impl_offset!(ByteOffset: u32);
56/// ```
57#[macro_export]
58macro_rules! impl_offset {
59    // Pattern for wrapper types: Type: InnerType
60    ($wrapper:ty: $inner:ty) => {
61        impl $crate::Offset for $wrapper {
62            const TYPE_NAME: &'static str = stringify!($wrapper);
63
64            #[inline]
65            fn try_from_usize(value: usize) -> Option<Self> {
66                <$inner as $crate::Offset>::try_from_usize(value).map(Self)
67            }
68
69            #[inline]
70            fn to_usize(self) -> usize {
71                <$inner as $crate::Offset>::to_usize(self.0)
72            }
73        }
74    };
75
76    // Pattern for multiple wrapper types
77    ($wrapper:ty: $inner:ty, $($rest:tt)*) => {
78        $crate::impl_offset!($wrapper: $inner);
79        $crate::impl_offset!($($rest)*);
80    };
81
82    // Pattern for primitive types
83    ($($ty:ty),+ $(,)?) => {
84        $(
85            impl $crate::Offset for $ty {
86                const TYPE_NAME: &'static str = stringify!($ty);
87
88                #[inline]
89                fn try_from_usize(value: usize) -> Option<Self> {
90                    <Self as core::convert::TryFrom<usize>>::try_from(value).ok()
91                }
92
93                #[inline]
94                fn to_usize(self) -> usize {
95                    self as usize
96                }
97            }
98        )+
99    };
100}
101
102#[cfg(target_pointer_width = "64")]
103crate::impl_offset!(u8, u16, u32, u64, usize);
104
105#[cfg(target_pointer_width = "32")]
106crate::impl_offset!(u8, u16, u32, usize);
107
108#[cfg(target_pointer_width = "16")]
109crate::impl_offset!(u8, u16, usize);
110
111// Supported pointer widths for this crate.
112#[cfg(not(any(
113    target_pointer_width = "16",
114    target_pointer_width = "32",
115    target_pointer_width = "64"
116)))]
117compile_error!("lite-strtab requires a 16-bit, 32-bit, or 64-bit target");