nexus_smartptr/lib.rs
1//! Inline smart pointers for `?Sized` types.
2//!
3//! `nexus-smartptr` provides [`Flat`] and [`Flex`] — smart pointers that
4//! store trait objects (or other `?Sized` types) inline, avoiding heap
5//! allocation for small values.
6//!
7//! - [`Flat<T, B>`] — inline only. Panics if the concrete type doesn't fit.
8//! - [`Flex<T, B>`] — inline with heap fallback. Never panics.
9//!
10//! `B` is a buffer marker type (`B32`, `B64`, etc.) whose name is the
11//! total size in bytes. `size_of::<Flat<dyn Trait, B32>>() == 32`.
12//!
13//! # Construction
14//!
15//! Use the [`flat!`] and [`flex!`] macros for `?Sized` types:
16//!
17//! ```
18//! use nexus_smartptr::{flat, flex, Flat, Flex, B32};
19//!
20//! trait Greet {
21//! fn greet(&self) -> &str;
22//! }
23//!
24//! struct Hello;
25//! impl Greet for Hello {
26//! fn greet(&self) -> &str { "hello" }
27//! }
28//!
29//! // Inline only — panics if Hello doesn't fit
30//! let f: Flat<dyn Greet, B32> = flat!(Hello);
31//! assert_eq!(f.greet(), "hello");
32//!
33//! // Inline with heap fallback
34//! let f: Flex<dyn Greet, B32> = flex!(Hello);
35//! assert!(f.is_inline());
36//! assert_eq!(f.greet(), "hello");
37//! ```
38//!
39//! For `Sized` types, use [`Flat::new`] or [`Flex::new`] directly:
40//!
41//! ```
42//! use nexus_smartptr::{Flat, Flex, B32};
43//!
44//! let f: Flat<u64, B32> = Flat::new(42);
45//! assert_eq!(*f, 42);
46//!
47//! let f: Flex<u64, B32> = Flex::new(42);
48//! assert_eq!(*f, 42);
49//! ```
50//!
51//! # Sizing Reference
52//!
53//! Capacities shown for 64-bit targets (pointer size = 8 bytes):
54//!
55//! | Marker | Total size | `?Sized` value capacity | `Sized` capacity |
56//! |--------|-----------|------------------------|-----------------|
57//! | `B16` | 16 bytes | 8 bytes | 16 bytes |
58//! | `B32` | 32 bytes | 24 bytes | 32 bytes |
59//! | `B64` | 64 bytes | 56 bytes | 64 bytes |
60//! | `B128` | 128 bytes | 120 bytes | 128 bytes |
61//! | `B256` | 256 bytes | 248 bytes | 256 bytes |
62//!
63//! For `Flex`, subtract an additional pointer-sized word (heap pointer / discriminant).
64
65#![warn(missing_docs)]
66
67mod flat;
68mod flex;
69mod meta;
70
71pub use flat::Flat;
72pub use flex::Flex;
73
74/// Inline buffer storage type.
75///
76/// Each implementor is a `repr(C, align(8))` byte array that [`Flat`] and
77/// [`Flex`] use directly as their backing store. `size_of::<Self>()` is the
78/// total capacity in bytes and must be a multiple of 8.
79///
80/// Use [`define_buffer!`] to create custom sizes.
81///
82/// # Safety
83///
84/// `CAPACITY` must equal `size_of::<Self>()`. Implementors must be
85/// `repr(C, align(8))` with size equal to `CAPACITY`.
86pub unsafe trait Buffer: Sized {
87 /// Total buffer capacity in bytes.
88 const CAPACITY: usize;
89}
90
91/// Defines a buffer marker type with a given byte capacity.
92///
93/// The size must be a multiple of 8 (the alignment requirement).
94///
95/// # Examples
96///
97/// ```
98/// nexus_smartptr::define_buffer!(B512, 512);
99/// ```
100///
101/// Non-multiples of 8 are rejected at compile time:
102///
103/// ```compile_fail
104/// nexus_smartptr::define_buffer!(B12, 12);
105/// ```
106#[macro_export]
107macro_rules! define_buffer {
108 ($name:ident, $bytes:literal) => {
109 /// Buffer marker:
110 #[doc = concat!(stringify!($bytes), " bytes, align(8).")]
111 #[repr(C, align(8))]
112 pub struct $name([u8; $bytes]);
113
114 // Validate that declared size matches actual struct size.
115 // Fails if $bytes is not a multiple of 8 (alignment padding changes size).
116 const _: () = assert!(
117 core::mem::size_of::<$name>() == $bytes,
118 "buffer size must be a multiple of 8 (alignment requirement)"
119 );
120
121 // SAFETY: CAPACITY == size_of::<Self>() == $bytes, verified above.
122 // repr(C, align(8)) guarantees layout.
123 unsafe impl $crate::Buffer for $name {
124 const CAPACITY: usize = $bytes;
125 }
126 };
127}
128
129define_buffer!(B16, 16);
130define_buffer!(B32, 32);
131define_buffer!(B48, 48);
132define_buffer!(B64, 64);
133define_buffer!(B128, 128);
134define_buffer!(B256, 256);
135define_buffer!(B512, 512);
136define_buffer!(B1024, 1024);
137define_buffer!(B2048, 2048);
138
139// -- Type aliases for convenience --
140
141/// `Flat` with 16-byte buffer.
142pub type Flat16<T> = Flat<T, B16>;
143/// `Flat` with 32-byte buffer.
144pub type Flat32<T> = Flat<T, B32>;
145/// `Flat` with 48-byte buffer.
146pub type Flat48<T> = Flat<T, B48>;
147/// `Flat` with 64-byte buffer.
148pub type Flat64<T> = Flat<T, B64>;
149/// `Flat` with 128-byte buffer.
150pub type Flat128<T> = Flat<T, B128>;
151/// `Flat` with 256-byte buffer.
152pub type Flat256<T> = Flat<T, B256>;
153
154/// `Flex` with 16-byte buffer.
155pub type Flex16<T> = Flex<T, B16>;
156/// `Flex` with 32-byte buffer.
157pub type Flex32<T> = Flex<T, B32>;
158/// `Flex` with 48-byte buffer.
159pub type Flex48<T> = Flex<T, B48>;
160/// `Flex` with 64-byte buffer.
161pub type Flex64<T> = Flex<T, B64>;
162/// `Flex` with 128-byte buffer.
163pub type Flex128<T> = Flex<T, B128>;
164/// `Flex` with 256-byte buffer.
165pub type Flex256<T> = Flex<T, B256>;
166
167/// Constructs a [`Flat`] from a concrete value via unsizing coercion.
168///
169/// The return type must be annotated so the compiler performs unsizing
170/// coercion (e.g., `Flat<dyn Trait, B32>`).
171///
172/// # Panics
173///
174/// Panics if the concrete value doesn't fit in the buffer (after
175/// reserving one pointer-sized word for metadata on `?Sized` targets).
176///
177/// # Examples
178///
179/// ```
180/// use nexus_smartptr::{flat, Flat, B32};
181/// use core::fmt::Display;
182///
183/// let f: Flat<dyn Display, B32> = flat!(42_u32);
184/// assert_eq!(format!("{}", &*f), "42");
185/// ```
186#[macro_export]
187macro_rules! flat {
188 ($val:expr) => {{
189 let val = $val;
190 let ptr: *const _ = &val;
191 // SAFETY: ptr is a (possibly fat) pointer produced by unsizing
192 // coercion at the call site. Its metadata corresponds to the
193 // concrete type of val.
194 unsafe { $crate::Flat::new_raw(val, ptr) }
195 }};
196}
197
198/// Constructs a [`Flex`] from a concrete value via unsizing coercion.
199///
200/// Stores inline if the value fits, otherwise heap-allocates.
201/// The return type must be annotated so the compiler performs unsizing
202/// coercion (e.g., `Flex<dyn Trait, B32>`).
203///
204/// # Examples
205///
206/// ```
207/// use nexus_smartptr::{flex, Flex, B32};
208/// use core::fmt::Display;
209///
210/// let f: Flex<dyn Display, B32> = flex!(42_u32);
211/// assert!(f.is_inline());
212/// assert_eq!(format!("{}", &*f), "42");
213/// ```
214#[macro_export]
215macro_rules! flex {
216 ($val:expr) => {{
217 let val = $val;
218 let ptr: *const _ = &val;
219 // SAFETY: same as flat! — ptr's metadata matches val's concrete type.
220 unsafe { $crate::Flex::new_raw(val, ptr) }
221 }};
222}