Skip to content

Commit a62f24c

Browse files
authored
Malloc implementation using the global allocator (#19)
* add malloc, realloc, calloc and free support
1 parent 0fe817f commit a62f24c

File tree

6 files changed

+178
-4
lines changed

6 files changed

+178
-4
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
**/*.rs.bk
3-
Cargo.lock
3+
Cargo.lock
4+
/.vscode

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ repository = "https://github.com/thejpster/tinyrlibc"
1010

1111
[dependencies]
1212

13+
[dev-dependencies]
14+
static-alloc = "0.2.4"
15+
1316
[build-dependencies]
1417
cc = "1.0"
1518

@@ -38,6 +41,7 @@ all = [
3841
"isdigit",
3942
"isalpha",
4043
"isupper",
44+
"alloc",
4145
]
4246
abs = []
4347
strcmp = []
@@ -61,3 +65,4 @@ isspace = []
6165
isdigit = []
6266
isalpha = []
6367
isupper = []
68+
alloc = []

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP
3030
* strchr
3131
* snprintf
3232
* vsnprintf
33+
* alloc (optional)
34+
* malloc
35+
* calloc
36+
* realloc
37+
* free
3338

3439
## Non-standard helper functions
3540

src/ctype.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
/// `void`
1111
pub type CVoid = ::core::ffi::c_void;
1212

13+
/// `size_t`
14+
pub type CSizeT = usize;
15+
1316
/// `long long int`
1417
pub type CLongLong = ::core::ffi::c_longlong;
1518

src/lib.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,27 @@
99
1010
#![cfg_attr(not(test), no_std)]
1111
#![allow(clippy::missing_safety_doc)]
12+
#![allow(unused_imports)]
1213

13-
#[cfg(test)]
14-
#[allow(unused_imports)]
15-
use std as core;
14+
#[cfg(feature = "alloc")]
15+
mod malloc;
16+
#[cfg(feature = "alloc")]
17+
pub use self::malloc::{calloc, free, malloc, realloc};
18+
19+
// A new global allocator is required for the tests, but not for the library itself.
20+
// This is because the default alloc crate uses the system allocator, collides with
21+
// the one in this crate, and causes a link error.
22+
#[cfg(all(feature = "alloc", test))]
23+
use static_alloc::Bump;
24+
#[cfg(all(feature = "alloc", test))]
25+
#[global_allocator]
26+
static ALLOCATOR: Bump<[u8; 1024 * 1024]> = Bump::uninit();
1627

1728
mod itoa;
29+
#[cfg(feature = "itoa")]
30+
pub use self::itoa::itoa;
31+
#[cfg(feature = "utoa")]
32+
pub use self::itoa::utoa;
1833

1934
mod abs;
2035
#[cfg(feature = "abs")]

src/malloc.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//! Rust implementation of C library function `malloc`, `calloc`, `realloc`, and `free`.
2+
//!
3+
//! Copyright (c) Gyungmin Myung <[email protected]>
4+
//! This file is licensed under the Blue Oak Model Licence 1.0.0
5+
6+
extern crate alloc;
7+
use crate::CSizeT;
8+
9+
// The maximum alignment of any fundamental type. Equivalent to max_align_t
10+
const MAX_ALIGN: usize = 16;
11+
12+
/// Rust implementation of C library function `malloc`
13+
///
14+
/// See [malloc](https://linux.die.net/man/3/malloc) for alignment details.
15+
#[no_mangle]
16+
pub unsafe extern "C" fn malloc(size: CSizeT) -> *mut u8 {
17+
// size + MAX_ALIGN for to store the size of the allocated memory.
18+
let layout = alloc::alloc::Layout::from_size_align(size + MAX_ALIGN, MAX_ALIGN).unwrap();
19+
let ptr = unsafe { alloc::alloc::alloc(layout) };
20+
if ptr.is_null() {
21+
return ptr;
22+
}
23+
unsafe {
24+
*(ptr as *mut CSizeT) = size;
25+
}
26+
unsafe { ptr.add(MAX_ALIGN) }
27+
}
28+
29+
/// Rust implementation of C library function `calloc`
30+
///
31+
/// See [calloc](https://linux.die.net/man/3/calloc) for alignment details.
32+
#[no_mangle]
33+
pub unsafe extern "C" fn calloc(nmemb: CSizeT, size: CSizeT) -> *mut u8 {
34+
let total_size = nmemb * size;
35+
let layout = alloc::alloc::Layout::from_size_align(total_size + MAX_ALIGN, MAX_ALIGN).unwrap();
36+
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
37+
if ptr.is_null() {
38+
return ptr;
39+
}
40+
unsafe {
41+
*(ptr as *mut CSizeT) = total_size;
42+
}
43+
unsafe { ptr.add(MAX_ALIGN) }
44+
}
45+
46+
/// Rust implementation of C library function `realloc`
47+
///
48+
/// See [realloc](https://linux.die.net/man/3/realloc) for alignment details.
49+
#[no_mangle]
50+
pub unsafe extern "C" fn realloc(ptr: *mut u8, size: CSizeT) -> *mut u8 {
51+
if ptr.is_null() {
52+
return malloc(size);
53+
}
54+
let old_size = unsafe { *(ptr.sub(MAX_ALIGN) as *mut CSizeT) };
55+
let layout = alloc::alloc::Layout::from_size_align(old_size + MAX_ALIGN, MAX_ALIGN).unwrap();
56+
let new_ptr = unsafe { alloc::alloc::realloc(ptr.sub(MAX_ALIGN), layout, size + MAX_ALIGN) };
57+
if new_ptr.is_null() {
58+
return new_ptr;
59+
}
60+
unsafe {
61+
*(new_ptr as *mut CSizeT) = size;
62+
}
63+
unsafe { new_ptr.add(MAX_ALIGN) }
64+
}
65+
66+
/// Rust implementation of C library function `free`
67+
#[no_mangle]
68+
pub unsafe extern "C" fn free(ptr: *mut u8) {
69+
if ptr.is_null() {
70+
return;
71+
}
72+
let old_size = unsafe { *(ptr.sub(MAX_ALIGN) as *mut CSizeT) };
73+
let layout = alloc::alloc::Layout::from_size_align(old_size + MAX_ALIGN, MAX_ALIGN).unwrap();
74+
unsafe { alloc::alloc::dealloc(ptr.sub(MAX_ALIGN), layout) };
75+
}
76+
77+
#[cfg(test)]
78+
mod test {
79+
use super::*;
80+
81+
#[test]
82+
fn test_malloc() {
83+
let ptr = unsafe { malloc(10) };
84+
assert!(!ptr.is_null());
85+
unsafe {
86+
assert_eq!(*(ptr.sub(MAX_ALIGN) as *mut CSizeT), 10);
87+
(0..10).for_each(|i| {
88+
*ptr.add(i) = i as u8;
89+
});
90+
(0..10).for_each(|i| {
91+
assert_eq!(*ptr.add(i), i as u8);
92+
});
93+
}
94+
unsafe { free(ptr) };
95+
}
96+
97+
#[test]
98+
fn test_calloc() {
99+
let ptr = unsafe { calloc(10, 10) };
100+
assert!(!ptr.is_null());
101+
unsafe {
102+
assert_eq!(*(ptr.sub(MAX_ALIGN) as *mut CSizeT), 100);
103+
(0..100).for_each(|i| {
104+
assert_eq!(*ptr.add(i), 0);
105+
});
106+
(0..100).for_each(|i| {
107+
*ptr.add(i) = i as u8;
108+
});
109+
(0..100).for_each(|i| {
110+
assert_eq!(*ptr.add(i), i as u8);
111+
});
112+
}
113+
unsafe { free(ptr) };
114+
}
115+
116+
#[test]
117+
fn test_realloc() {
118+
let ptr = unsafe { malloc(10) };
119+
assert!(!ptr.is_null());
120+
unsafe {
121+
assert_eq!(*(ptr.sub(MAX_ALIGN) as *mut CSizeT), 10);
122+
(0..10).for_each(|i| {
123+
*ptr.add(i) = i as u8;
124+
});
125+
(0..10).for_each(|i| {
126+
assert_eq!(*ptr.add(i), i as u8);
127+
});
128+
}
129+
let ptr = unsafe { realloc(ptr, 20) };
130+
assert!(!ptr.is_null());
131+
unsafe {
132+
assert_eq!(*(ptr.sub(MAX_ALIGN) as *mut CSizeT), 20);
133+
(0..10).for_each(|i| {
134+
assert_eq!(*ptr.add(i), i as u8);
135+
});
136+
(10..20).for_each(|i| {
137+
*ptr.add(i) = i as u8;
138+
});
139+
(10..20).for_each(|i| {
140+
assert_eq!(*ptr.add(i), i as u8);
141+
});
142+
}
143+
unsafe { free(ptr) };
144+
}
145+
}

0 commit comments

Comments
 (0)