Skip to main content

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}