mmap_allocator/
mmap_allocator.rs1use libc::{off_t, size_t};
33use std::alloc::{GlobalAlloc, Layout};
34use std::cell::Cell;
35use std::os::raw::{c_int, c_long, c_void};
36use std::ptr;
37
38#[derive(Debug, Clone, Copy)]
40pub struct MmapAllocator;
41
42impl Default for MmapAllocator {
43 #[inline]
44 fn default() -> Self {
45 Self
46 }
47}
48
49impl MmapAllocator {
50 #[inline]
52 pub const fn new() -> Self {
53 Self
54 }
55}
56
57unsafe impl GlobalAlloc for MmapAllocator {
73 #[inline]
79 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
80 const ADDR: *mut c_void = ptr::null_mut::<c_void>();
81 let length = layout.size() as size_t;
82 const PROT: c_int = libc::PROT_READ | libc::PROT_WRITE;
83
84 const FLAGS: c_int = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
88 const FD: c_int = -1; const OFFSET: off_t = 0; match mmap(ADDR, length, PROT, FLAGS, FD, OFFSET) {
92 libc::MAP_FAILED => ptr::null_mut::<u8>(),
93 ret => {
94 let ptr = ret as usize;
95 assert_eq!(0, ptr % layout.align());
96 ret as *mut u8
97 }
98 }
99 }
100
101 #[inline]
102 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
103 let addr = ptr as *mut c_void;
104 let length = layout.size() as size_t;
105
106 munmap(addr, length);
107 }
108
109 #[inline]
113 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
114 self.alloc(layout)
116 }
117
118 #[cfg(linux)]
119 #[inline]
120 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
121 let old_address = ptr as *mut c_void;
122 let old_size = layout.size() as size_t;
123 let new_size = new_size as size_t;
124 let FLAGS = libc::MREMAP_MAYMOVE;
125
126 match mremap(old_address, old_size, new_size, FLAGS) {
127 libc::MAP_FAILED => ptr::null_mut::<u8>(),
128 ret => ret as *mut u8,
129 }
130 }
131}
132
133extern "C" {
134 fn mmap(
135 addr: *mut c_void,
136 length: size_t,
137 prot: c_int,
138 flags: c_int,
139 fd: c_int,
140 offset: off_t,
141 ) -> *mut c_void;
142
143 fn munmap(addr: *mut c_void, length: size_t);
144
145 #[cfg(linux)]
146 fn mremap(
147 old_address: *mut c_void,
148 old_size: size_t,
149 new_size: size_t,
150 flags: c_int,
151 ) -> *mut c_void;
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use std::mem;
158 use std::ptr;
159
160 const ENOERR: i32 = 0;
161
162 fn clear_errno() {
163 unsafe { *libc::__errno_location() = 0 }
164 }
165
166 fn errno() -> i32 {
167 unsafe { *libc::__errno_location() }
168 }
169
170 #[test]
171 fn default() {
172 let _alloc = MmapAllocator::default();
173 }
174
175 #[test]
176 fn allocate() {
177 unsafe {
178 type T = i64;
179 let alloc = MmapAllocator::default();
180
181 let layout = Layout::new::<i64>();
182 let ptr = alloc.alloc(layout) as *mut T;
183 assert_ne!(std::ptr::null(), ptr);
184
185 *ptr = 84;
186 assert_eq!(84, *ptr);
187
188 *ptr = *ptr * -2;
189 assert_eq!(-168, *ptr);
190
191 alloc.dealloc(ptr as *mut u8, layout)
192 }
193 }
194
195 #[test]
196 fn allocate_too_large() {
197 unsafe {
198 clear_errno();
199
200 type T = String;
201 let alloc = MmapAllocator::default();
202
203 let align = mem::align_of::<T>();
204 let size = std::usize::MAX - mem::size_of::<T>();
205 let layout = Layout::from_size_align(size, align).unwrap();
206
207 assert_eq!(ptr::null(), alloc.alloc(layout));
208 assert_ne!(ENOERR, errno());
209 }
210 }
211
212 #[test]
213 fn allocate_zero_size() {
214 unsafe {
215 clear_errno();
216
217 type T = String;
218 let alloc = MmapAllocator::default();
219
220 let align = mem::align_of::<T>();
221 let size = 0;
222 let layout = Layout::from_size_align(size, align).unwrap();
223
224 assert_eq!(ptr::null(), alloc.alloc(layout));
225 assert_ne!(ENOERR, errno());
226 }
227 }
228
229 #[test]
230 fn alloc_zeroed() {
231 unsafe {
232 type T = [u8; 1025];
233 let alloc = MmapAllocator::default();
234
235 let layout = Layout::new::<T>();
236 let ptr = alloc.alloc_zeroed(layout) as *mut T;
237 let s: &[u8] = &*ptr;
238
239 for u in s {
240 assert_eq!(0, *u);
241 }
242
243 alloc.dealloc(ptr as *mut u8, layout);
244 }
245 }
246
247 #[test]
248 fn realloc() {
249 unsafe {
250 type T = [u8; 1025];
251 let alloc = MmapAllocator::default();
252
253 let layout = Layout::new::<T>();
254 let ptr = alloc.alloc(layout) as *mut T;
255
256 let ts = &mut *ptr;
257 for t in ts.iter_mut() {
258 *t = 1;
259 }
260
261 type U = (T, T);
262
263 let new_size = mem::size_of::<U>();
264 let ptr = alloc.realloc(ptr as *mut u8, layout, new_size) as *mut T;
265 let layout = Layout::from_size_align(new_size, layout.align()).unwrap();
266
267 let ts = &mut *ptr;
268 for t in ts.iter_mut() {
269 assert_eq!(1, *t);
270 *t = 2;
271 }
272
273 let new_size = mem::size_of::<u8>();
274 let ptr = alloc.realloc(ptr as *mut u8, layout, new_size);
275 let layout = Layout::from_size_align(new_size, layout.align()).unwrap();
276
277 assert_eq!(2, *ptr);
278
279 alloc.dealloc(ptr, layout);
280 }
281 }
282
283 #[test]
284 fn realloc_too_large() {
285 unsafe {
286 type T = [u8; 1025];
287 let alloc = MmapAllocator::default();
288
289 let layout = Layout::new::<T>();
290 let ptr = alloc.alloc(layout) as *mut T;
291
292 let ts = &mut *ptr;
293 for t in ts.iter_mut() {
294 *t = 1;
295 }
296
297 let new_size = std::usize::MAX - mem::size_of::<T>();
298 let new_ptr = alloc.realloc(ptr as *mut u8, layout, new_size);
299
300 assert!(new_ptr.is_null());
301 assert_ne!(ENOERR, errno());
302
303 for t in ts.iter() {
304 assert_eq!(1, *t);
305 }
306
307 alloc.dealloc(ptr as *mut u8, layout);
308 }
309 }
310}
311
312thread_local! {
313 static PAGE_SIZE: Cell<usize> = Cell::new(0);
314}
315
316#[inline]
320pub fn page_size() -> usize {
321 PAGE_SIZE.with(|s| match s.get() {
322 0 => {
323 let ret = unsafe { sysconf(libc::_SC_PAGE_SIZE) as usize };
324 s.set(ret);
325 ret
326 }
327 ret => ret,
328 })
329}
330
331extern "C" {
332 fn sysconf(name: c_int) -> c_long;
333}