Skip to content

Commit 4572b98

Browse files
committed
feat!: Add unstable Allocator API
Changes toolchain requirement to `nightly`.
1 parent da47a0e commit 4572b98

File tree

8 files changed

+332
-171
lines changed

8 files changed

+332
-171
lines changed

benches/buddy_alloc.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
#![feature(allocator_api)]
2+
13
#[macro_use]
24
extern crate criterion;
35

4-
use criterion::{Criterion, Throughput};
6+
use {
7+
criterion::{Criterion, Throughput},
8+
std::alloc::{Allocator, Layout},
9+
};
510

611
use buddy_alloc::buddy_alloc::{BuddyAlloc, BuddyAllocParam};
712

@@ -20,18 +25,21 @@ fn with_allocator<F: FnOnce(BuddyAlloc)>(f: F) {
2025

2126
fn bench_alloc(allocator: &mut BuddyAlloc, alloc_size: usize) {
2227
for _i in 0..(ALLOC_SIZE / alloc_size) {
23-
allocator.malloc(alloc_size);
28+
allocator
29+
.allocate(Layout::from_size_align(alloc_size, 1).unwrap())
30+
.unwrap();
2431
}
2532
}
2633

2734
fn bench_alloc_then_free(allocator: &mut BuddyAlloc, alloc_size: usize) {
2835
let count = ALLOC_SIZE / alloc_size;
36+
let layout = Layout::from_size_align(alloc_size, 1).unwrap();
2937
let mut ptrs = Vec::with_capacity(count);
3038
for _i in 0..count {
31-
ptrs.push(allocator.malloc(alloc_size));
39+
ptrs.push(allocator.allocate(layout).unwrap());
3240
}
3341
for _i in 0..count {
34-
allocator.free(ptrs.pop().unwrap());
42+
unsafe { allocator.deallocate(ptrs.pop().unwrap().cast(), layout) };
3543
}
3644
}
3745

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
stable
1+
nightly

src/buddy_alloc.rs

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
77
#![allow(clippy::needless_range_loop)]
88

9+
use core::{
10+
alloc::{AllocError, Allocator, Layout},
11+
ptr::NonNull,
12+
};
13+
914
const OOM_MSG: &str = "requires more memory space to initialize BuddyAlloc";
1015
const LEAF_ALIGN_ERROR_MSG: &str = "leaf size must be aligned to 16 bytes";
1116
/// required to align to 16 bytes, since Node takes 16 bytes on 64-bits machine.
@@ -299,11 +304,56 @@ impl BuddyAlloc {
299304
self.unavailable = end_addr - base_addr;
300305
}
301306

302-
pub fn malloc(&mut self, nbytes: usize) -> *mut u8 {
307+
/// available bytes
308+
pub fn available_bytes(&self) -> usize {
309+
self.end_addr - self.unavailable - self.base_addr
310+
}
311+
312+
fn entry(&self, i: usize) -> &Entry {
313+
debug_assert!(i < self.entries_size, "index out of range");
314+
unsafe { self.entries.add(i).as_ref().expect("entry") }
315+
}
316+
317+
/// find k for p
318+
fn find_k_for_p(&self, p: *const u8) -> usize {
319+
for k in 0..(self.entries_size - 1) {
320+
if bit_isset(self.entry(k + 1).split, self.block_index(k + 1, p)) {
321+
debug_assert!(bit_isset(self.entry(k).alloc, self.block_index(k, p)));
322+
return k;
323+
}
324+
}
325+
0
326+
}
327+
328+
/// block index of p under k
329+
fn block_index(&self, k: usize, p: *const u8) -> usize {
330+
if (p as usize) < self.base_addr {
331+
// TODO handle this outside
332+
panic!("out of memory");
333+
}
334+
let n = p as usize - self.base_addr;
335+
// equal to: n / block_size_2base(k, self.leaf2base);
336+
let index = (n >> k) >> self.leaf2base;
337+
debug_assert!(index < nblock(k, self.entries_size));
338+
index
339+
}
340+
341+
/// block addr of index under k
342+
fn block_addr(&self, k: usize, i: usize) -> usize {
343+
// equal to: i * block_size_2base(k, self.leaf2base);
344+
let n = (i << k) << self.leaf2base;
345+
self.base_addr + n
346+
}
347+
}
348+
349+
unsafe impl Allocator for BuddyAlloc {
350+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
351+
let nbytes = layout.size();
352+
// TODO: alignment!
303353
let fk = first_up_k(nbytes, 1 << self.leaf2base);
304354
let mut k = match (fk..self.entries_size).find(|&k| !Node::is_empty(self.entry(k).free)) {
305355
Some(k) => k,
306-
None => return core::ptr::null_mut(),
356+
None => return Err(AllocError),
307357
};
308358
let p: *mut u8 = Node::pop(self.entry(k).free) as *mut u8;
309359
bit_set(self.entry(k).alloc, self.block_index(k, p));
@@ -321,10 +371,15 @@ impl BuddyAlloc {
321371
p as usize,
322372
"misalignment"
323373
);
324-
p
374+
375+
Ok(NonNull::slice_from_raw_parts(
376+
unsafe { NonNull::new_unchecked(p) },
377+
layout.size(),
378+
))
325379
}
326380

327-
pub fn free(&mut self, mut p: *mut u8) {
381+
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
382+
let mut p = ptr.as_ptr();
328383
let mut k = self.find_k_for_p(p);
329384
while k < (self.entries_size - 1) {
330385
let block_index = self.block_index(k, p);
@@ -355,45 +410,4 @@ impl BuddyAlloc {
355410
debug_assert!(!bit_isset(self.entry(k).alloc, self.block_index(k, p)));
356411
Node::push(self.entry(k).free, p);
357412
}
358-
359-
/// available bytes
360-
pub fn available_bytes(&self) -> usize {
361-
self.end_addr - self.unavailable - self.base_addr
362-
}
363-
364-
fn entry(&self, i: usize) -> &Entry {
365-
debug_assert!(i < self.entries_size, "index out of range");
366-
unsafe { self.entries.add(i).as_ref().expect("entry") }
367-
}
368-
369-
/// find k for p
370-
fn find_k_for_p(&self, p: *const u8) -> usize {
371-
for k in 0..(self.entries_size - 1) {
372-
if bit_isset(self.entry(k + 1).split, self.block_index(k + 1, p)) {
373-
debug_assert!(bit_isset(self.entry(k).alloc, self.block_index(k, p)));
374-
return k;
375-
}
376-
}
377-
0
378-
}
379-
380-
/// block index of p under k
381-
fn block_index(&self, k: usize, p: *const u8) -> usize {
382-
if (p as usize) < self.base_addr {
383-
// TODO handle this outside
384-
panic!("out of memory");
385-
}
386-
let n = p as usize - self.base_addr;
387-
// equal to: n / block_size_2base(k, self.leaf2base);
388-
let index = (n >> k) >> self.leaf2base;
389-
debug_assert!(index < nblock(k, self.entries_size));
390-
index
391-
}
392-
393-
/// block addr of index under k
394-
fn block_addr(&self, k: usize, i: usize) -> usize {
395-
// equal to: i * block_size_2base(k, self.leaf2base);
396-
let n = (i << k) << self.leaf2base;
397-
self.base_addr + n
398-
}
399413
}

src/freelist_alloc.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
//! Freelist allocator
22
//! Optimized for fixed small memory block.
33
4+
use core::{
5+
alloc::{AllocError, Allocator, Layout},
6+
cell::RefCell,
7+
ptr::NonNull,
8+
};
9+
410
/// Fixed size 64 Bytes, can't allocate more in one allocation.
511
pub const BLOCK_SIZE: usize = 64;
612

@@ -65,7 +71,7 @@ pub struct FreelistAlloc {
6571
base_addr: usize,
6672
/// memory end addr
6773
end_addr: usize,
68-
free: *mut Node,
74+
free: RefCell<*mut Node>,
6975
}
7076

7177
impl FreelistAlloc {
@@ -94,36 +100,47 @@ impl FreelistAlloc {
94100
FreelistAlloc {
95101
base_addr,
96102
end_addr,
97-
free,
103+
free: RefCell::new(free),
98104
}
99105
}
100106

101107
pub fn contains_ptr(&self, p: *mut u8) -> bool {
102108
let addr = p as usize;
103109
addr >= self.base_addr && addr < self.end_addr
104110
}
111+
}
105112

106-
pub fn malloc(&mut self, nbytes: usize) -> *mut u8 {
107-
if nbytes > BLOCK_SIZE || self.free.is_null() {
108-
return core::ptr::null_mut();
113+
unsafe impl Allocator for FreelistAlloc {
114+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
115+
let nbytes = layout.size();
116+
// TODO: alignment!
117+
if nbytes > BLOCK_SIZE || self.free.borrow().is_null() {
118+
return Err(AllocError);
109119
}
110120

111-
let is_last = Node::is_empty(self.free);
112-
let p = Node::pop(self.free) as *mut u8;
121+
let is_last = Node::is_empty(self.free.borrow().as_const());
122+
let p = Node::pop(*self.free.borrow_mut()) as *mut u8;
113123
if is_last {
114-
self.free = core::ptr::null_mut();
124+
self.free.replace(core::ptr::null_mut());
115125
}
116-
p
126+
Ok(NonNull::slice_from_raw_parts(
127+
unsafe { NonNull::new_unchecked(p) },
128+
layout.size(),
129+
))
117130
}
118131

119-
pub fn free(&mut self, p: *mut u8) {
132+
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
133+
let p = ptr.as_ptr();
120134
debug_assert!(self.contains_ptr(p));
121-
if self.free.is_null() {
135+
let f = self.free.borrow();
136+
if f.is_null() {
122137
let n = p.cast();
123138
Node::init(n);
124-
self.free = n;
139+
drop(f);
140+
self.free.replace(n);
125141
} else {
126-
Node::push(self.free, p);
142+
drop(f);
143+
Node::push(*self.free.borrow_mut(), p);
127144
}
128145
}
129146
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#![cfg_attr(not(test), no_std)]
2+
#![feature(allocator_api)]
3+
#![feature(nonnull_slice_from_raw_parts)]
4+
#![feature(ptr_const_cast)]
5+
#![feature(slice_ptr_get)]
26

37
pub mod buddy_alloc;
48
pub mod freelist_alloc;

src/non_threadsafe_alloc.rs

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
//! NonThreadSafeAlloc
22
//! An allocator that does not support thread-safe
33
4-
use crate::buddy_alloc::{BuddyAlloc, BuddyAllocParam};
5-
use crate::freelist_alloc::{FreelistAlloc, FreelistAllocParam, BLOCK_SIZE};
6-
use core::alloc::{GlobalAlloc, Layout};
7-
use core::cell::RefCell;
4+
use {
5+
crate::{
6+
buddy_alloc::{BuddyAlloc, BuddyAllocParam},
7+
freelist_alloc::{FreelistAlloc, FreelistAllocParam, BLOCK_SIZE},
8+
},
9+
core::{
10+
alloc::{AllocError, Allocator, GlobalAlloc, Layout},
11+
cell::RefCell,
12+
ptr::NonNull,
13+
},
14+
};
815

916
/// Use buddy allocator if request bytes is large than this,
1017
/// otherwise use freelist allocator
@@ -50,34 +57,49 @@ impl NonThreadsafeAlloc {
5057
}
5158
}
5259

53-
unsafe impl GlobalAlloc for NonThreadsafeAlloc {
54-
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
55-
let bytes = layout.size();
60+
// ==== Allocator api ====
61+
unsafe impl Allocator for NonThreadsafeAlloc {
62+
/// Allocate a memory block from the pool.
63+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
5664
// use BuddyAlloc if size is larger than MAX_FREELIST_ALLOC_SIZE
57-
if bytes > MAX_FREELIST_ALLOC_SIZE {
58-
self.fetch_buddy_alloc(|alloc| alloc.malloc(bytes))
65+
if layout.size() > MAX_FREELIST_ALLOC_SIZE {
66+
unsafe { self.fetch_buddy_alloc(|alloc| alloc.allocate(layout)) }
5967
} else {
6068
// try freelist alloc, fallback to BuddyAlloc if failed
61-
let mut p = self.fetch_freelist_alloc(|alloc| alloc.malloc(bytes));
62-
if p.is_null() {
63-
p = self.fetch_buddy_alloc(|alloc| alloc.malloc(bytes));
69+
unsafe {
70+
self.fetch_freelist_alloc(|alloc| alloc.allocate(layout))
71+
.or_else(|_| self.fetch_buddy_alloc(|alloc| alloc.allocate(layout)))
6472
}
65-
p
6673
}
6774
}
68-
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
75+
76+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
6977
let freed = self.fetch_freelist_alloc(|alloc| {
70-
if alloc.contains_ptr(ptr) {
71-
alloc.free(ptr);
78+
if alloc.contains_ptr(ptr.as_ptr()) {
79+
alloc.deallocate(ptr, layout);
7280
true
7381
} else {
7482
false
7583
}
7684
});
7785
if !freed {
78-
self.fetch_buddy_alloc(|alloc| alloc.free(ptr));
86+
self.fetch_buddy_alloc(|alloc| alloc.deallocate(ptr, layout));
7987
}
8088
}
8189
}
8290

91+
// ==== GlobalAlloc api ====
8392
unsafe impl Sync for NonThreadsafeAlloc {}
93+
94+
unsafe impl GlobalAlloc for NonThreadsafeAlloc {
95+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
96+
self.allocate(layout)
97+
.map_or(core::ptr::null_mut(), |p| p.as_mut_ptr())
98+
}
99+
100+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
101+
if !ptr.is_null() {
102+
self.deallocate(NonNull::new_unchecked(ptr), layout)
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)