Coverage Report

Created: 2026-07-13 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/papaya-0.2.4/src/raw/alloc.rs
Line
Count
Source
1
use std::alloc::Layout;
2
use std::marker::PhantomData;
3
use std::sync::atomic::{AtomicPtr, AtomicU8, Ordering};
4
use std::{alloc, mem, ptr};
5
6
use super::{probe, State};
7
8
// A hash-table laid out in a single allocation.
9
//
10
// Note that the `PhantomData<T>` ensures that the hash-table is invariant
11
// with respect to `T`, as this struct is stored behind an `AtomicPtr`.
12
#[repr(transparent)]
13
pub struct RawTable<T>(u8, PhantomData<T>);
14
15
// The layout of the table allocation.
16
#[repr(C)]
17
struct TableLayout<T> {
18
    /// A mask to get an index into the table from a hash.
19
    mask: usize,
20
21
    /// The maximum probe limit for this table.
22
    limit: usize,
23
24
    /// State for the table resize.
25
    state: State<T>,
26
27
    /// An array of metadata for each entry.
28
    meta: [AtomicU8; 0],
29
30
    /// An array of entries.
31
    entries: [AtomicPtr<T>; 0],
32
}
33
34
// Manages a table allocation.
35
#[repr(C)]
36
pub struct Table<T> {
37
    /// A mask to get an index into the table from a hash.
38
    pub mask: usize,
39
40
    /// The maximum probe limit for this table.
41
    pub limit: usize,
42
43
    // The raw table allocation.
44
    //
45
    // Invariant: This pointer is initialized and valid for reads and writes.
46
    pub raw: *mut RawTable<T>,
47
}
48
49
impl<T> Copy for Table<T> {}
50
51
impl<T> Clone for Table<T> {
52
0
    fn clone(&self) -> Self {
53
0
        *self
54
0
    }
55
}
56
57
impl<T> Table<T> {
58
    // Allocate a table with the provided length and collector.
59
0
    pub fn alloc(len: usize) -> Table<T> {
60
0
        assert!(len.is_power_of_two());
61
62
        // Pad the meta table to fulfill the alignment requirement of an entry.
63
0
        let len = len.max(mem::align_of::<AtomicPtr<T>>());
64
0
        let mask = len - 1;
65
0
        let limit = probe::limit(len);
66
67
0
        let layout = Table::<T>::layout(len);
68
69
        // Allocate the table, zeroing the entries.
70
        //
71
        // Safety: The layout for is guaranteed to be non-zero.
72
0
        let ptr = unsafe { alloc::alloc_zeroed(layout) };
73
0
        if ptr.is_null() {
74
0
            alloc::handle_alloc_error(layout);
75
0
        }
76
77
        // Safety: We just allocated the pointer and ensured it is non-null above.
78
0
        unsafe {
79
0
            // Write the table state.
80
0
            ptr.cast::<TableLayout<T>>().write(TableLayout {
81
0
                mask,
82
0
                limit,
83
0
                meta: [],
84
0
                entries: [],
85
0
                state: State::default(),
86
0
            });
87
0
88
0
            // Initialize the meta table.
89
0
            ptr.add(mem::size_of::<TableLayout<T>>())
90
0
                .cast::<u8>()
91
0
                .write_bytes(super::meta::EMPTY, len);
92
0
        }
93
94
0
        Table {
95
0
            mask,
96
0
            limit,
97
0
            // Invariant: We allocated and initialized the allocation above.
98
0
            raw: ptr.cast::<RawTable<T>>(),
99
0
        }
100
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::alloc
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::alloc
101
102
    // Creates a `Table` from a raw pointer.
103
    //
104
    // # Safety
105
    //
106
    // The pointer must either be null, or a valid pointer created with `Table::alloc`.
107
    #[inline]
108
380k
    pub unsafe fn from_raw(raw: *mut RawTable<T>) -> Table<T> {
109
380k
        if raw.is_null() {
110
380k
            return Table {
111
380k
                raw,
112
380k
                mask: 0,
113
380k
                limit: 0,
114
380k
            };
115
0
        }
116
117
        // Safety: The caller guarantees that the pointer is valid.
118
0
        let layout = unsafe { &*raw.cast::<TableLayout<T>>() };
119
120
0
        Table {
121
0
            raw,
122
0
            mask: layout.mask,
123
0
            limit: layout.limit,
124
0
        }
125
380k
    }
<papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::from_raw
Line
Count
Source
108
380k
    pub unsafe fn from_raw(raw: *mut RawTable<T>) -> Table<T> {
109
380k
        if raw.is_null() {
110
380k
            return Table {
111
380k
                raw,
112
380k
                mask: 0,
113
380k
                limit: 0,
114
380k
            };
115
0
        }
116
117
        // Safety: The caller guarantees that the pointer is valid.
118
0
        let layout = unsafe { &*raw.cast::<TableLayout<T>>() };
119
120
0
        Table {
121
0
            raw,
122
0
            mask: layout.mask,
123
0
            limit: layout.limit,
124
0
        }
125
380k
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::from_raw
126
127
    // Returns the metadata entry at the given index.
128
    //
129
    // # Safety
130
    //
131
    // The index must be in-bounds for the length of the table.
132
    #[inline]
133
0
    pub unsafe fn meta(&self, i: usize) -> &AtomicU8 {
134
0
        debug_assert!(i < self.len());
135
136
        // Safety: The caller guarantees the index is in-bounds.
137
        unsafe {
138
0
            let meta = self.raw.add(mem::size_of::<TableLayout<T>>());
139
0
            &*meta.cast::<AtomicU8>().add(i)
140
        }
141
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::meta
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::meta
142
143
    // Returns the entry at the given index.
144
    //
145
    // # Safety
146
    //
147
    // The index must be in-bounds for the length of the table.
148
    #[inline]
149
0
    pub unsafe fn entry(&self, i: usize) -> &AtomicPtr<T> {
150
0
        debug_assert!(i < self.len());
151
152
        // Safety: The caller guarantees the index is in-bounds.
153
        unsafe {
154
0
            let meta = self.raw.add(mem::size_of::<TableLayout<T>>());
155
0
            let entries = meta.add(self.len()).cast::<AtomicPtr<T>>();
156
0
            &*entries.add(i)
157
        }
158
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::entry
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::entry
159
160
    /// Returns the length of the table.
161
    #[inline]
162
0
    pub fn len(&self) -> usize {
163
0
        self.mask + 1
164
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::len
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::len
165
166
    // Returns a reference to the table state.
167
    #[inline]
168
0
    pub fn state(&self) -> &State<T> {
169
        // Safety: The raw table pointer is always valid for reads and writes.
170
0
        unsafe { &(*self.raw.cast::<TableLayout<T>>()).state }
171
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::state
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::state
172
173
    // Returns a mutable reference to the table state.
174
    #[inline]
175
0
    pub fn state_mut(&mut self) -> &mut State<T> {
176
        // Safety: The raw table pointer is always valid for reads and writes.
177
0
        unsafe { &mut (*self.raw.cast::<TableLayout<T>>()).state }
178
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::state_mut
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::state_mut
179
180
    // Returns a pointer to the next table, if it has already been created.
181
    #[inline]
182
0
    pub fn next_table(&self) -> Option<Self> {
183
0
        let next = self.state().next.load(Ordering::Acquire);
184
185
0
        if !next.is_null() {
186
            // Safety: We verified that the pointer is non-null, and the
187
            // next pointer is otherwise a valid pointer to a table allocation.
188
0
            return unsafe { Some(Table::from_raw(next)) };
189
0
        }
190
191
0
        None
192
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::next_table
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::next_table
193
194
    // Deallocate the table.
195
    //
196
    // # Safety
197
    //
198
    // The table may not be accessed in any way after this method is
199
    // called.
200
0
    pub unsafe fn dealloc(table: Table<T>) {
201
0
        let layout = Self::layout(table.len());
202
203
        // Safety: The raw table pointer is valid and allocated with `alloc::alloc_zeroed`.
204
        // Additionally, the caller guarantees that the allocation will not be accessed after
205
        // this point.
206
0
        unsafe {
207
0
            ptr::drop_in_place(table.raw.cast::<TableLayout<T>>());
208
0
            alloc::dealloc(table.raw.cast::<u8>(), layout);
209
0
        };
210
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::dealloc
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::dealloc
211
212
    // Returns the non-zero layout for a table allocation.
213
0
    fn layout(len: usize) -> Layout {
214
0
        let size = mem::size_of::<TableLayout<T>>()
215
0
            + (mem::size_of::<u8>() * len) // Metadata table.
216
0
            + (mem::size_of::<AtomicPtr<T>>() * len); // Entry pointers.
217
                                                      //
218
0
        Layout::from_size_align(size, mem::align_of::<TableLayout<T>>()).unwrap()
219
0
    }
Unexecuted instantiation: <papaya::raw::alloc::Table<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>::layout
Unexecuted instantiation: <papaya::raw::alloc::Table<_>>::layout
220
}
221
222
#[test]
223
fn layout() {
224
    unsafe {
225
        let table: Table<u8> = Table::alloc(4);
226
        let table: Table<u8> = Table::from_raw(table.raw);
227
228
        // The capacity is padded for pointer alignment.
229
        assert_eq!(table.mask, 7);
230
        assert_eq!(table.len(), 8);
231
        Table::dealloc(table);
232
    }
233
}