1#![allow(unsafe_code)]
2
3use std::mem::ManuallyDrop;
4use std::sync::Arc;
5
6pub struct SelfRef<T: 'static> {
16 value: ManuallyDrop<T>,
18
19 backing: ManuallyDrop<Backing>,
21}
22
23pub trait SharedBacking: Send + Sync + 'static {
25 fn as_bytes(&self) -> &[u8];
27}
28
29pub enum Backing {
31 Boxed(Box<[u8]>),
34 Shared(Arc<dyn SharedBacking>),
36}
37
38impl Backing {
39 pub fn shared(shared: Arc<dyn SharedBacking>) -> Self {
41 Self::Shared(shared)
42 }
43
44 pub fn as_bytes(&self) -> &[u8] {
46 match self {
47 Backing::Boxed(b) => b,
48 Backing::Shared(s) => s.as_bytes(),
49 }
50 }
51}
52
53impl<T: 'static> Drop for SelfRef<T> {
54 fn drop(&mut self) {
55 unsafe {
57 ManuallyDrop::drop(&mut self.value);
58 ManuallyDrop::drop(&mut self.backing);
59 }
60 }
61}
62
63impl<T: 'static> SelfRef<T> {
64 pub fn try_new<E>(
70 backing: Backing,
71 builder: impl FnOnce(&'static [u8]) -> Result<T, E>,
72 ) -> Result<Self, E> {
73 let bytes: &'static [u8] = unsafe {
78 let b = backing.as_bytes();
79 std::slice::from_raw_parts(b.as_ptr(), b.len())
80 };
81
82 let value = builder(bytes)?;
83
84 Ok(Self {
85 value: ManuallyDrop::new(value),
86 backing: ManuallyDrop::new(backing),
87 })
88 }
89
90 pub fn new(backing: Backing, builder: impl FnOnce(&'static [u8]) -> T) -> Self {
92 Self::try_new(backing, |bytes| {
93 Ok::<_, std::convert::Infallible>(builder(bytes))
94 })
95 .unwrap_or_else(|e: std::convert::Infallible| match e {})
96 }
97 pub fn owning(backing: Backing, value: T) -> Self {
103 Self {
104 value: ManuallyDrop::new(value),
105 backing: ManuallyDrop::new(backing),
106 }
107 }
108
109 pub fn try_repack<U: 'static, E>(
120 mut self,
121 f: impl FnOnce(T, &'static [u8]) -> Result<U, E>,
122 ) -> Result<SelfRef<U>, E> {
123 let value = unsafe { ManuallyDrop::take(&mut self.value) };
124 let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
125 core::mem::forget(self);
126
127 let bytes: &'static [u8] = unsafe {
128 let b = backing.as_bytes();
129 std::slice::from_raw_parts(b.as_ptr(), b.len())
130 };
131
132 match f(value, bytes) {
133 Ok(u) => Ok(SelfRef {
134 value: ManuallyDrop::new(u),
135 backing: ManuallyDrop::new(backing),
136 }),
137 Err(e) => Err(e),
138 }
139 }
140
141 pub fn try_map<U: 'static, E>(
142 mut self,
143 f: impl FnOnce(T) -> Result<U, E>,
144 ) -> Result<SelfRef<U>, E> {
145 let value = unsafe { ManuallyDrop::take(&mut self.value) };
146 let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
147 core::mem::forget(self);
148
149 match f(value) {
150 Ok(u) => Ok(SelfRef {
151 value: ManuallyDrop::new(u),
152 backing: ManuallyDrop::new(backing),
153 }),
154 Err(e) => Err(e),
155 }
156 }
157
158 pub fn map<U: 'static>(mut self, f: impl FnOnce(T) -> U) -> SelfRef<U> {
159 let value = unsafe { ManuallyDrop::take(&mut self.value) };
162 let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
163 core::mem::forget(self);
164
165 SelfRef {
166 value: ManuallyDrop::new(f(value)),
167 backing: ManuallyDrop::new(backing),
168 }
169 }
170}
171
172pub unsafe trait Reborrow: 'static {
186 type Ref<'a>;
188}
189
190impl<T: Reborrow> SelfRef<T> {
191 pub fn get(&self) -> &T::Ref<'_> {
196 unsafe { core::mem::transmute::<&T, &T::Ref<'_>>(&self.value) }
200 }
201}
202
203unsafe impl Reborrow for u32 {
209 type Ref<'a> = u32;
210}
211unsafe impl Reborrow for usize {
212 type Ref<'a> = usize;
213}
214
215unsafe impl Reborrow for i8 {
216 type Ref<'a> = i8;
217}
218
219unsafe impl Reborrow for i16 {
220 type Ref<'a> = i16;
221}
222
223unsafe impl Reborrow for i32 {
224 type Ref<'a> = i32;
225}
226
227unsafe impl Reborrow for i64 {
228 type Ref<'a> = i64;
229}
230
231unsafe impl Reborrow for i128 {
232 type Ref<'a> = i128;
233}
234
235unsafe impl Reborrow for u8 {
236 type Ref<'a> = u8;
237}
238
239unsafe impl Reborrow for u16 {
240 type Ref<'a> = u16;
241}
242
243unsafe impl Reborrow for u64 {
244 type Ref<'a> = u64;
245}
246
247unsafe impl Reborrow for u128 {
248 type Ref<'a> = u128;
249}
250
251unsafe impl Reborrow for f32 {
252 type Ref<'a> = f32;
253}
254
255unsafe impl Reborrow for f64 {
256 type Ref<'a> = f64;
257}
258
259unsafe impl Reborrow for bool {
260 type Ref<'a> = bool;
261}
262
263unsafe impl Reborrow for char {
264 type Ref<'a> = char;
265}
266
267unsafe impl Reborrow for String {
268 type Ref<'a> = String;
269}
270
271unsafe impl Reborrow for &'static str {
272 type Ref<'a> = &'a str;
273}
274
275#[macro_export]
283macro_rules! impl_reborrow {
284 ($($ty:ident),* $(,)?) => {
285 $(
286 unsafe impl $crate::Reborrow for $ty<'static> {
288 type Ref<'a> = $ty<'a>;
289 }
290 )*
291 };
292}
293
294#[macro_export]
312macro_rules! selfref_match {
313 (
314 $selfref:expr, $field:ident {
315 $( $first:ident $(:: $rest:ident)* ($binding:tt) => $body:block )*
316 }
317 ) => {{
318 let __sref = $selfref;
319 $(
320 if ::core::matches!(&__sref.get().$field, $first$(::$rest)*(_)) {
321 #[allow(unused_variables)]
322 let $binding = __sref.map(|__v| match __v.$field {
323 $first$(::$rest)*(__inner) => __inner,
324 _ => unreachable!(),
325 });
326 $body
327 } else
328 )*
329 {
330 let _ = __sref;
332 }
333 }};
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use std::sync::atomic::{AtomicBool, Ordering};
340
341 unsafe impl Reborrow for &'static [u8] {
343 type Ref<'a> = &'a [u8];
344 }
345 unsafe impl Reborrow for (u32, u8, u8) {
346 type Ref<'a> = (u32, u8, u8);
347 }
348
349 struct TestSharedBacking {
350 bytes: Vec<u8>,
351 dropped: Arc<AtomicBool>,
352 }
353
354 impl SharedBacking for TestSharedBacking {
355 fn as_bytes(&self) -> &[u8] {
356 &self.bytes
357 }
358 }
359
360 impl Drop for TestSharedBacking {
361 fn drop(&mut self) {
362 self.dropped.store(true, Ordering::Release);
363 }
364 }
365
366 struct DropOrderValue {
367 backing_dropped: Arc<AtomicBool>,
368 value_dropped_before_backing: Arc<AtomicBool>,
369 }
370
371 impl Drop for DropOrderValue {
372 fn drop(&mut self) {
373 let backing_is_dropped = self.backing_dropped.load(Ordering::Acquire);
374 self.value_dropped_before_backing
375 .store(!backing_is_dropped, Ordering::Release);
376 }
377 }
378
379 #[test]
380 fn try_new_builds_borrowing_value_from_backing() {
381 let backing = Backing::Boxed(Box::from([1_u8, 2, 3, 4]));
382 let sref = SelfRef::try_new(backing, |bytes| Ok::<_, ()>(&bytes[1..3]))
383 .expect("try_new should succeed");
384 assert_eq!(sref.get(), &[2_u8, 3]);
385 }
386
387 #[test]
388 fn try_new_propagates_builder_error() {
389 let backing = Backing::Boxed(Box::from([9_u8, 8, 7]));
390 let err = match SelfRef::<u32>::try_new(backing, |_| Err::<u32, _>("boom")) {
391 Ok(_) => panic!("try_new should return builder error"),
392 Err(err) => err,
393 };
394 assert_eq!(err, "boom");
395 }
396
397 #[test]
398 fn try_map_and_try_repack_preserve_backing_and_transform_value() {
399 let backing = Backing::Boxed(Box::from(*b"hello"));
400 let sref = SelfRef::new(backing, |bytes| bytes);
401 let len_ref = sref
402 .try_map(|bytes| Ok::<_, ()>(bytes.len()))
403 .expect("try_map should succeed");
404 assert_eq!(*len_ref.get(), 5);
405
406 let backing = Backing::Boxed(Box::from(*b"abcdef"));
407 let sref = SelfRef::new(backing, |_| 10_u32);
408 let repacked = sref
409 .try_repack(|value, bytes| Ok::<_, ()>((value + 1, bytes[0], bytes[5])))
410 .expect("try_repack should succeed");
411 assert_eq!(*repacked.get(), (11_u32, b'a', b'f'));
412 }
413
414 #[test]
415 fn try_map_and_try_repack_propagate_errors() {
416 let backing = Backing::Boxed(Box::from([1_u8, 2, 3]));
417 let sref = SelfRef::new(backing, |_| 7_u8);
418 let err = match sref.try_map::<u8, _>(|_| Err::<u8, _>("nope")) {
419 Ok(_) => panic!("try_map error should propagate"),
420 Err(err) => err,
421 };
422 assert_eq!(err, "nope");
423
424 let backing = Backing::Boxed(Box::from([4_u8, 5, 6]));
425 let sref = SelfRef::new(backing, |_| 9_u8);
426 let err = match sref.try_repack::<u8, _>(|_, _| Err::<u8, _>("bad")) {
427 Ok(_) => panic!("try_repack error should propagate"),
428 Err(err) => err,
429 };
430 assert_eq!(err, "bad");
431 }
432
433 #[test]
434 fn drop_order_drops_value_before_backing() {
435 let backing_dropped = Arc::new(AtomicBool::new(false));
436 let value_dropped_before_backing = Arc::new(AtomicBool::new(false));
437
438 let shared = Arc::new(TestSharedBacking {
439 bytes: vec![1, 2, 3],
440 dropped: Arc::clone(&backing_dropped),
441 });
442
443 let value = DropOrderValue {
444 backing_dropped: Arc::clone(&backing_dropped),
445 value_dropped_before_backing: Arc::clone(&value_dropped_before_backing),
446 };
447
448 let sref = SelfRef::owning(Backing::shared(shared), value);
449 drop(sref);
450
451 assert!(
452 value_dropped_before_backing.load(Ordering::Acquire),
453 "value should drop before backing"
454 );
455 assert!(
456 backing_dropped.load(Ordering::Acquire),
457 "backing should eventually be dropped"
458 );
459 }
460}