1#![warn(missing_docs, missing_debug_implementations, unreachable_pub)]
61
62use std::alloc::{self, Layout};
63use std::cmp::max;
64use std::fmt;
65use std::future::Future;
66use std::mem::ManuallyDrop;
67use std::ops::{Deref, DerefMut};
68use std::pin::Pin;
69use std::ptr::{self, NonNull};
70use std::task::{Context, Poll};
71
72pub struct RecycleBox<T>
77where
78 T: ?Sized,
79{
80 ptr: NonNull<T>, base_ptr: NonNull<u8>,
82 layout: Layout,
83}
84
85impl<T> RecycleBox<T> {
86 pub fn new(x: T) -> Self {
88 unsafe { Self::with_layout_unchecked(x, Layout::new::<T>()) }
89 }
90
91 pub fn with_layout(x: T, layout: Layout) -> Self {
97 let x_layout = Layout::new::<T>();
98 let x_size_margin = if x_layout.align() > layout.align() {
99 x_layout.align() - layout.align()
100 } else {
101 0
102 };
103 assert!(x_layout.size() <= (std::usize::MAX - x_size_margin));
104
105 let size = max(x_layout.size() + x_size_margin, layout.size());
106 let safe_layout = Layout::from_size_align(size, layout.align()).unwrap();
107
108 unsafe { Self::with_layout_unchecked(x, safe_layout) }
111 }
112
113 unsafe fn with_layout_unchecked(x: T, layout: Layout) -> Self {
116 let base_ptr = if layout.size() == 0 {
117 NonNull::dangling()
119 } else {
120 NonNull::new(alloc::alloc(layout)).unwrap()
123 };
124 let ptr = compute_ptr(base_ptr.as_ptr(), layout).unwrap(); ptr::write(ptr, x);
126
127 Self {
128 ptr: NonNull::new_unchecked(ptr),
129 base_ptr,
130 layout,
131 }
132 }
133
134 pub fn replace<U>(boxed: Self, x: U) -> (T, RecycleBox<U>) {
140 let boxed = ManuallyDrop::new(boxed);
141 let old: T = unsafe { ptr::read(boxed.ptr.as_ptr()) };
142
143 if let Some(ptr) = compute_ptr(boxed.base_ptr.as_ptr(), boxed.layout) {
144 unsafe {
145 ptr::write(ptr, x);
146
147 (
148 old,
149 RecycleBox {
150 base_ptr: boxed.base_ptr,
151 ptr: NonNull::new_unchecked(ptr),
152 layout: boxed.layout,
153 },
154 )
155 }
156 } else {
157 unsafe {
158 if boxed.layout.size() != 0 {
159 alloc::dealloc(boxed.base_ptr.as_ptr(), boxed.layout);
160 }
161
162 (old, RecycleBox::new(x))
163 }
164 }
165 }
166
167 pub fn take(boxed: Self) -> (T, RecycleBox<()>) {
175 let boxed = ManuallyDrop::new(boxed);
176 let old: T = unsafe { ptr::read(boxed.ptr.as_ptr()) };
177
178 (
179 old,
180 RecycleBox {
181 base_ptr: boxed.base_ptr,
182 ptr: NonNull::dangling(),
183 layout: boxed.layout,
184 },
185 )
186 }
187
188 pub fn pin(x: T) -> Pin<RecycleBox<T>> {
191 RecycleBox::into_pin(RecycleBox::new(x))
192 }
193}
194
195impl<T> RecycleBox<T>
196where
197 T: ?Sized,
198{
199 pub fn recycle<U>(boxed: Self, x: U) -> RecycleBox<U> {
205 if let Some(ptr) = compute_ptr(boxed.base_ptr.as_ptr(), boxed.layout) {
206 unsafe {
207 let boxed = ManuallyDrop::new(boxed);
208 ptr::drop_in_place(boxed.ptr.as_ptr());
209 ptr::write(ptr, x);
210 RecycleBox {
211 base_ptr: boxed.base_ptr,
212 ptr: NonNull::new_unchecked(ptr),
213 layout: boxed.layout,
214 }
215 }
216 } else {
217 drop(boxed);
218 RecycleBox::new(x)
219 }
220 }
221
222 pub fn vacate(boxed: Self) -> RecycleBox<()> {
229 unsafe {
230 let boxed = ManuallyDrop::new(boxed);
231 ptr::drop_in_place(boxed.ptr.as_ptr());
232 RecycleBox {
233 base_ptr: boxed.base_ptr,
234 ptr: NonNull::dangling(),
235 layout: boxed.layout,
236 }
237 }
238 }
239
240 pub fn recycle_pinned<U>(boxed: Pin<RecycleBox<T>>, x: U) -> RecycleBox<U> {
257 unsafe { Self::recycle(Pin::into_inner_unchecked(boxed), x) }
258 }
259
260 pub fn vacate_pinned(boxed: Pin<RecycleBox<T>>) -> RecycleBox<()> {
276 unsafe { Self::vacate(Pin::into_inner_unchecked(boxed)) }
277 }
278
279 pub fn into_pin(boxed: Self) -> Pin<Self> {
285 unsafe { Pin::new_unchecked(boxed) }
289 }
290
291 pub unsafe fn from_raw_parts(ptr: *mut T, base_ptr: *mut u8, layout: Layout) -> Self {
304 Self {
305 ptr: NonNull::new_unchecked(ptr),
306 base_ptr: NonNull::new_unchecked(base_ptr),
307 layout,
308 }
309 }
310
311 pub fn into_raw_parts(boxed: Self) -> (*mut T, *mut u8, Layout) {
316 let boxed = ManuallyDrop::new(boxed);
317 (boxed.ptr.as_ptr(), boxed.base_ptr.as_ptr(), boxed.layout)
318 }
319}
320
321fn compute_ptr<T>(base_ptr: *mut u8, layout: Layout) -> Option<*mut T> {
326 let value_layout = Layout::new::<T>();
328 if layout.size() < value_layout.size() {
329 return None;
330 }
331
332 let offset = base_ptr.align_offset(value_layout.align());
335 if offset <= layout.size() - value_layout.size() {
336 Some(unsafe { base_ptr.add(offset) } as *mut T)
337 } else {
338 None
339 }
340}
341
342unsafe impl<T: Send + ?Sized> Send for RecycleBox<T> {}
343unsafe impl<T: Sync + ?Sized> Sync for RecycleBox<T> {}
344
345impl<T> Drop for RecycleBox<T>
346where
347 T: ?Sized,
348{
349 fn drop(&mut self) {
350 unsafe {
351 ptr::drop_in_place(self.ptr.as_ptr());
352 if self.layout.size() != 0 {
353 alloc::dealloc(self.base_ptr.as_ptr(), self.layout);
354 }
355 }
356 }
357}
358
359impl<T> AsRef<T> for RecycleBox<T>
360where
361 T: ?Sized,
362{
363 fn as_ref(&self) -> &T {
364 unsafe { self.ptr.as_ref() }
366 }
367}
368
369impl<T> AsMut<T> for RecycleBox<T>
370where
371 T: ?Sized,
372{
373 fn as_mut(&mut self) -> &mut T {
374 unsafe { self.ptr.as_mut() }
376 }
377}
378
379impl<T> Deref for RecycleBox<T>
380where
381 T: ?Sized,
382{
383 type Target = T;
384
385 fn deref(&self) -> &T {
386 self.as_ref()
387 }
388}
389
390impl<T> DerefMut for RecycleBox<T>
391where
392 T: ?Sized,
393{
394 fn deref_mut(&mut self) -> &mut T {
395 self.as_mut()
396 }
397}
398
399impl<T> fmt::Display for RecycleBox<T>
400where
401 T: fmt::Display + ?Sized,
402{
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 fmt::Display::fmt(&**self, f)
405 }
406}
407
408impl<T> fmt::Debug for RecycleBox<T>
409where
410 T: fmt::Debug + ?Sized,
411{
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 fmt::Debug::fmt(&**self, f)
414 }
415}
416
417impl<T> fmt::Pointer for RecycleBox<T>
418where
419 T: ?Sized,
420{
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 let ptr: *const T = &**self;
423 fmt::Pointer::fmt(&ptr, f)
424 }
425}
426
427impl<T> From<RecycleBox<T>> for Pin<RecycleBox<T>>
428where
429 T: ?Sized,
430{
431 fn from(boxed: RecycleBox<T>) -> Self {
432 unsafe { Pin::new_unchecked(boxed) }
437 }
438}
439
440impl<F> Future for RecycleBox<F>
441where
442 F: ?Sized + Future + Unpin,
443{
444 type Output = F::Output;
445
446 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
447 F::poll(Pin::new(&mut *self), cx)
448 }
449}
450
451#[macro_export]
481macro_rules! coerce_box {
482 ($src:expr) => {{
483 let (base_ptr, ptr, layout) = $crate::RecycleBox::into_raw_parts($src);
484 unsafe { $crate::RecycleBox::from_raw_parts(base_ptr, ptr, layout) }
485 }};
486}
487
488#[cfg(test)]
489mod tests {
490 use crate::RecycleBox;
491 use std::cell::Cell;
492 use std::fmt::Debug;
493 use std::mem;
494 use std::rc::Rc;
495
496 trait TestTrait: Debug {
497 fn name(&self) -> &'static str;
498 }
499
500 #[derive(Debug)]
501 struct TestLoad<T> {
502 load: T,
503 name: &'static str,
504 counter: Rc<Cell<usize>>,
505 }
506 impl<T> TestLoad<T> {
507 fn new(load: T, name: &'static str, counter: Rc<Cell<usize>>) -> Self {
508 counter.set(counter.get() + 1);
509 Self {
510 load,
511 name,
512 counter,
513 }
514 }
515 fn load(&self) -> T
516 where
517 T: Clone,
518 {
519 self.load.clone()
520 }
521 }
522 impl<T: Debug> TestTrait for TestLoad<T> {
523 fn name(&self) -> &'static str {
524 self.name
525 }
526 }
527 impl<T: PartialEq> PartialEq for TestLoad<T> {
528 fn eq(&self, other: &Self) -> bool {
529 self.load == other.load
530 }
531 }
532 impl<T> Drop for TestLoad<T> {
533 fn drop(&mut self) {
534 self.counter.set(self.counter.get() - 1);
535 }
536 }
537
538 #[derive(Debug, PartialEq)]
539 struct EmptyTestLoad;
540 impl TestTrait for EmptyTestLoad {
541 fn name(&self) -> &'static str {
542 "Empty load"
543 }
544 }
545
546 fn has_same_location<T, U>(first: *const T, second: *const U) -> bool {
547 fn distance(a: usize, b: usize) -> usize {
548 if a > b {
549 a - b
550 } else {
551 b - a
552 }
553 }
554
555 distance(first as usize, second as usize)
556 <= distance(mem::align_of::<T>(), mem::align_of::<U>())
557 }
558
559 #[test]
560 fn test_new() {
561 let counter = Rc::new(Cell::new(0));
562 let v = TestLoad::new(5, "A", counter.clone());
563 let b = RecycleBox::new(v);
564 assert_eq!(b.as_ref().load(), 5);
565 assert_eq!(counter.get(), 1);
566 drop(b);
567 assert_eq!(counter.get(), 0);
568 }
569 #[test]
570 fn test_new_zero_sized() {
571 let v = EmptyTestLoad;
572 let _b = RecycleBox::new(v);
573 }
574 #[test]
575 fn test_recycle_with_smaller() {
576 let counter = Rc::new(Cell::new(0));
577 let v1 = TestLoad::new(5.0, "A", counter.clone());
578 let v2 = TestLoad::new(3, "B", counter.clone());
579 let b1 = RecycleBox::new(v1);
580 assert_eq!(counter.get(), 2);
581 let b1_ptr = &*b1 as *const TestLoad<f64>;
582 let b2 = RecycleBox::recycle(b1, v2);
583 assert_eq!(counter.get(), 1);
584 assert_eq!(b2.as_ref().load(), 3);
585 assert!(has_same_location(b1_ptr, &*b2));
586 drop(b2);
587 assert_eq!(counter.get(), 0);
588 }
589 #[test]
590 fn test_recycle_with_bigger() {
591 let counter = Rc::new(Cell::new(0));
592 let v1 = TestLoad::new(5, "A", counter.clone());
593 let v2 = TestLoad::new([1; 10], "B", counter.clone());
594 let b1 = RecycleBox::new(v1);
595 assert_eq!(counter.get(), 2);
596 let b2 = RecycleBox::recycle(b1, v2);
597 assert_eq!(counter.get(), 1);
598 assert_eq!(b2.as_ref().load(), [1; 10]);
599 drop(b2);
600 assert_eq!(counter.get(), 0);
601 }
602 #[test]
603 fn test_recycle_with_zero_sized() {
604 let counter = Rc::new(Cell::new(0));
605 let v1 = TestLoad::new(5, "A", counter.clone());
606 let v2 = EmptyTestLoad;
607 let b1 = RecycleBox::new(v1);
608 assert_eq!(counter.get(), 1);
609 let b1_ptr = &*b1 as *const TestLoad<i32>;
610 let b2 = RecycleBox::recycle(b1, v2);
611 assert!(has_same_location(b1_ptr, &*b2));
612 assert_eq!(counter.get(), 0);
613 assert_eq!(*b2.as_ref(), EmptyTestLoad);
614 }
615 #[test]
616 fn test_recycle_from_zero_sized() {
617 let counter = Rc::new(Cell::new(0));
618 let v1 = EmptyTestLoad;
619 let v2 = TestLoad::new(5, "B", counter.clone());
620 let b1 = RecycleBox::new(v1);
621 let b2 = RecycleBox::recycle(b1, v2);
622 assert_eq!(counter.get(), 1);
623 assert_eq!(b2.as_ref().load(), 5);
624 drop(b2);
625 assert_eq!(counter.get(), 0);
626 }
627 #[test]
628 fn test_vacate() {
629 let counter = Rc::new(Cell::new(0));
630 let v1 = TestLoad::new(5, "A", counter.clone());
631 let b1 = RecycleBox::new(v1);
632 assert_eq!(counter.get(), 1);
633 let b1_ptr = &*b1 as *const TestLoad<i32>;
634 let b2 = RecycleBox::vacate(b1);
635 assert_eq!(counter.get(), 0);
636 let v2 = TestLoad::new(5, "B", counter.clone());
637 let b3 = RecycleBox::recycle(b2, v2);
638 assert!(has_same_location(b1_ptr, &*b3));
639 assert_eq!(counter.get(), 1);
640 assert_eq!(b3.as_ref().load(), 5);
641 }
642 #[test]
643 fn test_replace_with_smaller() {
644 let counter = Rc::new(Cell::new(0));
645 let v1 = TestLoad::new(5.0, "A", counter.clone());
646 let v2 = TestLoad::new(3, "B", counter.clone());
647 let b1 = RecycleBox::new(v1);
648 assert_eq!(counter.get(), 2);
649 let b1_ptr = &*b1 as *const TestLoad<f64>;
650 let (v1bis, b2) = RecycleBox::replace(b1, v2);
651 assert!(has_same_location(b1_ptr, &*b2));
652 assert_eq!(v1bis.load(), 5.0);
653 assert_eq!(counter.get(), 2);
654 assert_eq!(b2.as_ref().load(), 3);
655 drop(b2);
656 assert_eq!(counter.get(), 1);
657 }
658 #[test]
659 fn test_replace_with_bigger() {
660 let counter = Rc::new(Cell::new(0));
661 let v1 = TestLoad::new(5, "A", counter.clone());
662 let v2 = TestLoad::new([1; 10], "B", counter.clone());
663 let b1 = RecycleBox::new(v1);
664 assert_eq!(counter.get(), 2);
665 let (v1bis, b2) = RecycleBox::replace(b1, v2);
666 assert_eq!(v1bis.load(), 5);
667 assert_eq!(counter.get(), 2);
668 assert_eq!(b2.as_ref().load(), [1; 10]);
669 drop(b2);
670 assert_eq!(counter.get(), 1);
671 }
672 #[test]
673 fn test_replace_with_zero_sized() {
674 let counter = Rc::new(Cell::new(0));
675 let v1 = TestLoad::new(5, "A", counter.clone());
676 let v2 = EmptyTestLoad;
677 let b1 = RecycleBox::new(v1);
678 assert_eq!(counter.get(), 1);
679 let b1_ptr = &*b1 as *const TestLoad<i32>;
680 let (v1bis, b2) = RecycleBox::replace(b1, v2);
681 assert!(has_same_location(b1_ptr, &*b2));
682 assert_eq!(v1bis.load(), 5);
683 assert_eq!(counter.get(), 1);
684 assert_eq!(*b2.as_ref(), EmptyTestLoad);
685 }
686 #[test]
687 fn test_replace_from_zero_sized() {
688 let counter = Rc::new(Cell::new(0));
689 let v1 = EmptyTestLoad;
690 let v2 = TestLoad::new(5, "B", counter.clone());
691 let b1 = RecycleBox::new(v1);
692 let (v1bis, b2) = RecycleBox::replace(b1, v2);
693 assert_eq!(v1bis, EmptyTestLoad);
694 assert_eq!(counter.get(), 1);
695 assert_eq!(b2.as_ref().load(), 5);
696 drop(b2);
697 assert_eq!(counter.get(), 0);
698 }
699 #[test]
700 fn test_take() {
701 let counter = Rc::new(Cell::new(0));
702 let v1 = TestLoad::new(5, "A", counter.clone());
703 let b1 = RecycleBox::new(v1);
704 assert_eq!(counter.get(), 1);
705 let b1_ptr = &*b1 as *const TestLoad<i32>;
706 let (v1bis, b2) = RecycleBox::take(b1);
707 assert_eq!(v1bis.load(), 5);
708 assert_eq!(counter.get(), 1);
709 let v2 = TestLoad::new(5, "B", counter.clone());
710 let (bempty, b3) = RecycleBox::replace(b2, v2);
711 assert!(has_same_location(b1_ptr, &*b3));
712 assert_eq!(bempty, ());
713 assert_eq!(counter.get(), 2);
714 assert_eq!(b3.as_ref().load(), 5);
715 }
716 #[test]
717 fn test_coerce_unsized() {
718 let counter = Rc::new(Cell::new(0));
719 let v1 = TestLoad::new(0, "A", counter.clone());
720 let b1 = RecycleBox::new(v1);
721 let mut b_unsized: RecycleBox<dyn TestTrait> = coerce_box!(b1);
722 assert_eq!(counter.get(), 1);
723 assert_eq!(b_unsized.as_ref().name(), "A");
724
725 let v2 = TestLoad::new([0; 10], "B", counter.clone());
726 assert_eq!(counter.get(), 2);
727 b_unsized = coerce_box!(RecycleBox::recycle(b_unsized, v2));
728 assert_eq!(counter.get(), 1);
729 assert_eq!(b_unsized.as_ref().name(), "B");
730
731 let v3 = EmptyTestLoad;
732 b_unsized = coerce_box!(RecycleBox::recycle(b_unsized, v3));
733 assert_eq!(counter.get(), 0);
734 assert_eq!(b_unsized.as_ref().name(), "Empty load");
735 }
736}