talc/source/
global_alloc.rs1use core::{
2 alloc::{GlobalAlloc, Layout},
3 fmt::Debug,
4 mem::{align_of, size_of},
5 ptr::{NonNull, addr_of_mut},
6};
7
8use crate::{
9 base::binning::Binning,
10 base::{CHUNK_UNIT, Talc},
11 node::Node,
12 ptr_utils,
13};
14
15use super::Source;
16
17#[derive(Debug)]
38pub struct GlobalAllocSource<G: GlobalAlloc> {
39 block_size: usize,
40 allocator: G,
41 allocation_chain: Option<NonNull<Option<NonNull<Node>>>>,
42}
43
44unsafe impl<G: GlobalAlloc + Send> Send for GlobalAllocSource<G> {}
46
47const DEFAULT_BLOCK_SIZE: usize = 1 << 20;
49
50impl<G: GlobalAlloc> GlobalAllocSource<G> {
51 pub const fn new(allocator: G) -> Self {
57 Self { block_size: DEFAULT_BLOCK_SIZE, allocator, allocation_chain: None }
58 }
59
60 pub const fn with_block_size(allocator: G, block_size: usize) -> Self {
66 assert!(block_size.is_power_of_two());
67
68 Self { block_size, allocator, allocation_chain: None }
69 }
70}
71
72unsafe impl<G: GlobalAlloc + Debug> Source for GlobalAllocSource<G> {
73 fn acquire<B: Binning>(talc: &mut Talc<Self, B>, layout: Layout) -> Result<(), ()> {
74 let mut required_size = layout.size() + layout.align();
78
79 required_size += CHUNK_UNIT + CHUNK_UNIT;
83 required_size += size_of::<Footer>();
85
86 if !talc.is_metadata_established() {
87 required_size += crate::min_first_heap_layout::<B>().size();
89 required_size += size_of::<Option<NonNull<Node>>>();
91 }
92
93 let required_blocks =
94 (required_size + talc.source.block_size - 1) & !(talc.source.block_size - 1);
95
96 debug_assert!(CHUNK_UNIT > align_of::<Footer>());
97 let layout = unsafe { Layout::from_size_align_unchecked(required_blocks, BLOCK_ALIGN) };
98 let allocation = unsafe { talc.source.allocator.alloc(layout) };
99
100 if allocation.is_null() {
101 return Err(());
102 }
103
104 let mut base_offset = 0;
105
106 let meta = if let Some(meta) = talc.source.allocation_chain {
107 meta.as_ptr()
108 } else {
109 let meta = ptr_utils::align_up_by(allocation, align_of::<Option<NonNull<Node>>>())
110 .cast::<Option<NonNull<Node>>>();
111
112 unsafe {
113 *meta = None;
114 }
115
116 base_offset = size_of::<Option<NonNull<Node>>>() + meta as usize - allocation as usize;
117
118 let allocation_chain = NonNull::new(meta);
119 debug_assert!(allocation_chain.is_some());
120 talc.source.allocation_chain = allocation_chain;
121
122 meta
123 };
124
125 let heap_end = unsafe {
126 talc.claim(
127 allocation.wrapping_add(base_offset),
128 required_blocks - base_offset - size_of::<Footer>(),
129 )
130 .unwrap_unchecked()
131 };
132
133 unsafe {
134 let footer = heap_end.as_ptr().cast::<Footer>();
135 Node::link_at(addr_of_mut!((*footer).node), Node { next: *meta, next_of_prev: meta });
136 (*footer).base = allocation;
137 (*footer).size = required_blocks;
138 }
139
140 Ok(())
141 }
142
143 const TRACK_HEAP_END: bool = true;
144
145 unsafe fn resize(
146 &mut self,
147 chunk_base: *mut u8,
148 heap_end: *mut u8,
149 is_heap_base: bool,
150 ) -> *mut u8 {
151 if is_heap_base {
152 let footer = heap_end.cast::<Footer>();
153 Node::unlink((*footer).node);
154
155 let layout = Layout::from_size_align_unchecked((*footer).size, BLOCK_ALIGN);
156 self.allocator.dealloc((*footer).base, layout);
157
158 chunk_base
159 } else {
160 heap_end
161 }
162 }
163}
164
165impl<G: GlobalAlloc> Drop for GlobalAllocSource<G> {
166 fn drop(&mut self) {
167 if let Some(chain) = self.allocation_chain {
168 unsafe {
169 for node_ptr in Node::iter_mut(chain.as_ptr().read()) {
170 let footer = node_ptr.cast::<Footer>().as_ptr();
171 let layout = Layout::from_size_align_unchecked((*footer).size, CHUNK_UNIT);
172 self.allocator.dealloc((*footer).base, layout);
173 }
174 }
175 }
176 }
177}
178
179#[repr(C)] struct Footer {
181 node: Node,
182 base: *mut u8,
183 size: usize,
184}
185
186const BLOCK_ALIGN: usize = 1;