Skip to content

Commit dfe8f23

Browse files
authored
Merge pull request #3 from ratel-rust/0.4.1
0.4.1
2 parents 95a5fd7 + 21e6645 commit dfe8f23

File tree

4 files changed

+110
-42
lines changed

4 files changed

+110
-42
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "toolshed"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
authors = ["maciejhirsz <[email protected]>"]
55
license = "MIT/Apache-2.0"
66
description = "Arena allocator and a handful of useful data structures"

src/arena.rs

Lines changed: 101 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Module containing the `Arena` and `Uninitialized` structs. For convenience the
2+
//! `Arena` is exported at the root of the crate.
3+
14
use std::mem::size_of;
25
use std::cell::Cell;
36

@@ -16,8 +19,57 @@ pub struct Arena {
1619
offset: Cell<usize>
1720
}
1821

22+
/// A pointer to an uninitialized region of memory.
23+
pub struct Uninitialized<'arena, T: 'arena> {
24+
pointer: &'arena mut T,
25+
}
26+
27+
impl<'arena, T: 'arena> Uninitialized<'arena, T> {
28+
/// Initialize the memory at the pointer with a given value.
29+
#[inline]
30+
pub fn init(self, value: T) -> &'arena T {
31+
*self.pointer = value;
32+
33+
&*self.pointer
34+
}
35+
36+
/// Get a reference to the pointer without writing to it.
37+
///
38+
/// **Reading from this reference without calling `init` is undefined behavior.**
39+
#[inline]
40+
pub unsafe fn as_ref(&self) -> &'arena T {
41+
&*(self.pointer as *const T)
42+
}
43+
44+
/// Convert the `Uninitialized` to a regular mutable reference.
45+
///
46+
/// **Reading from this reference without calling `init` is undefined behavior.**
47+
#[inline]
48+
pub unsafe fn into_mut(self) -> &'arena mut T {
49+
self.pointer
50+
}
51+
52+
/// Convert a raw pointer to an `Uninitialized`. This method is unsafe since it can
53+
/// bind to arbitrary lifetimes.
54+
#[inline]
55+
pub unsafe fn from_raw(pointer: *mut T) -> Self {
56+
Uninitialized {
57+
pointer: &mut *pointer,
58+
}
59+
}
60+
}
61+
62+
impl<'arena, T: 'arena> From<&'arena mut T> for Uninitialized<'arena, T> {
63+
#[inline]
64+
fn from(pointer: &'arena mut T) -> Self {
65+
Uninitialized {
66+
pointer
67+
}
68+
}
69+
}
70+
1971
impl Arena {
20-
/// Create a new arena with a single preallocated 64KiB page
72+
/// Create a new arena with a single preallocated 64KiB page.
2173
pub fn new() -> Self {
2274
let mut store = vec![Vec::with_capacity(ARENA_BLOCK)];
2375
let ptr = store[0].as_mut_ptr();
@@ -31,55 +83,53 @@ impl Arena {
3183

3284
/// Put the value onto the page of the arena and return a reference to it.
3385
#[inline]
34-
pub fn alloc<'a, T: Sized + Copy>(&'a self, val: T) -> &'a T {
35-
unsafe {
36-
let ptr = self.alloc_uninitialized();
37-
*ptr = val;
38-
&*ptr
39-
}
86+
pub fn alloc<'arena, T: Sized + Copy>(&'arena self, value: T) -> &'arena T {
87+
self.alloc_uninitialized().init(value)
4088
}
4189

42-
/// Allocate enough bytes for the type `T`, then return a pointer to the memory.
43-
/// Memory behind the pointer is uninitialized, can contain garbage and reading
44-
/// from it is undefined behavior.
90+
/// Allocate enough bytes for the type `T`, then return an `Uninitialized` pointer to the memory.
4591
#[inline]
46-
pub unsafe fn alloc_uninitialized<'a, T: Sized + Copy>(&'a self) -> &'a mut T {
47-
&mut *(self.require(size_of::<T>()) as *mut T)
92+
pub fn alloc_uninitialized<'arena, T: Sized + Copy>(&'arena self) -> Uninitialized<'arena, T> {
93+
Uninitialized {
94+
pointer: unsafe { &mut *(self.require(size_of::<T>()) as *mut T) }
95+
}
4896
}
4997

50-
/// Allocate an `&str` slice onto the arena and return a reference to it. This is
51-
/// useful when the original slice has an undefined lifetime.
98+
/// Allocate a slice of `T` slice onto the arena and return a reference to it.
99+
/// This is useful when the original slice has an undefined lifetime.
52100
///
53-
/// Note: static slices (`&'static str`) can be safely used in place of arena-bound
101+
/// Note: static slices (`&'static [T]`) can be safely used in place of arena-bound
54102
/// slices without having to go through this method.
55-
pub fn alloc_str<'a>(&'a self, val: &str) -> &'a str {
56-
let offset = self.offset.get();
57-
let alignment = size_of::<usize>() - (val.len() % size_of::<usize>());
58-
let cap = offset + val.len() + alignment;
59-
60-
if cap > ARENA_BLOCK {
61-
return self.alloc_string(val.into());
62-
}
63-
64-
self.offset.set(cap);
103+
pub fn alloc_slice<'arena, T: Copy>(&'arena self, val: &[T]) -> &'arena [T] {
104+
let ptr = self.require(val.len() * size_of::<T>()) as *mut T;
65105

