1use core::cell::UnsafeCell;
6use core::marker::PhantomData;
7use core::mem::{ManuallyDrop, MaybeUninit};
8use core::{fmt, ptr};
9
10pub const DEFAULT_INLINE_FN_SIZE: usize = 3 * core::mem::size_of::<usize>();
12
13const INLINE_STORAGE_ALIGN: usize = 8;
15
16#[repr(C, align(8))]
17struct AlignedStorage<const SIZE: usize> {
18 bytes: UnsafeCell<[MaybeUninit<u8>; SIZE]>,
19}
20
21const _: () = assert!(core::mem::align_of::<AlignedStorage<0>>() == INLINE_STORAGE_ALIGN);
22
23impl<const SIZE: usize> AlignedStorage<SIZE> {
24 #[inline]
25 const fn uninit() -> Self {
26 Self { bytes: UnsafeCell::new([MaybeUninit::uninit(); SIZE]) }
27 }
28
29 #[inline]
30 fn as_ptr(&self) -> *mut u8 {
31 self.bytes.get().cast::<u8>()
32 }
33}
34
35mod private {
36 pub trait SealedThreadSafety {}
37 pub trait SealedSig {}
38 pub trait SealedWithSig<Sig> {}
39}
40
41pub struct Local(PhantomData<*mut ()>);
46
47impl fmt::Debug for Local {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 f.write_str("Local")
50 }
51}
52
53pub struct SendOnly(PhantomData<UnsafeCell<()>>);
58
59impl fmt::Debug for SendOnly {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str("SendOnly")
62 }
63}
64
65pub struct SendSync(PhantomData<()>);
70
71impl fmt::Debug for SendSync {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.write_str("SendSync")
74 }
75}
76
77impl private::SealedThreadSafety for Local {}
78impl private::SealedThreadSafety for SendOnly {}
79impl private::SealedThreadSafety for SendSync {}
80
81pub unsafe trait ThreadSafety<F>: private::SealedThreadSafety {}
91
92unsafe impl<F> ThreadSafety<F> for Local {}
94unsafe impl<F: Send> ThreadSafety<F> for SendOnly {}
96unsafe impl<F: Send + Sync> ThreadSafety<F> for SendSync {}
98
99pub trait FnSig: private::SealedSig {
101 #[doc(hidden)]
102 type Trampoline: Copy;
103}
104
105#[doc(hidden)]
106#[derive(Clone, Copy, Debug)]
107pub struct VTableOnce<Trampoline> {
108 call_once: Trampoline,
109 drop: unsafe fn(*mut u8),
110}
111
112#[doc(hidden)]
113#[derive(Clone, Copy, Debug)]
114pub struct VTable<Trampoline> {
115 call: Trampoline,
116 once: VTableOnce<Trampoline>,
117}
118
119trait HasDrop: Copy {
120 fn drop_fn(&self) -> unsafe fn(*mut u8);
121}
122
123impl<Trampoline: Copy> HasDrop for VTableOnce<Trampoline> {
124 #[inline]
125 fn drop_fn(&self) -> unsafe fn(*mut u8) {
126 self.drop
127 }
128}
129
130impl<Trampoline: Copy> HasDrop for VTable<Trampoline> {
131 #[inline]
132 fn drop_fn(&self) -> unsafe fn(*mut u8) {
133 self.once.drop
134 }
135}
136
137pub unsafe trait FnOnceWithSig<Sig: FnSig>: Sized + private::SealedWithSig<Sig> {
148 #[doc(hidden)]
149 const VTABLE_ONCE: VTableOnce<Sig::Trampoline>;
150}
151
152pub unsafe trait FnMutWithSig<Sig: FnSig>: FnOnceWithSig<Sig> {
162 #[doc(hidden)]
163 const VTABLE_MUT: VTable<Sig::Trampoline>;
164}
165
166pub unsafe trait FnWithSig<Sig: FnSig>: FnMutWithSig<Sig> {
176 #[doc(hidden)]
177 const VTABLE: VTable<Sig::Trampoline>;
178}
179
180unsafe fn noop_drop(_ptr: *mut u8) {}
186
187unsafe fn drop_in_place_fn<F>(ptr: *mut u8) {
195 unsafe {
198 ptr::drop_in_place(ptr.cast::<F>());
199 }
200}
201
202const fn drop_fn_for<F>() -> unsafe fn(*mut u8) {
203 if core::mem::needs_drop::<F>() { drop_in_place_fn::<F> } else { noop_drop }
204}
205
206struct RawInlineFn<'a, const SIZE: usize, V: HasDrop, S> {
223 storage: AlignedStorage<SIZE>,
224 vtable: V,
225 _marker: PhantomData<(&'a (), S)>,
226}
227
228unsafe impl<'a, const SIZE: usize, V: HasDrop, S: Send> Send for RawInlineFn<'a, SIZE, V, S> {}
233
234unsafe impl<'a, const SIZE: usize, V: HasDrop, S: Sync> Sync for RawInlineFn<'a, SIZE, V, S> {}
239
240impl<'a, const SIZE: usize, V: HasDrop, S> Drop for RawInlineFn<'a, SIZE, V, S> {
241 #[inline]
242 fn drop(&mut self) {
243 unsafe {
248 (self.vtable.drop_fn())(self.as_ptr());
249 }
250 }
251}
252
253impl<'a, const SIZE: usize, V: HasDrop, S> RawInlineFn<'a, SIZE, V, S> {
254 #[inline]
262 unsafe fn new<F>(closure: F, vtable: V) -> Self
263 where
264 F: 'a,
265 S: ThreadSafety<F>,
266 {
267 const {
268 assert!(
269 core::mem::size_of::<F>() <= SIZE,
270 "Closure state exceeds inline storage capacity"
271 );
272 assert!(
273 core::mem::align_of::<F>() <= INLINE_STORAGE_ALIGN,
274 "Closure alignment requirement exceeds inline storage alignment"
275 );
276 }
277
278 let storage = AlignedStorage::<SIZE>::uninit();
279 unsafe {
284 ptr::write(storage.as_ptr().cast::<F>(), closure);
285 }
286
287 Self { storage, vtable, _marker: PhantomData }
288 }
289
290 #[inline]
291 fn as_ptr(&self) -> *mut u8 {
292 self.storage.as_ptr()
293 }
294
295 #[inline]
296 fn resize<const NEW_SIZE: usize>(self) -> RawInlineFn<'a, NEW_SIZE, V, S> {
297 const {
298 assert!(
299 NEW_SIZE >= SIZE,
300 "Target inline storage capacity must be greater than or equal to source capacity"
301 );
302 }
303
304 let this = ManuallyDrop::new(self);
305 let storage = AlignedStorage::<NEW_SIZE>::uninit();
306 unsafe {
311 ptr::copy_nonoverlapping(
312 this.as_ptr().cast::<MaybeUninit<u8>>(),
313 storage.as_ptr().cast::<MaybeUninit<u8>>(),
314 SIZE,
315 );
316 }
317
318 RawInlineFn { storage, vtable: this.vtable, _marker: PhantomData }
319 }
320
321 #[inline]
331 unsafe fn map_vtable_and_safety<NewV: HasDrop, NewS>(
332 self,
333 new_vtable: NewV,
334 ) -> RawInlineFn<'a, SIZE, NewV, NewS> {
335 let this = ManuallyDrop::new(self);
336 let storage = unsafe { ptr::read(&this.storage) };
340
341 RawInlineFn { storage, vtable: new_vtable, _marker: PhantomData }
342 }
343}
344
345pub struct InlineFn<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
360 raw: RawInlineFn<'a, SIZE, VTable<Sig::Trampoline>, S>,
364}
365
366impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFn<'a, Sig, SIZE, S> {
367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368 f.debug_struct("InlineFn")
369 .field("size", &SIZE)
370 .field("drop", &(self.raw.vtable.once.drop as usize))
371 .finish()
372 }
373}
374
375impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFn<'a, Sig, SIZE, S> {
376 #[inline]
379 pub fn new<F>(closure: F) -> Self
380 where
381 F: FnWithSig<Sig> + 'a,
382 S: ThreadSafety<F>,
383 {
384 Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE) } }
387 }
388
389 #[inline]
393 pub fn resize<const NEW_SIZE: usize>(self) -> InlineFn<'a, Sig, NEW_SIZE, S> {
394 InlineFn { raw: self.raw.resize::<NEW_SIZE>() }
395 }
396
397 #[inline]
399 pub fn into_local(self) -> InlineFn<'a, Sig, SIZE, Local> {
400 let vtable = self.raw.vtable;
401 InlineFn { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
404 }
405
406 #[inline]
409 pub fn into_fn_mut(self) -> InlineFnMut<'a, Sig, SIZE, S> {
410 let vtable = self.raw.vtable;
411 InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
415 }
416
417 #[inline]
420 pub fn into_fn_once(self) -> InlineFnOnce<'a, Sig, SIZE, S> {
421 let once_vtable = self.raw.vtable.once;
422 InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(once_vtable) } }
425 }
426}
427
428impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, Local> {
429 #[inline]
431 pub fn new_local<F>(closure: F) -> Self
432 where
433 F: FnWithSig<Sig> + 'a,
434 {
435 Self::new(closure)
436 }
437}
438
439impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, SendOnly> {
440 #[inline]
442 pub fn new_send<F>(closure: F) -> Self
443 where
444 F: FnWithSig<Sig> + Send + 'a,
445 {
446 Self::new(closure)
447 }
448}
449
450impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, SendSync> {
451 #[inline]
453 pub fn new_send_sync<F>(closure: F) -> Self
454 where
455 F: FnWithSig<Sig> + Send + Sync + 'a,
456 {
457 Self::new(closure)
458 }
459
460 #[inline]
462 pub fn into_send(self) -> InlineFn<'a, Sig, SIZE, SendOnly> {
463 let vtable = self.raw.vtable;
464 InlineFn { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
467 }
468}
469
470impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFn<'a, Sig, SIZE, S>>
471 for InlineFnMut<'a, Sig, SIZE, S>
472{
473 #[inline]
474 fn from(func: InlineFn<'a, Sig, SIZE, S>) -> Self {
475 func.into_fn_mut()
476 }
477}
478
479impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFn<'a, Sig, SIZE, S>>
480 for InlineFnOnce<'a, Sig, SIZE, S>
481{
482 #[inline]
483 fn from(func: InlineFn<'a, Sig, SIZE, S>) -> Self {
484 func.into_fn_once()
485 }
486}
487
488pub struct InlineFnMut<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
493 raw: RawInlineFn<'a, SIZE, VTable<Sig::Trampoline>, S>,
498}
499
500impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFnMut<'a, Sig, SIZE, S> {
501 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502 f.debug_struct("InlineFnMut")
503 .field("size", &SIZE)
504 .field("drop", &(self.raw.vtable.once.drop as usize))
505 .finish()
506 }
507}
508
509impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFnMut<'a, Sig, SIZE, S> {
510 #[inline]
513 pub fn new<F>(closure: F) -> Self
514 where
515 F: FnMutWithSig<Sig> + 'a,
516 S: ThreadSafety<F>,
517 {
518 Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE_MUT) } }
521 }
522
523 #[inline]
527 pub fn resize<const NEW_SIZE: usize>(self) -> InlineFnMut<'a, Sig, NEW_SIZE, S> {
528 InlineFnMut { raw: self.raw.resize::<NEW_SIZE>() }
529 }
530
531 #[inline]
533 pub fn into_local(self) -> InlineFnMut<'a, Sig, SIZE, Local> {
534 let vtable = self.raw.vtable;
535 InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
538 }
539
540 #[inline]
543 pub fn into_fn_once(self) -> InlineFnOnce<'a, Sig, SIZE, S> {
544 let once_vtable = self.raw.vtable.once;
545 InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(once_vtable) } }
548 }
549}
550
551impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, Local> {
552 #[inline]
554 pub fn new_local<F>(closure: F) -> Self
555 where
556 F: FnMutWithSig<Sig> + 'a,
557 {
558 Self::new(closure)
559 }
560}
561
562impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, SendOnly> {
563 #[inline]
565 pub fn new_send<F>(closure: F) -> Self
566 where
567 F: FnMutWithSig<Sig> + Send + 'a,
568 {
569 Self::new(closure)
570 }
571}
572
573impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, SendSync> {
574 #[inline]
576 pub fn new_send_sync<F>(closure: F) -> Self
577 where
578 F: FnMutWithSig<Sig> + Send + Sync + 'a,
579 {
580 Self::new(closure)
581 }
582
583 #[inline]
585 pub fn into_send(self) -> InlineFnMut<'a, Sig, SIZE, SendOnly> {
586 let vtable = self.raw.vtable;
587 InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
590 }
591}
592
593impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFnMut<'a, Sig, SIZE, S>>
594 for InlineFnOnce<'a, Sig, SIZE, S>
595{
596 #[inline]
597 fn from(func: InlineFnMut<'a, Sig, SIZE, S>) -> Self {
598 func.into_fn_once()
599 }
600}
601
602pub struct InlineFnOnce<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
607 raw: RawInlineFn<'a, SIZE, VTableOnce<Sig::Trampoline>, S>,
611}
612
613impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFnOnce<'a, Sig, SIZE, S> {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 f.debug_struct("InlineFnOnce")
616 .field("size", &SIZE)
617 .field("drop", &(self.raw.vtable.drop as usize))
618 .finish()
619 }
620}
621
622impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFnOnce<'a, Sig, SIZE, S> {
623 #[inline]
626 pub fn new<F>(closure: F) -> Self
627 where
628 F: FnOnceWithSig<Sig> + 'a,
629 S: ThreadSafety<F>,
630 {
631 Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE_ONCE) } }
634 }
635
636 #[inline]
640 pub fn resize<const NEW_SIZE: usize>(self) -> InlineFnOnce<'a, Sig, NEW_SIZE, S> {
641 InlineFnOnce { raw: self.raw.resize::<NEW_SIZE>() }
642 }
643
644 #[inline]
646 pub fn into_local(self) -> InlineFnOnce<'a, Sig, SIZE, Local> {
647 let vtable = self.raw.vtable;
648 InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
651 }
652}
653
654impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, Local> {
655 #[inline]
657 pub fn new_local<F>(closure: F) -> Self
658 where
659 F: FnOnceWithSig<Sig> + 'a,
660 {
661 Self::new(closure)
662 }
663}
664
665impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, SendOnly> {
666 #[inline]
668 pub fn new_send<F>(closure: F) -> Self
669 where
670 F: FnOnceWithSig<Sig> + Send + 'a,
671 {
672 Self::new(closure)
673 }
674}
675
676impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, SendSync> {
677 #[inline]
679 pub fn new_send_sync<F>(closure: F) -> Self
680 where
681 F: FnOnceWithSig<Sig> + Send + Sync + 'a,
682 {
683 Self::new(closure)
684 }
685
686 #[inline]
688 pub fn into_send(self) -> InlineFnOnce<'a, Sig, SIZE, SendOnly> {
689 let vtable = self.raw.vtable;
690 InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
693 }
694}
695
696pub type SendInlineFn<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
698 InlineFn<'a, Sig, SIZE, SendOnly>;
699
700pub type SendSyncInlineFn<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
702 InlineFn<'a, Sig, SIZE, SendSync>;
703
704pub type SendInlineFnMut<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
706 InlineFnMut<'a, Sig, SIZE, SendOnly>;
707
708pub type SendSyncInlineFnMut<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
710 InlineFnMut<'a, Sig, SIZE, SendSync>;
711
712pub type SendInlineFnOnce<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
714 InlineFnOnce<'a, Sig, SIZE, SendOnly>;
715
716pub type SendSyncInlineFnOnce<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
718 InlineFnOnce<'a, Sig, SIZE, SendSync>;
719
720macro_rules! impl_inline_fn_for_signature {
721 ($($arg:ident : $arg_ty:ident),*) => {
722 impl<$($arg_ty,)* Ret> private::SealedSig for fn($($arg_ty),*) -> Ret {}
723
724 impl<$($arg_ty,)* Ret> FnSig for fn($($arg_ty),*) -> Ret {
725 type Trampoline = unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret;
726 }
727
728 impl<$($arg_ty,)* Ret, F> private::SealedWithSig<fn($($arg_ty),*) -> Ret> for F
729 where
730 F: FnOnce($($arg_ty),*) -> Ret,
731 {
732 }
733
734 unsafe impl<$($arg_ty,)* Ret, F> FnOnceWithSig<fn($($arg_ty),*) -> Ret> for F
737 where
738 F: FnOnce($($arg_ty),*) -> Ret,
739 {
740 const VTABLE_ONCE: VTableOnce<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
741 #[allow(clippy::too_many_arguments)]
752 unsafe fn call_once_trampoline<$($arg_ty,)* Ret, F: FnOnce($($arg_ty),*) -> Ret>(
753 state_ptr: *mut u8,
754 $($arg: $arg_ty),*
755 ) -> Ret {
756 let closure = unsafe { ptr::read(state_ptr.cast::<F>()) };
760 closure($($arg),*)
761 }
762
763 VTableOnce {
764 call_once: call_once_trampoline::<$($arg_ty,)* Ret, F>,
765 drop: drop_fn_for::<F>(),
766 }
767 };
768 }
769
770 unsafe impl<$($arg_ty,)* Ret, F> FnMutWithSig<fn($($arg_ty),*) -> Ret> for F
773 where
774 F: FnMut($($arg_ty),*) -> Ret,
775 {
776 const VTABLE_MUT: VTable<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
777 #[allow(clippy::too_many_arguments)]
786 unsafe fn call_mut_trampoline<$($arg_ty,)* Ret, F: FnMut($($arg_ty),*) -> Ret>(
787 state_ptr: *mut u8,
788 $($arg: $arg_ty),*
789 ) -> Ret {
790 let closure_mut = unsafe { &mut *state_ptr.cast::<F>() };
793 closure_mut($($arg),*)
794 }
795
796 VTable {
797 call: call_mut_trampoline::<$($arg_ty,)* Ret, F>,
798 once: <F as FnOnceWithSig<fn($($arg_ty),*) -> Ret>>::VTABLE_ONCE,
799 }
800 };
801 }
802
803 unsafe impl<$($arg_ty,)* Ret, F> FnWithSig<fn($($arg_ty),*) -> Ret> for F
806 where
807 F: Fn($($arg_ty),*) -> Ret,
808 {
809 const VTABLE: VTable<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
810 #[allow(clippy::too_many_arguments)]
819 unsafe fn call_trampoline<$($arg_ty,)* Ret, F: Fn($($arg_ty),*) -> Ret>(
820 state_ptr: *mut u8,
821 $($arg: $arg_ty),*
822 ) -> Ret {
823 let closure_ref = unsafe { &*state_ptr.cast::<F>() };
826 closure_ref($($arg),*)
827 }
828
829 VTable {
830 call: call_trampoline::<$($arg_ty,)* Ret, F>,
831 once: <F as FnOnceWithSig<fn($($arg_ty),*) -> Ret>>::VTABLE_ONCE,
832 }
833 };
834 }
835
836 impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
837 InlineFn<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
838 {
839 #[inline]
841 #[allow(clippy::too_many_arguments)]
842 pub fn call(&self, $($arg: $arg_ty),*) -> Ret {
843 unsafe { (self.raw.vtable.call)(self.raw.as_ptr() $(, $arg)*) }
847 }
848 }
849
850 impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
851 InlineFnMut<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
852 {
853 #[inline]
855 #[allow(clippy::too_many_arguments)]
856 pub fn call(&mut self, $($arg: $arg_ty),*) -> Ret {
857 unsafe { (self.raw.vtable.call)(self.raw.as_ptr() $(, $arg)*) }
861 }
862 }
863
864 impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
865 InlineFnOnce<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
866 {
867 #[inline]
869 #[allow(clippy::too_many_arguments)]
870 pub fn call(self, $($arg: $arg_ty),*) -> Ret {
871 let raw = ManuallyDrop::new(self.raw);
872 unsafe { (raw.vtable.call_once)(raw.as_ptr() $(, $arg)*) }
877 }
878 }
879 };
880}
881
882impl_inline_fn_for_signature!();
883impl_inline_fn_for_signature!(a1: A1);
884impl_inline_fn_for_signature!(a1: A1, a2: A2);
885impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3);
886impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4);
887impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5);
888impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6);
889impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7);
890impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8);
891
892#[cfg(test)]
893mod tests {
894 use super::*;
895 use core::cell::Cell;
896 use core::fmt::Write as _;
897 use core::sync::atomic::{AtomicUsize, Ordering};
898
899 #[test]
900 fn test_inline_fn_zero_and_multiple_args() {
901 let f0: InlineFn<'_, fn() -> u64> = InlineFn::new(|| 42);
902 assert_eq!(f0.call(), 42);
903
904 let factor = 3u32;
905 let f2: InlineFn<'_, fn(u32, u32) -> u32> = InlineFn::new(|a, b| (a + b) * factor);
906 assert_eq!(f2.call(4, 5), 27);
907 assert_eq!(f2.call(1, 2), 9);
908
909 let f8: InlineFn<'_, fn(u32, u32, u32, u32, u32, u32, u32, u32) -> u32> =
910 InlineFn::new(|a1, a2, a3, a4, a5, a6, a7, a8| a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8);
911 assert_eq!(f8.call(1, 2, 3, 4, 5, 6, 7, 8), 36);
912 }
913
914 #[test]
915 fn test_zero_sized_closure_and_non_static_refs() {
916 let zst_fn: InlineFn<'_, fn(u32) -> u32, 0> = InlineFn::new(|x: u32| x + 1);
917 assert_eq!(zst_fn.call(9), 10);
918
919 fn call_with_local_ref<'a, 'b>(local_val: &'a u32, arg: &'b u32) -> u32 {
920 let borrow_fn: InlineFn<'a, fn(&'b u32) -> u32> =
921 InlineFn::new(|r: &'b u32| *r + *local_val);
922 borrow_fn.call(arg)
923 }
924
925 let local_val = 100u32;
926 let arg = 23u32;
927 assert_eq!(call_with_local_ref(&local_val, &arg), 123);
928 }
929
930 #[test]
931 fn test_zst_with_drop_and_resize() {
932 static ZST_DROPS: AtomicUsize = AtomicUsize::new(0);
933 struct ZstGuard;
934 impl Drop for ZstGuard {
935 fn drop(&mut self) {
936 ZST_DROPS.fetch_add(1, Ordering::SeqCst);
937 }
938 }
939
940 ZST_DROPS.store(0, Ordering::SeqCst);
941 {
942 let guard = ZstGuard;
943 let f: InlineFn<'static, fn() -> u32, 0> = InlineFn::new(move || {
944 let _ = &guard;
945 7
946 });
947 assert_eq!(f.call(), 7);
948 let widened: InlineFn<'static, fn() -> u32, 8> = f.resize::<8>();
949 assert_eq!(widened.call(), 7);
950 assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 0);
951 }
952 assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 1);
953
954 {
955 let guard = ZstGuard;
956 let f_once: InlineFnOnce<'static, fn() -> u32, 0> = InlineFnOnce::new(move || {
957 drop(guard);
958 11
959 });
960 let widened_once: InlineFnOnce<'static, fn() -> u32, 8> = f_once.resize::<8>();
961 assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 1);
962 assert_eq!(widened_once.call(), 11);
963 assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 2);
964 }
965 assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 2);
966 }
967
968 #[test]
969 fn test_inline_fn_mut() {
970 let mut sum = 0u64;
971 {
972 let mut f: InlineFnMut<'_, fn(u64) -> u64> = InlineFnMut::new(|x| {
973 sum += x;
974 sum
975 });
976 assert_eq!(f.call(10), 10);
977 assert_eq!(f.call(25), 35);
978 }
979 assert_eq!(sum, 35);
980 }
981
982 #[test]
983 fn test_inline_fn_once_consumes_and_drops_once() {
984 struct DropCounter<'a>(&'a Cell<usize>, u32);
985 impl Drop for DropCounter<'_> {
986 fn drop(&mut self) {
987 self.0.set(self.0.get() + 1);
988 }
989 }
990
991 let drops = Cell::new(0);
992 let token = DropCounter(&drops, 99);
993 let f: InlineFnOnce<'_, fn(u32) -> u32> = InlineFnOnce::new(move |x| {
994 let t = token;
995 t.1 + x
996 });
997 assert_eq!(drops.get(), 0);
998 assert_eq!(f.call(1), 100);
999 assert_eq!(drops.get(), 1);
1000 }
1001
1002 #[test]
1003 fn test_drop_without_calling() {
1004 struct DropCounter<'a>(&'a Cell<usize>, [u64; 2]);
1005 impl Drop for DropCounter<'_> {
1006 fn drop(&mut self) {
1007 self.0.set(self.0.get() + 1);
1008 }
1009 }
1010
1011 let drops = Cell::new(0);
1012 {
1013 let token = DropCounter(&drops, [1, 2]);
1014 let _f: InlineFn<'_, fn() -> u64> = InlineFn::new(move || token.1[0] + token.1[1]);
1015 assert_eq!(drops.get(), 0);
1016 }
1017 assert_eq!(drops.get(), 1);
1018
1019 {
1020 let token = DropCounter(&drops, [3, 4]);
1021 let _f_once: InlineFnOnce<'_, fn() -> u64> =
1022 InlineFnOnce::new(move || token.1[0] + token.1[1]);
1023 assert_eq!(drops.get(), 1);
1024 }
1025 assert_eq!(drops.get(), 2);
1026 }
1027
1028 #[test]
1029 fn test_custom_sizes_and_resize() {
1030 struct DropCounter<'a>(&'a Cell<usize>, [u64; 2]);
1031 impl Drop for DropCounter<'_> {
1032 fn drop(&mut self) {
1033 self.0.set(self.0.get() + 1);
1034 }
1035 }
1036
1037 let data = [10u64, 20, 30, 40, 50, 60];
1038 let small: InlineFn<'_, fn() -> u64, 48> = InlineFn::new(move || data.iter().sum());
1039 assert_eq!(small.call(), 210);
1040
1041 let same_size: InlineFn<'_, fn() -> u64, 48> = small.resize::<48>();
1042 assert_eq!(same_size.call(), 210);
1043
1044 let larger: InlineFn<'_, fn() -> u64, 64> = same_size.resize::<64>();
1045 assert_eq!(larger.call(), 210);
1046
1047 let drops = Cell::new(0);
1049 {
1050 let mut token = DropCounter(&drops, [5, 10]);
1051 let mut f_mut: InlineFnMut<'_, fn(u64) -> u64, 24> = InlineFnMut::new(move |x| {
1052 token.1[0] += x;
1053 token.1[0] + token.1[1]
1054 });
1055 assert_eq!(f_mut.call(3), 18);
1056 let mut f_mut_wide: InlineFnMut<'_, fn(u64) -> u64, 48> = f_mut.resize::<48>();
1057 assert_eq!(drops.get(), 0);
1058 assert_eq!(f_mut_wide.call(2), 20);
1059 }
1060 assert_eq!(drops.get(), 1);
1061
1062 {
1063 let token = DropCounter(&drops, [20, 30]);
1064 let f_once: InlineFnOnce<'_, fn(u64) -> u64, 24> = InlineFnOnce::new(move |x| {
1065 let t = token;
1066 t.1[0] + t.1[1] + x
1067 });
1068 let f_once_wide: InlineFnOnce<'_, fn(u64) -> u64, 48> = f_once.resize::<48>();
1069 assert_eq!(drops.get(), 1);
1070 assert_eq!(f_once_wide.call(4), 54);
1071 assert_eq!(drops.get(), 2);
1072 }
1073 }
1074
1075 #[test]
1076 fn test_align8_with_padding() {
1077 #[repr(C, align(8))]
1078 struct Padded<'a> {
1079 tag: u8,
1080 value: u64,
1082 drops: &'a Cell<usize>,
1083 }
1084
1085 impl Drop for Padded<'_> {
1086 fn drop(&mut self) {
1087 self.drops.set(self.drops.get() + 1);
1088 }
1089 }
1090
1091 let drops = Cell::new(0);
1092 {
1093 let p = Padded { tag: 3, value: 100, drops: &drops };
1094 let f: InlineFn<'_, fn(u64) -> u64, 24> =
1095 InlineFn::new(move |x| (p.tag as u64) + p.value + x);
1096 let widened = f.resize::<32>();
1097 assert_eq!(widened.call(7), 110);
1098 assert_eq!(drops.get(), 0);
1099 }
1100 assert_eq!(drops.get(), 1);
1101 }
1102
1103 #[test]
1104 fn test_reentrant_inline_fn_call() {
1105 type RecFn<'a> = InlineFn<'a, fn(u32) -> u32>;
1106 let depth = Cell::new(0u32);
1107 let self_ptr: Cell<*const RecFn<'_>> = Cell::new(core::ptr::null());
1108 let f: RecFn<'_> = InlineFn::new(|n| {
1109 depth.set(depth.get() + 1);
1110 if n == 0 {
1111 0
1112 } else {
1113 let me = unsafe { &*self_ptr.get() };
1115 n + me.call(n - 1)
1116 }
1117 });
1118 self_ptr.set(&f);
1119 assert_eq!(f.call(4), 10);
1120 assert_eq!(depth.get(), 5);
1121 }
1122
1123 #[test]
1124 fn test_conversions_between_variants() {
1125 let drops = Cell::new(0usize);
1126 struct Guard<'a>(&'a Cell<usize>);
1127 impl Drop for Guard<'_> {
1128 fn drop(&mut self) {
1129 self.0.set(self.0.get() + 1);
1130 }
1131 }
1132
1133 let g = Guard(&drops);
1134 let shared: InlineFn<'_, fn(i32) -> i32> = InlineFn::new(move |x| {
1135 let _ = &g;
1136 x * 2
1137 });
1138 assert_eq!(shared.call(5), 10);
1139
1140 let mut mutable = InlineFnMut::from(shared);
1141 assert_eq!(mutable.call(7), 14);
1142
1143 let once = InlineFnOnce::from(mutable);
1144 assert_eq!(once.call(9), 18);
1145 assert_eq!(drops.get(), 1);
1146
1147 let g2 = Guard(&drops);
1148 let shared2: InlineFn<'_, fn(i32) -> i32> = InlineFn::new(move |x| {
1149 let _ = &g2;
1150 x + 1
1151 });
1152 let once_direct = InlineFnOnce::from(shared2);
1153 assert_eq!(once_direct.call(4), 5);
1154 assert_eq!(drops.get(), 2);
1155 }
1156
1157 #[test]
1158 fn test_send_and_sync_variants() {
1159 fn assert_send<T: Send>(_: &T) {}
1160 fn assert_sync<T: Sync>(_: &T) {}
1161
1162 let counter = AtomicUsize::new(1);
1163 let f_sync = SendSyncInlineFn::<fn(usize) -> usize>::new(|v| {
1164 counter.fetch_add(v, Ordering::Relaxed)
1165 });
1166 assert_send(&f_sync);
1167 assert_sync(&f_sync);
1168 assert_eq!(f_sync.call(4), 1);
1169 assert_eq!(counter.load(Ordering::Relaxed), 5);
1170
1171 let f_send: SendInlineFn<'_, fn(usize) -> usize> = f_sync.into_send();
1172 assert_send(&f_send);
1173 assert_eq!(f_send.call(2), 5);
1174
1175 let f_local = f_send.into_local();
1176 assert_eq!(f_local.call(3), 7);
1177
1178 let f_send_explicit =
1179 SendInlineFn::<fn(usize) -> usize>::new_send(|v| counter.load(Ordering::Relaxed) + v);
1180 assert_send(&f_send_explicit);
1181 assert_eq!(f_send_explicit.call(2), 12);
1182
1183 let mut mut_state = 10u32;
1184 let mut f_mut_sync = SendSyncInlineFnMut::<fn(u32) -> u32>::new_send_sync(move |x| {
1185 mut_state += x;
1186 mut_state
1187 });
1188 assert_send(&f_mut_sync);
1189 assert_sync(&f_mut_sync);
1190 assert_eq!(f_mut_sync.call(5), 15);
1191
1192 let mut f_mut_send = f_mut_sync.into_send();
1193 assert_send(&f_mut_send);
1194 assert_eq!(f_mut_send.call(5), 20);
1195
1196 let mut f_mut_local = f_mut_send.into_local();
1197 assert_eq!(f_mut_local.call(5), 25);
1198
1199 let mut f_mut_send2 = SendInlineFnMut::<fn(u32) -> u32>::new_send(move |x| {
1200 mut_state += x;
1201 mut_state
1202 });
1203 assert_send(&f_mut_send2);
1204 assert_eq!(f_mut_send2.call(5), 15);
1205
1206 let mut f_mut_local2 = InlineFnMut::<fn(u32) -> u32>::new_local(move |x| {
1207 mut_state += x;
1208 mut_state
1209 });
1210 assert_eq!(f_mut_local2.call(3), 13);
1211
1212 let f_once_sync =
1213 SendSyncInlineFnOnce::<fn(u32) -> u32>::new_send_sync(move |x| mut_state + x);
1214 assert_send(&f_once_sync);
1215 assert_sync(&f_once_sync);
1216 let f_once_send = f_once_sync.into_send();
1217 assert_send(&f_once_send);
1218 let f_once_local = f_once_send.into_local();
1219 assert_eq!(f_once_local.call(20), 30);
1220
1221 let f_once_send2 = SendInlineFnOnce::<fn(u32) -> u32>::new_send(move |x| mut_state + x);
1222 assert_send(&f_once_send2);
1223 assert_eq!(f_once_send2.call(10), 20);
1224
1225 let f_once_local2 = InlineFnOnce::<fn(u32) -> u32>::new_local(move |x| mut_state + x);
1226 assert_eq!(f_once_local2.call(5), 15);
1227
1228 let local_cell = Cell::new(10u32);
1230 let raw_ptr: *const Cell<u32> = &local_cell;
1231 let local_fn: InlineFn<'_, fn(u32) -> u32> = InlineFn::new_local(move |x| {
1232 let cell = unsafe { &*raw_ptr };
1234 cell.set(cell.get() + x);
1235 cell.get()
1236 });
1237 assert_eq!(local_fn.call(5), 15);
1238 }
1239
1240 #[test]
1241 fn test_debug_formatting() {
1242 struct Buf {
1243 bytes: [u8; 128],
1244 len: usize,
1245 }
1246 impl Buf {
1247 fn new() -> Self {
1248 Self { bytes: [0; 128], len: 0 }
1249 }
1250 fn as_str(&self) -> &str {
1251 core::str::from_utf8(&self.bytes[..self.len]).unwrap()
1252 }
1253 }
1254 impl fmt::Write for Buf {
1255 fn write_str(&mut self, s: &str) -> fmt::Result {
1256 let b = s.as_bytes();
1257 self.bytes[self.len..self.len + b.len()].copy_from_slice(b);
1258 self.len += b.len();
1259 Ok(())
1260 }
1261 }
1262
1263 let f: InlineFn<'_, fn()> = InlineFn::new(|| {});
1264 let mut buf = Buf::new();
1265 write!(&mut buf, "{:?}", f).unwrap();
1266 assert!(buf.as_str().starts_with("InlineFn { size: "));
1267
1268 let f_mut: InlineFnMut<'_, fn()> = InlineFnMut::new(|| {});
1269 let mut buf = Buf::new();
1270 write!(&mut buf, "{:?}", f_mut).unwrap();
1271 assert!(buf.as_str().starts_with("InlineFnMut { size: "));
1272
1273 let f_once: InlineFnOnce<'_, fn()> = InlineFnOnce::new(|| {});
1274 let mut buf = Buf::new();
1275 write!(&mut buf, "{:?}", f_once).unwrap();
1276 assert!(buf.as_str().starts_with("InlineFnOnce { size: "));
1277
1278 let mut buf = Buf::new();
1279 write!(
1280 &mut buf,
1281 "{:?},{:?},{:?}",
1282 Local(PhantomData),
1283 SendOnly(PhantomData),
1284 SendSync(PhantomData)
1285 )
1286 .unwrap();
1287 assert_eq!(buf.as_str(), "Local,SendOnly,SendSync");
1288 }
1289
1290 #[test]
1291 fn test_option_niche_optimization() {
1292 assert_eq!(
1293 core::mem::size_of::<Option<InlineFn<'static, fn()>>>(),
1294 core::mem::size_of::<InlineFn<'static, fn()>>()
1295 );
1296 assert_eq!(
1297 core::mem::size_of::<Option<InlineFnMut<'static, fn()>>>(),
1298 core::mem::size_of::<InlineFnMut<'static, fn()>>()
1299 );
1300 assert_eq!(
1301 core::mem::size_of::<Option<InlineFnOnce<'static, fn()>>>(),
1302 core::mem::size_of::<InlineFnOnce<'static, fn()>>()
1303 );
1304 }
1305}