obstack/lib.rs
1//! A fast segmented stack allocator, supporting multiple objects of any type.
2//!
3//! A type of arena allocator, obstacks deallocate memory all at once, when the `Obstack` itself is
4//! destroyed. The benefit is extremely fast allocation: just a pointer bump. Unlike a typed arena,
5//! a single `Obstack` can contain values with any number of different types.
6//!
7//! For `Copy` types pushing a value to an `Obstack` returns a standard mutable reference:
8//!
9//! # use obstack::Obstack;
10//! # let stack = Obstack::new();
11//! let r: &mut u8 = stack.push_copy(42);
12//! assert_eq!(*r, 42);
13//!
14//! As `Copy` types can't implement `Drop`, nothing needs to be done to deallocate the value beyond
15//! deallocating the memory itself, which is done en-mass when the `Obstack` itself is dropped.
16//! `push_copy()` is thus limited to types that implement `Copy`.
17//!
18//! Types that do not implement `Copy` *may* implement `Drop`. As Rust's type system doesn't have a
19//! negative `!Drop` trait, `Obstack` has a second method - `push()` - that is not restricted to
20//! `Copy` types. This method returns the wrapper type `Ref<T>`, that wraps the underlying mutable
21//! reference. This wrapper owns the value on the stack, and ensures that `drop` is
22//! called when the wrapper goes out of scope. Essentially `Ref` is the equivalent of `Box`, but
23//! using an `Obstack` rather than the heap.
24//!
25//! In practice even when the `Ref` wrapper is used, if the underlying type doesn't actually
26//! implement a meaningful `drop` method, the Rust compiler is able to optimize away all calls to
27//! `drop`, resulting in the same performance as for `Copy` types; the actual `Ref::drop()` method
28//! is `[inline(always)]` to aid this process. This is important as not all non-drop types can
29//! implement `Copy` - notably mutable references can't.
30//!
31//! `Obstack` allocates memory as a segmented stack consisting of one or more segments of
32//! contiguous memory. Each time the top segment becomes full, a new segment is allocated from the
33//! heap. To ensure the total number of allocations remains small, segments are allocated in a
34//! powers-of-two fashion, with each segment being twice the size of the previous one.
35//!
36//! Once a segment has been allocated it's stable for the life of the `Obstack`, allowing
37//! the values contained in that segment to be referenced directly; Rust lifetimes ensure that the
38//! references are valid for the lifetime of the `Obstack`.
39
40#[cfg(test)]
41extern crate rand;
42
43use std::cell::UnsafeCell;
44use std::cmp;
45use std::fmt;
46use std::mem;
47use std::ptr;
48use std::slice;
49
50mod alignedvec;
51use alignedvec::AlignedVec;
52
53pub const DEFAULT_INITIAL_CAPACITY: usize = 256;
54
55/// An obstack
56#[derive(Debug)]
57pub struct Obstack {
58 state: UnsafeCell<State<usize>>,
59}
60
61impl Obstack {
62 /// Constructs a new `Obstack` with the specified initial capacity.
63 ///
64 /// The obstack will be able to allocate at least `initial_capacity` bytes before having to
65 /// allocate again.
66 pub fn with_initial_capacity(initial_capacity: usize) -> Self {
67 let n = initial_capacity;
68 let n = if n.is_power_of_two() { n } else { n.next_power_of_two() };
69
70 let state = State::new(n);
71 Obstack {
72 state: UnsafeCell::new(state)
73 }
74 }
75
76 /// Constructs a new `Obstack`.
77 ///
78 /// The initial capacity will be set to `DEFAULT_INITIAL_CAPACITY`.
79 pub fn new() -> Self {
80 Self::with_initial_capacity(DEFAULT_INITIAL_CAPACITY-1)
81 }
82
83 /// Pushes a value to the `Obstack`.
84 ///
85 /// Returns a `Ref` that can be dereferenced to the value's location on the stack.
86 ///
87 /// # use std::convert::From;
88 /// # use obstack::{Obstack, Ref};
89 /// # let stack = Obstack::new();
90 /// let r: Ref<String> = stack.push(String::from("Hello World!"));
91 /// assert_eq!(*r, "Hello World!");
92 ///
93 #[inline]
94 pub fn push<'a, T>(&'a self, value: T) -> Ref<'a, T> {
95 let ptr = self.alloc(&value) as *mut T;
96 unsafe {
97 ptr::write(ptr, value);
98
99 Ref {
100 ptr: &mut *ptr,
101 }
102 }
103 }
104
105 /// Pushes a `Copy` value to the `Obstack`.
106 ///
107 /// Returns a mutable reference to the value on the stack.
108 ///
109 /// # use obstack::Obstack;
110 /// # let stack = Obstack::new();
111 /// let r: &mut [u8; 5] = stack.push_copy([1,2,3,4,5]);
112 /// assert_eq!(*r, [1,2,3,4,5]);
113 ///
114 #[inline]
115 pub fn push_copy<'a, T>(&'a self, value: T) -> &'a mut T
116 where T: Copy,
117 {
118 unsafe {
119 let r = &mut *(self.alloc(&value) as *mut T);
120 *r = value;
121 r
122 }
123 }
124
125 /// Copies values from a slice to the `Obstack`.
126 ///
127 /// Returns a mutable reference to a newly allocated slice:
128 ///
129 /// ```
130 /// # use obstack::Obstack;
131 /// # let stack = Obstack::new();
132 /// let v: Box<[u8]> = Box::new([1,2,3,4,5]);
133 /// let r: &mut [u8] = stack.copy_from_slice(&v[0..3]);
134 ///
135 /// assert_eq!(r.len(), 3);
136 /// assert_eq!(r, &[1,2,3][..]);
137 /// ```
138 ///
139 /// Zero-length slices work as expected without allocations:
140 ///
141 /// ```
142 /// # use obstack::Obstack;
143 /// # let stack = Obstack::new();
144 /// let prev_used = stack.bytes_used();
145 /// let r: &mut [u8] = stack.copy_from_slice(&[]);
146 ///
147 /// assert_eq!(prev_used, stack.bytes_used());
148 /// assert_eq!(r.len(), 0);
149 /// assert_eq!(r, &[]);
150 /// ```
151 ///
152 /// As do slices of zero-sized types:
153 ///
154 /// ```
155 /// # use std::usize;
156 /// # use obstack::Obstack;
157 /// # let stack = Obstack::new();
158 /// let v: Box<[()]> = Box::new([(); 1_000]);
159 /// let prev_used = stack.bytes_used();
160 /// let r: &mut [()] = stack.copy_from_slice(&v);
161 ///
162 /// assert_eq!(prev_used, stack.bytes_used());
163 /// assert_eq!(r.len(), 1_000);
164 /// assert!(r == &[(); 1_000][..]);
165 /// ```
166 #[inline]
167 pub fn copy_from_slice<'a, T>(&'a self, src: &[T]) -> &'a mut [T]
168 where T: Copy,
169 {
170 unsafe {
171 let ptr = self.alloc(src) as *mut T;
172 let r = slice::from_raw_parts_mut(ptr, src.len());
173 r.copy_from_slice(src);
174 r
175 }
176 }
177
178 /// Returns the total bytes currently used by values.
179 ///
180 /// Includes bytes used for alignment padding. However, this figure is *not* the total size
181 /// *allocated* by the `Obstack`, which would include bytes allocated for parts of segments
182 /// that haven't been used yet. Thus the return value of this method will change after every
183 /// non-zero-sized value allocated.
184 ///
185 /// `bytes_used` always starts at zero:
186 ///
187 /// ```
188 /// # use obstack::Obstack;
189 /// let stack = Obstack::new();
190 /// assert_eq!(stack.bytes_used(), 0);
191 /// ```
192 pub fn bytes_used(&self) -> usize {
193 unsafe {
194 let state = &*self.state.get();
195
196 state.tip.len_bytes()
197 + state.used_slabs.iter()
198 .map(|used_slab| used_slab.len_bytes())
199 .sum::<usize>()
200 }
201 }
202
203 /// Alocates memory for a value, without initializing it.
204 #[inline]
205 fn alloc<'a, T: ?Sized>(&'a self, value_ref: &T) -> *mut u8 {
206 let size = mem::size_of_val(value_ref);
207 let alignment = mem::align_of_val(value_ref);
208
209 if size > 0 {
210 unsafe {
211 let state = &mut *self.state.get();
212 state.alloc(size, alignment) as *mut u8
213 }
214 } else {
215 mem::align_of_val(value_ref) as *mut u8
216 }
217 }
218}
219
220/// A wrapper referencing a value in an `Obstack`.
221///
222/// A `Ref` value owns the value it references, and will invoke `drop` on the value when the `Ref`
223/// goes out of scope. Effectively a `Ref` is a `Box` that uses an `Obstack` rather than the heap.
224///
225/// The inherent methods of `Ref` are all associated functions, which means you have to call them
226/// as e.g. `Ref::unwrap(value)` instead of `value.unwrap()`. This avoids conflicts with methods of
227/// the inner type `T`.
228pub struct Ref<'a, T: 'a + ?Sized> {
229 ptr: &'a mut T,
230}
231
232impl<'a, T: ?Sized> Ref<'a, T> {
233 /// Returns the owned value, consuming the `Ref`.
234 ///
235 /// This allows the value to taken out of the `Obstack` and used even after it goes out of
236 /// scope:
237 ///
238 /// ```
239 /// # use obstack::{Obstack, Ref};
240 /// fn f() -> String {
241 /// let stack = Obstack::new();
242 /// let r = stack.push(String::from("foo"));
243 ///
244 /// Ref::unwrap(r)
245 /// }
246 ///
247 /// assert_eq!(f(), "foo");
248 /// ```
249 ///
250 /// Since obstacks only free memory when they go out of scope, the `bytes_used` remains
251 /// unchanged:
252 ///
253 /// ```
254 /// # use obstack::{Obstack, Ref};
255 /// # let stack = Obstack::new();
256 /// let r = stack.push(String::new());
257 ///
258 /// let used = stack.bytes_used();
259 /// let inner = Ref::unwrap(r);
260 ///
261 /// assert_eq!(used, stack.bytes_used());
262 /// # assert_eq!(inner, "");
263 /// ```
264 #[inline]
265 pub fn unwrap(this: Self) -> T
266 where T: Sized
267 {
268 unsafe {
269 let ptr: *const T = this.ptr;
270 mem::forget(this);
271 ptr::read(ptr)
272 }
273 }
274}
275
276impl<'a, T: ?Sized> Drop for Ref<'a, T> {
277 #[inline(always)]
278 fn drop(&mut self) {
279 unsafe {
280 ptr::drop_in_place(self.ptr);
281 }
282 }
283}
284
285
286#[derive(Debug)]
287struct State<A> {
288 tip: AlignedVec<A>,
289 used_slabs: Vec<AlignedVec<A>>,
290}
291
292impl<A> State<A> {
293 fn next_capacity(prev_capacity: usize, required: usize, alignment: usize) -> usize {
294 cmp::max(prev_capacity + 1, required + alignment)
295 .next_power_of_two()
296 }
297
298 fn new(min_initial_capacity: usize) -> State<A> {
299 let capacity = Self::next_capacity(0, min_initial_capacity, 0);
300 State {
301 tip: AlignedVec::with_capacity_bytes(capacity),
302 used_slabs: Vec::new(),
303 }
304 }
305
306 #[inline(never)]
307 unsafe fn alloc_from_new_slab(&mut self, size: usize, alignment: usize) -> *mut A {
308 let new_capacity = Self::next_capacity(self.tip.capacity_bytes(),
309 size, alignment);
310 let new_tip = AlignedVec::with_capacity_bytes(new_capacity);
311 let old_tip = mem::replace(&mut self.tip, new_tip);
312 self.used_slabs.push(old_tip);
313
314 self.alloc(size, alignment)
315 }
316
317 #[inline]
318 unsafe fn alloc(&mut self, size: usize, alignment: usize) -> *mut A {
319 let start_ptr = self.tip.as_mut_ptr()
320 .offset(self.tip.len_items() as isize);
321
322 let padding = (alignment - (start_ptr as usize % alignment)) % alignment;
323 let start_ptr = start_ptr.offset(AlignedVec::<A>::bytes_to_items(padding) as isize);
324 let new_used = self.tip.len_items() + AlignedVec::<A>::bytes_to_items(padding + size);
325
326 if new_used <= self.tip.capacity_items() {
327 self.tip.set_len_items(new_used);
328 start_ptr as *mut A
329 } else {
330 self.alloc_from_new_slab(size, alignment)
331 }
332 }
333}
334
335
336impl fmt::Display for Obstack {
337 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
338 unsafe {
339 let state = &*self.state.get();
340
341 write!(f, "Obstack Slabs:\n")?;
342
343 write!(f, " {:p}: size = {}, used = {}\n",
344 state.tip.as_ptr(),
345 state.tip.capacity_bytes(),
346 state.tip.len_bytes())?;
347
348 for slab in state.used_slabs.iter().rev() {
349 write!(f, " {:p}: size = {}, used = {}\n",
350 slab.as_ptr(), slab.capacity_bytes(), slab.len_bytes())?;
351 }
352 Ok(())
353 }
354 }
355}
356
357mod impls;
358pub use impls::*;
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 use std::cell::Cell;
365 use std::ops::{Deref, DerefMut};
366 use std::rc::Rc;
367 use std::thread;
368
369 use rand::{Rng, thread_rng};
370
371 #[test]
372 fn test_consistency_simple() {
373 let stack: Obstack = Obstack::new();
374
375 let mut v = Vec::new();
376 for i in 0 .. 10_000 {
377 let r: &mut usize = stack.push_copy(i);
378 assert_eq!(*r, i);
379 v.push((i, r));
380 }
381
382 // After filling the stack every value should have it's original value
383 for &(ref orig, ref stack_ref) in v.iter() {
384 assert_eq!(*orig, **stack_ref);
385 }
386
387 // Change every value and make sure what changed was what we expected
388 for &mut(_, ref mut stack_ref) in v.iter_mut() {
389 **stack_ref = **stack_ref + 42;
390 }
391 for &(ref orig, ref stack_ref) in v.iter() {
392 assert_eq!(*orig + 42, **stack_ref);
393 }
394 }
395
396 #[test]
397 fn test_consistency_multiple_types() {
398 let stack: Obstack = Obstack::new();
399
400 #[derive(Debug)]
401 enum Multi<'a> {
402 Bool((bool, &'a mut bool)),
403 U32((u32, &'a mut u32)),
404 U64((u64, &'a mut u64)),
405 ArrayU8(([u8;5], &'a mut [u8;5])),
406 Unit(&'a mut ()),
407 UnitRef(Ref<'a, ()>),
408 RcBool((Rc<bool>, Ref<'a, Rc<bool>>)),
409 BoxU64((u64, Ref<'a, Box<u64>>)),
410 }
411
412 fn fill<'a>(stack: &'a Obstack) -> Multi<'a> {
413 match thread_rng().next_u32() % 8 {
414 0 => {
415 let value = thread_rng().gen();
416 Multi::Bool((value, stack.push_copy(value)))
417 },
418 1 => {
419 let value = thread_rng().gen();
420 Multi::U32((value, stack.push_copy(value)))
421 },
422 2 => {
423 let value = thread_rng().gen();
424 Multi::U64((value, stack.push_copy(value)))
425 },
426 3 => {
427 let value = thread_rng().gen();
428 Multi::ArrayU8((value, stack.push_copy(value)))
429 },
430 4 => {
431 Multi::Unit(stack.push_copy(()))
432 },
433 5 => {
434 Multi::UnitRef(stack.push(()))
435 },
436 6 => {
437 let rc_bool = Rc::new(thread_rng().gen::<bool>());
438 Multi::RcBool((rc_bool.clone(), stack.push(rc_bool)))
439 },
440 7 => {
441 let i = thread_rng().gen();
442 Multi::BoxU64((i, stack.push(Box::new(i))))
443 },
444 _ => unreachable!(),
445 }
446 }
447
448 fn check(entry: &Multi) {
449 match *entry {
450 Multi::Bool((ref v, ref r)) => assert_eq!(v, *r),
451 Multi::U32((ref v, ref r)) => assert_eq!(v, *r),
452 Multi::U64((ref v, ref r)) => assert_eq!(v, *r),
453 Multi::ArrayU8((ref v, ref r)) => assert_eq!(v, *r),
454 Multi::Unit(ref r) => assert_eq!(&(), r.deref()),
455 Multi::UnitRef(ref r) => assert_eq!(&(), r.deref()),
456 Multi::RcBool((ref v, ref r)) => assert!(Rc::ptr_eq(v, r)),
457 Multi::BoxU64((ref v, ref r)) => assert_eq!(*v, ***r),
458 }
459 }
460
461 fn mutate(entry: &mut Multi) {
462 match *entry {
463 Multi::Bool((ref mut v, ref mut r)) => {
464 let nv = thread_rng().gen();
465 *v = nv;
466 **r = nv;
467 },
468 Multi::U32((ref mut v, ref mut r)) => {
469 let nv = thread_rng().gen();
470 *v = nv;
471 **r = nv;
472 },
473 Multi::U64((ref mut v, ref mut r)) => {
474 let nv = thread_rng().gen();
475 *v = nv;
476 **r = nv;
477 },
478 Multi::ArrayU8((ref mut v, ref mut r)) => {
479 let nv = thread_rng().gen();
480 *v = nv;
481 **r = nv;
482 },
483 Multi::Unit(ref mut r) => { **r = (); },
484 Multi::UnitRef(ref mut r) => { *r.deref_mut() = (); },
485 Multi::RcBool(_) => {},
486 Multi::BoxU64((ref mut v, ref mut r)) => {
487 let nv = thread_rng().gen();
488 *v = nv;
489 *(r.deref_mut().deref_mut()) = nv;
490 },
491 }
492 }
493
494 let mut v = Vec::new();
495 for _ in 0 .. 10_000 {
496 let entry = fill(&stack);
497 check(&entry);
498 v.push(entry);
499 }
500
501 for entry_ref in v.iter() {
502 check(entry_ref);
503 }
504 for entry_ref in v.iter_mut() {
505 mutate(entry_ref);
506 }
507 for entry_ref in v.iter() {
508 check(entry_ref);
509 }
510 }
511
512 #[derive(Debug)]
513 struct DropCounter<'a> {
514 count_ref: &'a Cell<usize>,
515 }
516
517 impl<'a> Drop for DropCounter<'a> {
518 fn drop(&mut self) {
519 self.count_ref.set(self.count_ref.get() + 1);
520 }
521 }
522
523 #[test]
524 fn test_ref_drop() {
525 let stack: Obstack = Obstack::new();
526
527 let drop_counts = vec![Cell::new(0); 10000];
528 let mut dropcounter_refs = Vec::new();
529
530 for count_ref in drop_counts.iter() {
531 assert_eq!(count_ref.get(), 0);
532
533 let drop_counter = DropCounter {
534 count_ref: count_ref,
535 };
536
537 let r = stack.push(drop_counter);
538 if thread_rng().gen() {
539 dropcounter_refs.push(r);
540 assert_eq!(count_ref.get(), 0);
541 } else {
542 mem::drop(r);
543 assert_eq!(count_ref.get(), 1);
544 }
545 }
546
547 mem::drop(dropcounter_refs);
548 for drop_count in drop_counts.iter() {
549 assert_eq!(drop_count.get(), 1);
550 }
551 }
552
553 #[test]
554 fn test_threads() {
555
556 // Perfectly ok to move a stack into a thread
557 let stack: Obstack = Obstack::new();
558 thread::spawn(move || {
559 let r = stack.push(0);
560 assert_eq!(*r, 0);
561 });
562
563 // ...including if it's been used, so long as all references have been dropped
564 let stack: Obstack = Obstack::new();
565 {
566 let _r1 = stack.push(String::from("String in main thread"));
567 let _r2 = stack.push(42);
568 }
569 thread::spawn(move || {
570 let r = stack.push(12345);
571 assert_eq!(*r, 12345);
572 });
573 }
574
575 #[test]
576 fn test_recursive_ref() {
577 let drop_count = Cell::new(0);
578 {
579 let stack: Obstack = Obstack::new();
580
581 // Store the drop counter on the stack:
582 let r1 = stack.push(DropCounter { count_ref: &drop_count });
583 assert_eq!(drop_count.get(), 0);
584
585 // We got a reference to the drop counter in return.
586 //
587 // Now store that reference on the stack:
588 let r_r1 = stack.push(r1);
589
590 // r_r1 is now the thing responsible for dropping the drop counter!
591
592 assert_eq!(drop_count.get(), 0);
593
594 // So let's drop it:
595 mem::drop(r_r1);
596 assert_eq!(drop_count.get(), 1);
597 }
598
599 // Dropping the stack itself does nothing to the drop counter of course.
600 assert_eq!(drop_count.get(), 1);
601 }
602
603 #[test]
604 fn test_large_alignment() {
605 // Large enough that the alignment overflows the default initial capacity
606 #[repr(align(256))]
607 #[derive(Copy, Clone)]
608 struct LargeAlign(u8);
609
610 let obstack = Obstack::new();
611 let val_ref = obstack.push_copy(LargeAlign(0));
612
613 let address = val_ref as *mut _ as usize;
614 assert!(address % std::mem::align_of::<LargeAlign>() == 0);
615 }
616}