|  | @@ -7,8 +7,16 @@ use core::{ptr, mem, ops};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// Elements required _more_ than the length as capacity.
 | 
	
		
			
				|  |  |  ///
 | 
	
		
			
				|  |  | -/// See guarantee 4.
 | 
	
		
			
				|  |  | -pub const EXTRA_ELEMENTS: usize = 2;
 | 
	
		
			
				|  |  | +/// This represents how many elements that are needed to conduct a `reserve` without the
 | 
	
		
			
				|  |  | +/// stack overflowing, plus one (representing the new element):
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// 1. Aligner.
 | 
	
		
			
				|  |  | +/// 2. Excessive space.
 | 
	
		
			
				|  |  | +/// 3. The old buffer.
 | 
	
		
			
				|  |  | +/// 4. The pushed or inserted block.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// See assumption 4.
 | 
	
		
			
				|  |  | +pub const EXTRA_ELEMENTS: usize = 4;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #[cfg(feature = "alloc_id")]
 | 
	
		
			
				|  |  |  use core::sync::atomic::{self, AtomicUsize};
 | 
	
	
		
			
				|  | @@ -38,9 +46,9 @@ pub struct Bookkeeper {
 | 
	
		
			
				|  |  |      /// 2. No two consecutive or empty block delimited blocks are adjacent, except if the right
 | 
	
		
			
				|  |  |      ///    block is empty.
 | 
	
		
			
				|  |  |      /// 3. There are no trailing empty blocks.
 | 
	
		
			
				|  |  | -    /// 4. The capacity is always two blocks more than the length (this is due to reallocation
 | 
	
		
			
				|  |  | -    ///    pushing at maximum two elements, so we reserve two extra to allow pushing one
 | 
	
		
			
				|  |  | -    ///    additional element without unbounded recursion).
 | 
	
		
			
				|  |  | +    /// 4. The capacity is always `EXTRA_ELEMENTS` blocks more than the length (this is due to
 | 
	
		
			
				|  |  | +    ///    reallocation pushing at maximum two elements, so we reserve two or more extra to allow
 | 
	
		
			
				|  |  | +    ///    pushing one additional element without unbounded recursion).
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// These are **not** invariants: If these assumpptions are not held, it will simply act strange
 | 
	
		
			
				|  |  |      /// (e.g. logic bugs), but not memory unsafety.
 | 
	
	
		
			
				|  | @@ -165,6 +173,10 @@ impl Bookkeeper {
 | 
	
		
			
				|  |  |              // Reverse iterator over the blocks.
 | 
	
		
			
				|  |  |              let mut it = self.pool.iter().enumerate().rev();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            // Check that the capacity is large enough.
 | 
	
		
			
				|  |  | +            assert!(self.pool.len() + EXTRA_ELEMENTS <= self.pool.capacity(), "The capacity should \
 | 
	
		
			
				|  |  | +                    be at least {} more than the length of the pool.", EXTRA_ELEMENTS);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if let Some((_, x)) = it.next() {
 | 
	
		
			
				|  |  |                  // Make sure there are no leading empty blocks.
 | 
	
		
			
				|  |  |                  assert!(!x.is_empty());
 | 
	
	
		
			
				|  | @@ -631,9 +643,12 @@ pub trait Allocator: ops::DerefMut<Target = Bookkeeper> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /// Reserve some number of elements.
 | 
	
		
			
				|  |  |      fn reserve(&mut self, min_cap: usize) {
 | 
	
		
			
				|  |  | +        // Logging.
 | 
	
		
			
				|  |  | +        log!(self;min_cap, "Reserving {}.", min_cap);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          if self.pool.capacity() < self.pool.len() + EXTRA_ELEMENTS || self.pool.capacity() < min_cap {
 | 
	
		
			
				|  |  | -            // One extra for the old buffer.
 | 
	
		
			
				|  |  | -            let new_cap = (min_cap + EXTRA_ELEMENTS) * 2 + 16 + 1;
 | 
	
		
			
				|  |  | +            // Reserve a little extra for performance reasons.
 | 
	
		
			
				|  |  | +            let new_cap = (min_cap + EXTRA_ELEMENTS) * 2 + 16;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Catch 'em all.
 | 
	
		
			
				|  |  |              debug_assert!(new_cap > self.pool.capacity(), "Reserve shrinks?!");
 | 
	
	
		
			
				|  | @@ -747,7 +762,8 @@ pub trait Allocator: ops::DerefMut<Target = Bookkeeper> {
 | 
	
		
			
				|  |  |          unsafe {
 | 
	
		
			
				|  |  |              // TODO clean this mess up.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | +            // We will only extend the length if we were unable to fit it into the current length.
 | 
	
		
			
				|  |  | +            if ind + n == self.pool.len() {
 | 
	
		
			
				|  |  |                  // We will move a block into reserved memory but outside of the vec's bounds. For
 | 
	
		
			
				|  |  |                  // that reason, we push an uninitialized element to extend the length, which will
 | 
	
		
			
				|  |  |                  // be assigned in the memcpy.
 |