Skip to content

Commit f2c9a5e

Browse files
committed
alloc_slice
1 parent 95a5fd7 commit f2c9a5e

File tree

3 files changed

+106
-41
lines changed

3 files changed

+106
-41
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: 98 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,57 @@ pub struct Arena {
1616
offset: Cell<usize>
1717
}
1818

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

3281
/// Put the value onto the page of the arena and return a reference to it.
3382
#[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-
}
83+
pub fn alloc<'arena, T: Sized + Copy>(&'arena self, value: T) -> &'arena T {
84+
self.alloc_uninitialized().init(value)
4085
}
4186

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.
87+
/// Allocate enough bytes for the type `T`, then return an `Uninitialized` pointer to the memory.
4588
#[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)
89+
pub fn alloc_uninitialized<'arena, T: Sized + Copy>(&'arena self) -> Uninitialized<'arena, T> {
90+
Uninitialized {
91+
pointer: unsafe { &mut *(self.require(size_of::<T>()) as *mut T) }
92+
}
4893
}
4994

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.
95+
/// Allocate a slice of `T` slice onto the arena and return a reference to it.
96+
/// This is useful when the original slice has an undefined lifetime.
5297
///
53-
/// Note: static slices (`&'static str`) can be safely used in place of arena-bound
98+
/// Note: static slices (`&'static [T]`) can be safely used in place of arena-bound
5499
/// 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);
100+
pub fn alloc_slice<'arena, T: Copy>(&'arena self, val: &[T]) -> &'arena [T] {
101+
let ptr = self.require(val.len() * size_of::<T>()) as *mut T;
65102

66103
unsafe {
67104
use std::ptr::copy_nonoverlapping;
68-
use std::str::from_utf8_unchecked;
69105
use std::slice::from_raw_parts;
70106

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

74-
from_utf8_unchecked(from_raw_parts(ptr, val.len()))
121+
from_utf8_unchecked(self.alloc_slice(val.as_bytes()))
75122
}
76123
}
77124

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

@@ -94,7 +141,7 @@ impl Arena {
94141

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

@@ -128,10 +175,9 @@ impl Arena {
128175
return self.alloc_bytes(size);
129176
}
130177

131-
// This should also be optimized away.
132178
let size = match size % size_of::<usize>() {
133179
0 => size,
134-
n => size + n
180+
n => size + (size_of::<usize>() - n),
135181
};
136182

137183
let offset = self.offset.get();
@@ -209,7 +255,7 @@ mod test {
209255
assert_eq!(arena.alloc(0u64), &0);
210256
assert_eq!(arena.alloc(42u64), &42);
211257

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

214260
// Still writes to the first page
215261
assert_eq!(arena.offset.get(), 8 * 2);
@@ -226,6 +272,25 @@ mod test {
226272
assert_eq!(arena.store.get_mut()[1].capacity(), size_of::<usize>() * 1024 * 1024);
227273
}
228274

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

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)