1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use core::{alloc::Layout, ptr::NonNull};

use allocator_api2::alloc::{AllocError, Allocator};

use crate::{polyfill::nonnull, BumpAllocator};

/// Wraps an bump allocator and does nothing on [`deallocate`](Allocator::deallocate).
///
/// This type only implements [`Allocator`] for wrapped types that implement [`BumpAllocator`], so you don't accidentally leak memory.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WithoutDealloc<A>(pub A);

impl<A> WithoutDealloc<A> {
    /// Wraps `self` in [`WithoutShrink`] so that [`shrink`] becomes a no-op.
    ///
    /// [`shrink`]: Allocator::shrink
    pub fn without_shrink(self) -> WithoutShrink<Self> {
        WithoutShrink(self)
    }
}

unsafe impl<A: BumpAllocator> Allocator for WithoutDealloc<A> {
    #[inline(always)]
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.allocate(layout)
    }

    #[inline(always)]
    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.allocate_zeroed(layout)
    }

    #[inline(always)]
    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        let _ = (ptr, layout);
    }

    #[inline(always)]
    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.grow(ptr, old_layout, new_layout)
    }

    #[inline(always)]
    unsafe fn grow_zeroed(
        &self,
        ptr: NonNull<u8>,
        old_layout: Layout,
        new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        self.0.grow_zeroed(ptr, old_layout, new_layout)
    }

    #[inline(always)]
    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.shrink(ptr, old_layout, new_layout)
    }
}

unsafe impl<A: BumpAllocator> BumpAllocator for WithoutDealloc<A> {}

/// Wraps an bump allocator and does nothing on [`shrink`](Allocator::shrink).
///
/// This type only implements [`Allocator`] for wrapped types that implement [`BumpAllocator`], so you don't accidentally leak memory.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WithoutShrink<A>(pub A);

impl<A> WithoutShrink<A> {
    /// Wraps `self` in [`WithoutDealloc`] so that [`deallocate`] becomes a no-op.
    ///
    /// [`deallocate`]: Allocator::deallocate
    pub fn without_dealloc(self) -> WithoutDealloc<Self> {
        WithoutDealloc(self)
    }
}

unsafe impl<A: BumpAllocator> Allocator for WithoutShrink<A> {
    #[inline(always)]
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.allocate(layout)
    }

    #[inline(always)]
    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.allocate_zeroed(layout)
    }

    #[inline(always)]
    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        self.0.deallocate(ptr, layout);
    }

    #[inline(always)]
    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.0.grow(ptr, old_layout, new_layout)
    }

    #[inline(always)]
    unsafe fn grow_zeroed(
        &self,
        ptr: NonNull<u8>,
        old_layout: Layout,
        new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        self.0.grow_zeroed(ptr, old_layout, new_layout)
    }

    #[inline(always)]
    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        #[cold]
        #[inline(never)]
        unsafe fn shrink_unfit<A: BumpAllocator>(
            this: &WithoutShrink<A>,
            ptr: NonNull<u8>,
            old_layout: Layout,
            new_layout: Layout,
        ) -> Result<NonNull<[u8]>, AllocError> {
            let new_ptr = this.0.allocate(new_layout)?.cast::<u8>();
            nonnull::copy_nonoverlapping(ptr, new_ptr, old_layout.size());
            Ok(nonnull::slice_from_raw_parts(new_ptr, new_layout.size()))
        }

        if nonnull::is_aligned_to(ptr, new_layout.align()) {
            Ok(nonnull::slice_from_raw_parts(ptr, new_layout.size()))
        } else {
            // expected to virtually never occur
            shrink_unfit(self, ptr, old_layout, new_layout)
        }
    }
}

unsafe impl<A: BumpAllocator> BumpAllocator for WithoutShrink<A> {}