66106
unsafe {
67107
use std::ptr::copy_nonoverlapping;
68-
use std::str::from_utf8_unchecked;
69108
use std::slice::from_raw_parts;
70109

71-
let ptr = self.ptr.get().offset(offset as isize);
72110
copy_nonoverlapping(val.as_ptr(), ptr, val.len());
111+
from_raw_parts(ptr, val.len())
112+
}
113+
}
114+
115+
/// Allocate an `&str` slice onto the arena and return a reference to it. This is
116+
/// useful when the original slice has an undefined lifetime.
117+
///
118+
/// Note: static slices (`&'static str`) can be safely used in place of arena-bound
119+
/// slices without having to go through this method.
120+
pub fn alloc_str<'arena>(&'arena self, val: &str) -> &'arena str {
121+
unsafe {
122+
use std::str::from_utf8_unchecked;
73123

74-
from_utf8_unchecked(from_raw_parts(ptr, val.len()))
124+
from_utf8_unchecked(self.alloc_slice(val.as_bytes()))
75125
}
76126
}
77127

78128
/// Allocate an `&str` slice onto the arena as null terminated C-style string.
79129
/// No checks are performed on the source and whether or not it already contains
80130
/// any nul bytes. While this does not create any memory issues, it assumes that
81131
/// the reader of the source can deal with malformed source.
82-
pub fn alloc_str_with_nul<'a>(&'a self, val: &str) -> *const u8 {
132+
pub fn alloc_str_with_nul<'arena>(&'arena self, val: &str) -> *const u8 {
83133
let len_with_zero = val.len() + 1;
84134
let ptr = self.require(len_with_zero);
85135

@@ -94,7 +144,7 @@ impl Arena {
94144

95145
/// Pushes the `String` as it's own page onto the arena and returns a reference to it.
96146
/// This does not copy or reallocate the original `String`.
97-
pub fn alloc_string<'a>(&'a self, val: String) -> &'a str {
147+
pub fn alloc_string<'arena>(&'arena self, val: String) -> &'arena str {
98148
let len = val.len();
99149
let ptr = self.alloc_vec(val.into_bytes());
100150

@@ -128,10 +178,9 @@ impl Arena {
128178
return self.alloc_bytes(size);
129179
}
130180

131-
// This should also be optimized away.
132181
let size = match size % size_of::<usize>() {
133182
0 => size,
134-
n => size + n
183+
n => size + (size_of::<usize>() - n),
135184
};
136185

137186
let offset = self.offset.get();
@@ -209,7 +258,7 @@ mod test {
209258
assert_eq!(arena.alloc(0u64), &0);
210259
assert_eq!(arena.alloc(42u64), &42);
211260

212-
unsafe { arena.alloc_uninitialized::<[usize; 1024 * 1024]>() };
261+
arena.alloc_uninitialized::<[usize; 1024 * 1024]>();
213262

214263
// Still writes to the first page
215264
assert_eq!(arena.offset.get(), 8 * 2);
@@ -226,6 +275,25 @@ mod test {
226275
assert_eq!(arena.store.get_mut()[1].capacity(), size_of::<usize>() * 1024 * 1024);
227276
}
228277

278+
#[test]
279+
fn alloc_slice() {
280+
let arena = Arena::new();
281+
282+
assert_eq!(arena.alloc_slice(&[10u16, 20u16]), &[10u16, 20u16][..]);
283+
assert_eq!(arena.offset.get(), 8);
284+
}
285+
286+
#[test]
287+
fn aligns_slice_allocs() {
288+
let arena = Arena::new();
289+
290+
assert_eq!(arena.alloc_slice(b"foo"), b"foo");
291+
assert_eq!(arena.offset.get(), 8);
292+
293+
assert_eq!(arena.alloc_slice(b"doge to the moon!"), b"doge to the moon!");
294+
assert_eq!(arena.offset.get(), 32);
295+
}
296+
229297
#[test]
230298
fn aligns_str_allocs() {
231299
let arena = Arena::new();

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub mod cell;
9696
pub mod map;
9797
pub mod set;
9898
pub mod list;
99-
mod arena;
99+
pub mod arena;
100100
mod bloom;
101101
mod impl_partial_eq;
102102
mod impl_debug;

src/list.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! A linked list and auxiliary types that can be used with the `Arena`.
22
3-
use Arena;
3+
use arena::Arena;
44
use cell::CopyCell;
55

66
#[derive(Debug, PartialEq, Clone)]
@@ -190,8 +190,8 @@ pub struct GrowableList<'arena, T>
190190
where
191191
T: 'arena,
192192
{
193-
first: CopyCell<Option<&'arena ListNode<'arena, T>>>,
194193
last: CopyCell<Option<&'arena ListNode<'arena, T>>>,
194+
first: CopyCell<Option<&'arena ListNode<'arena, T>>>,
195195
}
196196

197197
impl<'arena, T> GrowableList<'arena, T>
@@ -247,7 +247,7 @@ pub struct ListBuilder<'arena, T: 'arena>
247247
where
248248
T: 'arena,
249249
{
250-
first: CopyCell<&'arena ListNode<'arena, T>>,
250+
first: &'arena ListNode<'arena, T>,
251251
last: CopyCell<&'arena ListNode<'arena, T>>,
252252
}
253253

@@ -258,14 +258,14 @@ where
258258
/// Create a new builder with the first element.
259259
#[inline]
260260
pub fn new(arena: &'arena Arena, first: T) -> Self {
261-
let first = CopyCell::new(arena.alloc(ListNode {
261+
let first = arena.alloc(ListNode {
262262
value: first,
263263
next: CopyCell::new(None)
264-
}));
264+
});
265265

266266
ListBuilder {
267267
first,
268-
last: first,
268+
last: CopyCell::new(first),
269269
}
270270
}
271271

@@ -290,7 +290,7 @@ where
290290
#[inline]
291291
pub fn as_list(&self) -> List<'arena, T> {
292292
List {
293-
root: CopyCell::new(Some(self.first.get()))
293+
root: CopyCell::new(Some(self.first))
294294
}
295295
}
296296
}

0 commit comments

Comments
 (0)