auto merge of #18747 : csherratt/rust/ringbuf-remove-option, r=huonw
Fix for task in Metabug #18009 (Rebased version of https://github.com/rust-lang/rust/pull/18170) This changes much of about how RingBuf functions. `lo`, `nelts` are replaced by a more traditional `head` and`tail`. The `Vec<Option<T>>` is replaced by a bare pointer that is managed by the `RingBuf` itself. This also expects the ring buffer to always be size that is a power of 2. This change also includes a number of new tests to cover the some areas that could be of concern with manual memory management. The benchmarks have been reworked since the old ones were benchmarking of the Ring buffers growth rather then the actual test. The unit test suite have been expanded, and exposed some bugs in `fn get()` and `fn get_mut()` ## Benchmark **Before:** ``` test ring_buf::tests::bench_grow_1025 ... bench: 8919 ns/iter (+/- 87) test ring_buf::tests::bench_iter_1000 ... bench: 924 ns/iter (+/- 28) test ring_buf::tests::bench_mut_iter_1000 ... bench: 918 ns/iter (+/- 6) test ring_buf::tests::bench_new ... bench: 15 ns/iter (+/- 0) test ring_buf::tests::bench_pop_100 ... bench: 294 ns/iter (+/- 9) test ring_buf::tests::bench_pop_front_100 ... bench: 948 ns/iter (+/- 32) test ring_buf::tests::bench_push_back_100 ... bench: 291 ns/iter (+/- 16) test ring_buf::tests::bench_push_front_100 ... bench: 311 ns/iter (+/- 27 ``` **After:** ``` test ring_buf::tests::bench_grow_1025 ... bench: 2209 ns/iter (+/- 169) test ring_buf::tests::bench_iter_1000 ... bench: 534 ns/iter (+/- 27) test ring_buf::tests::bench_mut_iter_1000 ... bench: 515 ns/iter (+/- 28) test ring_buf::tests::bench_new ... bench: 11 ns/iter (+/- 0) test ring_buf::tests::bench_pop_100 ... bench: 170 ns/iter (+/- 5) test ring_buf::tests::bench_pop_front_100 ... bench: 171 ns/iter (+/- 11) test ring_buf::tests::bench_push_back_100 ... bench: 172 ns/iter (+/- 13) test ring_buf::tests::bench_push_front_100 ... bench: 158 ns/iter (+/- 12) ```
This commit is contained in:
commit
0b7b4f075a
1 changed files with 514 additions and 199 deletions
|
|
@ -15,14 +15,19 @@
|
|||
|
||||
use core::prelude::*;
|
||||
|
||||
use core::cmp;
|
||||
use core::default::Default;
|
||||
use core::fmt;
|
||||
use core::iter;
|
||||
use core::slice;
|
||||
use std::hash::{Writer, Hash};
|
||||
use core::raw::Slice as RawSlice;
|
||||
use core::ptr;
|
||||
use core::kinds::marker;
|
||||
use core::mem;
|
||||
use core::num::{Int, UnsignedInt};
|
||||
|
||||
use vec::Vec;
|
||||
use std::hash::{Writer, Hash};
|
||||
use std::cmp;
|
||||
|
||||
use alloc::heap;
|
||||
|
||||
static INITIAL_CAPACITY: uint = 8u; // 2^3
|
||||
static MINIMUM_CAPACITY: uint = 2u;
|
||||
|
|
@ -33,11 +38,37 @@ static MINIMUM_CAPACITY: uint = 2u;
|
|||
|
||||
|
||||
/// `RingBuf` is a circular buffer that implements `Deque`.
|
||||
#[deriving(Clone)]
|
||||
pub struct RingBuf<T> {
|
||||
nelts: uint,
|
||||
lo: uint,
|
||||
elts: Vec<Option<T>>
|
||||
// tail and head are pointers into the buffer. Tail always points
|
||||
// to the first element that could be read, Head always points
|
||||
// to where data should be written.
|
||||
// If tail == head the buffer is empty. The length of the ringbuf
|
||||
// is defined as the distance between the two.
|
||||
|
||||
tail: uint,
|
||||
head: uint,
|
||||
cap: uint,
|
||||
ptr: *mut T
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for RingBuf<T> {
|
||||
fn clone(&self) -> RingBuf<T> {
|
||||
self.iter().map(|t| t.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<T> Drop for RingBuf<T> {
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
unsafe {
|
||||
if mem::size_of::<T>() != 0 {
|
||||
heap::deallocate(self.ptr as *mut u8,
|
||||
self.cap * mem::size_of::<T>(),
|
||||
mem::min_align_of::<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for RingBuf<T> {
|
||||
|
|
@ -45,6 +76,34 @@ impl<T> Default for RingBuf<T> {
|
|||
fn default() -> RingBuf<T> { RingBuf::new() }
|
||||
}
|
||||
|
||||
impl<T> RingBuf<T> {
|
||||
/// Turn ptr into a slice
|
||||
#[inline]
|
||||
unsafe fn buffer_as_slice(&self) -> &[T] {
|
||||
mem::transmute(RawSlice { data: self.ptr as *const T, len: self.cap })
|
||||
}
|
||||
|
||||
/// Moves an element out of the buffer
|
||||
#[inline]
|
||||
unsafe fn buffer_read(&mut self, off: uint) -> T {
|
||||
ptr::read(self.ptr.offset(off as int) as *const T)
|
||||
}
|
||||
|
||||
/// Writes an element into the buffer, moving it.
|
||||
#[inline]
|
||||
unsafe fn buffer_write(&mut self, off: uint, t: T) {
|
||||
ptr::write(self.ptr.offset(off as int), t);
|
||||
}
|
||||
|
||||
/// Returns true iff the buffer is at capacity
|
||||
#[inline]
|
||||
fn is_full(&self) -> bool { self.cap - self.len() == 1 }
|
||||
|
||||
/// Returns the index in the underlying buffer for a given logical element index.
|
||||
#[inline]
|
||||
fn wrap_index(&self, idx: uint) -> uint { wrap_index(idx, self.cap) }
|
||||
}
|
||||
|
||||
impl<T> RingBuf<T> {
|
||||
/// Creates an empty `RingBuf`.
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
|
|
@ -55,8 +114,27 @@ impl<T> RingBuf<T> {
|
|||
/// Creates an empty `RingBuf` with space for at least `n` elements.
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn with_capacity(n: uint) -> RingBuf<T> {
|
||||
RingBuf{nelts: 0, lo: 0,
|
||||
elts: Vec::from_fn(cmp::max(MINIMUM_CAPACITY, n), |_| None)}
|
||||
// +1 since the ringbuffer always leaves one space empty
|
||||
let cap = cmp::max(n + 1, MINIMUM_CAPACITY).next_power_of_two();
|
||||
let size = cap.checked_mul(mem::size_of::<T>())
|
||||
.expect("capacity overflow");
|
||||
|
||||
let ptr = if mem::size_of::<T>() != 0 {
|
||||
unsafe {
|
||||
let ptr = heap::allocate(size, mem::min_align_of::<T>()) as *mut T;;
|
||||
if ptr.is_null() { ::alloc::oom() }
|
||||
ptr
|
||||
}
|
||||
} else {
|
||||
heap::EMPTY as *mut T
|
||||
};
|
||||
|
||||
RingBuf {
|
||||
tail: 0,
|
||||
head: 0,
|
||||
cap: cap,
|
||||
ptr: ptr
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves an element in the `RingBuf` by index.
|
||||
|
|
@ -74,9 +152,11 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn get(&self, i: uint) -> Option<&T> {
|
||||
match self.elts.get(i) {
|
||||
None => None,
|
||||
Some(opt) => opt.as_ref(),
|
||||
if i < self.len() {
|
||||
let idx = self.wrap_index(self.tail + i);
|
||||
unsafe { Some(&*self.ptr.offset(idx as int)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,9 +182,11 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn get_mut(&mut self, i: uint) -> Option<&mut T> {
|
||||
match self.elts.get_mut(i) {
|
||||
None => None,
|
||||
Some(opt) => opt.as_mut(),
|
||||
if i < self.len() {
|
||||
let idx = self.wrap_index(self.tail + i);
|
||||
unsafe { Some(&mut *self.ptr.offset(idx as int)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,15 +212,11 @@ impl<T> RingBuf<T> {
|
|||
pub fn swap(&mut self, i: uint, j: uint) {
|
||||
assert!(i < self.len());
|
||||
assert!(j < self.len());
|
||||
let ri = self.raw_index(i);
|
||||
let rj = self.raw_index(j);
|
||||
self.elts.as_mut_slice().swap(ri, rj);
|
||||
}
|
||||
|
||||
/// Returns the index in the underlying `Vec` for a given logical element
|
||||
/// index.
|
||||
fn raw_index(&self, idx: uint) -> uint {
|
||||
raw_index(self.lo, self.elts.len(), idx)
|
||||
let ri = self.wrap_index(self.tail + i);
|
||||
let rj = self.wrap_index(self.tail + j);
|
||||
unsafe {
|
||||
ptr::swap(self.ptr.offset(ri as int), self.ptr.offset(rj as int))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of elements the `RingBuf` can hold without
|
||||
|
|
@ -150,14 +228,11 @@ impl<T> RingBuf<T> {
|
|||
/// use std::collections::RingBuf;
|
||||
///
|
||||
/// let buf: RingBuf<int> = RingBuf::with_capacity(10);
|
||||
/// assert_eq!(buf.capacity(), 10);
|
||||
/// assert!(buf.capacity() >= 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn capacity(&self) -> uint {
|
||||
// FXIME(Gankro): not the actual usable capacity if you use reserve/reserve_exact
|
||||
self.elts.capacity()
|
||||
}
|
||||
pub fn capacity(&self) -> uint { self.cap - 1 }
|
||||
|
||||
/// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the
|
||||
/// given `RingBuf`. Does nothing if the capacity is already sufficient.
|
||||
|
|
@ -181,8 +256,7 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn reserve_exact(&mut self, additional: uint) {
|
||||
// FIXME(Gankro): this is just wrong. The ringbuf won't actually use this space
|
||||
self.elts.reserve_exact(additional);
|
||||
self.reserve(additional);
|
||||
}
|
||||
|
||||
/// Reserves capacity for at least `additional` more elements to be inserted in the given
|
||||
|
|
@ -203,8 +277,69 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn reserve(&mut self, additional: uint) {
|
||||
// FIXME(Gankro): this is just wrong. The ringbuf won't actually use this space
|
||||
self.elts.reserve(additional);
|
||||
let new_len = self.len() + additional;
|
||||
assert!(new_len + 1 > self.len(), "capacity overflow");
|
||||
if new_len > self.capacity() {
|
||||
let count = (new_len + 1).next_power_of_two();
|
||||
assert!(count >= new_len + 1);
|
||||
|
||||
if mem::size_of::<T>() != 0 {
|
||||
let old = self.cap * mem::size_of::<T>();
|
||||
let new = count.checked_mul(mem::size_of::<T>())
|
||||
.expect("capacity overflow");
|
||||
unsafe {
|
||||
self.ptr = heap::reallocate(self.ptr as *mut u8,
|
||||
old,
|
||||
new,
|
||||
mem::min_align_of::<T>()) as *mut T;
|
||||
if self.ptr.is_null() { ::alloc::oom() }
|
||||
}
|
||||
}
|
||||
|
||||
// Move the shortest contiguous section of the ring buffer
|
||||
// T H
|
||||
// [o o o o o o o . ]
|
||||
// T H
|
||||
// A [o o o o o o o . . . . . . . . . ]
|
||||
// H T
|
||||
// [o o . o o o o o ]
|
||||
// T H
|
||||
// B [. . . o o o o o o o . . . . . . ]
|
||||
// H T
|
||||
// [o o o o o . o o ]
|
||||
// H T
|
||||
// C [o o o o o . . . . . . . . . o o ]
|
||||
|
||||
let oldcap = self.cap;
|
||||
self.cap = count;
|
||||
|
||||
if self.tail <= self.head { // A
|
||||
// Nop
|
||||
} else if self.head < oldcap - self.tail { // B
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping_memory(
|
||||
self.ptr.offset(oldcap as int),
|
||||
self.ptr as *const T,
|
||||
self.head
|
||||
);
|
||||
}
|
||||
self.head += oldcap;
|
||||
debug_assert!(self.head > self.tail);
|
||||
} else { // C
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping_memory(
|
||||
self.ptr.offset((count - (oldcap - self.tail)) as int),
|
||||
self.ptr.offset(self.tail as int) as *const T,
|
||||
oldcap - self.tail
|
||||
);
|
||||
}
|
||||
self.tail = count - (oldcap - self.tail);
|
||||
debug_assert!(self.head < self.tail);
|
||||
}
|
||||
debug_assert!(self.head < self.cap);
|
||||
debug_assert!(self.tail < self.cap);
|
||||
debug_assert!(self.cap.count_ones() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a front-to-back iterator.
|
||||
|
|
@ -223,7 +358,11 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn iter(&self) -> Items<T> {
|
||||
Items{index: 0, rindex: self.nelts, lo: self.lo, elts: self.elts.as_slice()}
|
||||
Items {
|
||||
tail: self.tail,
|
||||
head: self.head,
|
||||
ring: unsafe { self.buffer_as_slice() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a front-to-back iterator which returns mutable references.
|
||||
|
|
@ -244,32 +383,14 @@ impl<T> RingBuf<T> {
|
|||
/// assert_eq!(buf.iter_mut().collect::<Vec<&mut int>>()[], b);
|
||||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn iter_mut(&mut self) -> MutItems<T> {
|
||||
let start_index = raw_index(self.lo, self.elts.len(), 0);
|
||||
let end_index = raw_index(self.lo, self.elts.len(), self.nelts);
|
||||
|
||||
// Divide up the array
|
||||
if end_index <= start_index {
|
||||
// Items to iterate goes from:
|
||||
// start_index to self.elts.len()
|
||||
// and then
|
||||
// 0 to end_index
|
||||
let (temp, remaining1) = self.elts.split_at_mut(start_index);
|
||||
let (remaining2, _) = temp.split_at_mut(end_index);
|
||||
MutItems {
|
||||
remaining1: remaining1.iter_mut(),
|
||||
remaining2: remaining2.iter_mut(),
|
||||
nelts: self.nelts,
|
||||
}
|
||||
} else {
|
||||
// Items to iterate goes from start_index to end_index:
|
||||
let (empty, elts) = self.elts.split_at_mut(0);
|
||||
let remaining1 = elts[mut start_index..end_index];
|
||||
MutItems {
|
||||
remaining1: remaining1.iter_mut(),
|
||||
remaining2: empty.iter_mut(),
|
||||
nelts: self.nelts,
|
||||
}
|
||||
pub fn iter_mut<'a>(&'a mut self) -> MutItems<'a, T> {
|
||||
MutItems {
|
||||
tail: self.tail,
|
||||
head: self.head,
|
||||
cap: self.cap,
|
||||
ptr: self.ptr,
|
||||
marker: marker::ContravariantLifetime::<'a>,
|
||||
marker2: marker::NoCopy
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +407,7 @@ impl<T> RingBuf<T> {
|
|||
/// assert_eq!(v.len(), 1);
|
||||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn len(&self) -> uint { self.nelts }
|
||||
pub fn len(&self) -> uint { count(self.tail, self.head, self.cap) }
|
||||
|
||||
/// Returns true if the buffer contains no elements
|
||||
///
|
||||
|
|
@ -317,9 +438,9 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn clear(&mut self) {
|
||||
for x in self.elts.iter_mut() { *x = None }
|
||||
self.nelts = 0;
|
||||
self.lo = 0;
|
||||
while self.pop_front().is_some() {}
|
||||
self.head = 0;
|
||||
self.tail = 0;
|
||||
}
|
||||
|
||||
/// Provides a reference to the front element, or `None` if the sequence is
|
||||
|
|
@ -339,7 +460,7 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn front(&self) -> Option<&T> {
|
||||
if self.nelts > 0 { Some(&self[0]) } else { None }
|
||||
if !self.is_empty() { Some(&self[0]) } else { None }
|
||||
}
|
||||
|
||||
/// Provides a mutable reference to the front element, or `None` if the
|
||||
|
|
@ -363,7 +484,7 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn front_mut(&mut self) -> Option<&mut T> {
|
||||
if self.nelts > 0 { Some(&mut self[0]) } else { None }
|
||||
if !self.is_empty() { Some(&mut self[0]) } else { None }
|
||||
}
|
||||
|
||||
/// Provides a reference to the back element, or `None` if the sequence is
|
||||
|
|
@ -383,7 +504,7 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn back(&self) -> Option<&T> {
|
||||
if self.nelts > 0 { Some(&self[self.nelts - 1]) } else { None }
|
||||
if !self.is_empty() { Some(&self[self.len() - 1]) } else { None }
|
||||
}
|
||||
|
||||
/// Provides a mutable reference to the back element, or `None` if the
|
||||
|
|
@ -407,8 +528,8 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn back_mut(&mut self) -> Option<&mut T> {
|
||||
let nelts = self.nelts;
|
||||
if nelts > 0 { Some(&mut self[nelts - 1]) } else { None }
|
||||
let len = self.len();
|
||||
if !self.is_empty() { Some(&mut self[len - 1]) } else { None }
|
||||
}
|
||||
|
||||
/// Removes the first element and returns it, or `None` if the sequence is
|
||||
|
|
@ -429,12 +550,13 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn pop_front(&mut self) -> Option<T> {
|
||||
let result = self.elts[self.lo].take();
|
||||
if result.is_some() {
|
||||
self.lo = (self.lo + 1u) % self.elts.len();
|
||||
self.nelts -= 1u;
|
||||
if self.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let tail = self.tail;
|
||||
self.tail = self.wrap_index(self.tail + 1);
|
||||
unsafe { Some(self.buffer_read(tail)) }
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Inserts an element first in the sequence.
|
||||
|
|
@ -451,14 +573,14 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn push_front(&mut self, t: T) {
|
||||
if self.nelts == self.elts.len() {
|
||||
grow(self.nelts, &mut self.lo, &mut self.elts);
|
||||
if self.is_full() {
|
||||
self.reserve(1);
|
||||
debug_assert!(!self.is_full());
|
||||
}
|
||||
if self.lo == 0u {
|
||||
self.lo = self.elts.len() - 1u;
|
||||
} else { self.lo -= 1u; }
|
||||
self.elts[self.lo] = Some(t);
|
||||
self.nelts += 1u;
|
||||
|
||||
self.tail = self.wrap_index(self.tail - 1);
|
||||
let tail = self.tail;
|
||||
unsafe { self.buffer_write(tail, t); }
|
||||
}
|
||||
|
||||
/// Deprecated: Renamed to `push_back`.
|
||||
|
|
@ -481,12 +603,14 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn push_back(&mut self, t: T) {
|
||||
if self.nelts == self.elts.len() {
|
||||
grow(self.nelts, &mut self.lo, &mut self.elts);
|
||||
if self.is_full() {
|
||||
self.reserve(1);
|
||||
debug_assert!(!self.is_full());
|
||||
}
|
||||
let hi = self.raw_index(self.nelts);
|
||||
self.elts[hi] = Some(t);
|
||||
self.nelts += 1u;
|
||||
|
||||
let head = self.head;
|
||||
self.head = self.wrap_index(self.head + 1);
|
||||
unsafe { self.buffer_write(head, t) }
|
||||
}
|
||||
|
||||
/// Deprecated: Renamed to `pop_back`.
|
||||
|
|
@ -511,38 +635,51 @@ impl<T> RingBuf<T> {
|
|||
/// ```
|
||||
#[unstable = "matches collection reform specification, waiting for dust to settle"]
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
if self.nelts > 0 {
|
||||
self.nelts -= 1;
|
||||
let hi = self.raw_index(self.nelts);
|
||||
self.elts[hi].take()
|
||||
} else {
|
||||
if self.is_empty() {
|
||||
None
|
||||
} else {
|
||||
self.head = self.wrap_index(self.head - 1);
|
||||
let head = self.head;
|
||||
unsafe { Some(self.buffer_read(head)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index in the underlying buffer for a given logical element index.
|
||||
#[inline]
|
||||
fn wrap_index(index: uint, size: uint) -> uint {
|
||||
// size is always a power of 2
|
||||
index & (size - 1)
|
||||
}
|
||||
|
||||
/// Calculate the number of elements left to be read in the buffer
|
||||
#[inline]
|
||||
fn count(tail: uint, head: uint, size: uint) -> uint {
|
||||
// size is always a power of 2
|
||||
(head - tail) & (size - 1)
|
||||
}
|
||||
|
||||
/// `RingBuf` iterator.
|
||||
pub struct Items<'a, T:'a> {
|
||||
lo: uint,
|
||||
index: uint,
|
||||
rindex: uint,
|
||||
elts: &'a [Option<T>],
|
||||
ring: &'a [T],
|
||||
tail: uint,
|
||||
head: uint
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator<&'a T> for Items<'a, T> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
if self.index == self.rindex {
|
||||
if self.tail == self.head {
|
||||
return None;
|
||||
}
|
||||
let raw_index = raw_index(self.lo, self.elts.len(), self.index);
|
||||
self.index += 1;
|
||||
Some(self.elts[raw_index].as_ref().unwrap())
|
||||
let tail = self.tail;
|
||||
self.tail = wrap_index(self.tail + 1, self.ring.len());
|
||||
unsafe { Some(self.ring.unsafe_get(tail)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (uint, Option<uint>) {
|
||||
let len = self.rindex - self.index;
|
||||
let len = count(self.tail, self.head, self.ring.len());
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
|
@ -550,129 +687,87 @@ impl<'a, T> Iterator<&'a T> for Items<'a, T> {
|
|||
impl<'a, T> DoubleEndedIterator<&'a T> for Items<'a, T> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<&'a T> {
|
||||
if self.index == self.rindex {
|
||||
if self.tail == self.head {
|
||||
return None;
|
||||
}
|
||||
self.rindex -= 1;
|
||||
let raw_index = raw_index(self.lo, self.elts.len(), self.rindex);
|
||||
Some(self.elts[raw_index].as_ref().unwrap())
|
||||
self.head = wrap_index(self.head - 1, self.ring.len());
|
||||
unsafe { Some(self.ring.unsafe_get(self.head)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, T> ExactSize<&'a T> for Items<'a, T> {}
|
||||
|
||||
impl<'a, T> RandomAccessIterator<&'a T> for Items<'a, T> {
|
||||
#[inline]
|
||||
fn indexable(&self) -> uint { self.rindex - self.index }
|
||||
fn indexable(&self) -> uint {
|
||||
let (len, _) = self.size_hint();
|
||||
len
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn idx(&mut self, j: uint) -> Option<&'a T> {
|
||||
if j >= self.indexable() {
|
||||
None
|
||||
} else {
|
||||
let raw_index = raw_index(self.lo, self.elts.len(), self.index + j);
|
||||
Some(self.elts[raw_index].as_ref().unwrap())
|
||||
let idx = wrap_index(self.tail + j, self.ring.len());
|
||||
unsafe { Some(self.ring.unsafe_get(idx)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME This was implemented differently from Items because of a problem
|
||||
// with returning the mutable reference. I couldn't find a way to
|
||||
// make the lifetime checker happy so, but there should be a way.
|
||||
/// `RingBuf` mutable iterator.
|
||||
pub struct MutItems<'a, T:'a> {
|
||||
remaining1: slice::MutItems<'a, Option<T>>,
|
||||
remaining2: slice::MutItems<'a, Option<T>>,
|
||||
nelts: uint,
|
||||
ptr: *mut T,
|
||||
tail: uint,
|
||||
head: uint,
|
||||
cap: uint,
|
||||
marker: marker::ContravariantLifetime<'a>,
|
||||
marker2: marker::NoCopy
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator<&'a mut T> for MutItems<'a, T> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a mut T> {
|
||||
if self.nelts == 0 {
|
||||
if self.tail == self.head {
|
||||
return None;
|
||||
}
|
||||
self.nelts -= 1;
|
||||
match self.remaining1.next() {
|
||||
Some(ptr) => return Some(ptr.as_mut().unwrap()),
|
||||
None => {}
|
||||
}
|
||||
match self.remaining2.next() {
|
||||
Some(ptr) => return Some(ptr.as_mut().unwrap()),
|
||||
None => unreachable!(),
|
||||
let tail = self.tail;
|
||||
self.tail = wrap_index(self.tail + 1, self.cap);
|
||||
if mem::size_of::<T>() != 0 {
|
||||
unsafe { Some(&mut *self.ptr.offset(tail as int)) }
|
||||
} else {
|
||||
// use a non-zero pointer
|
||||
Some(unsafe { mem::transmute(1u) })
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (uint, Option<uint>) {
|
||||
(self.nelts, Some(self.nelts))
|
||||
let len = count(self.tail, self.head, self.cap);
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DoubleEndedIterator<&'a mut T> for MutItems<'a, T> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<&'a mut T> {
|
||||
if self.nelts == 0 {
|
||||
if self.tail == self.head {
|
||||
return None;
|
||||
}
|
||||
self.nelts -= 1;
|
||||
match self.remaining2.next_back() {
|
||||
Some(ptr) => return Some(ptr.as_mut().unwrap()),
|
||||
None => {}
|
||||
}
|
||||
match self.remaining1.next_back() {
|
||||
Some(ptr) => return Some(ptr.as_mut().unwrap()),
|
||||
None => unreachable!(),
|
||||
}
|
||||
self.head = wrap_index(self.head - 1, self.cap);
|
||||
unsafe { Some(&mut *self.ptr.offset(self.head as int)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSize<&'a mut T> for MutItems<'a, T> {}
|
||||
|
||||
/// Grow is only called on full elts, so nelts is also len(elts), unlike
|
||||
/// elsewhere.
|
||||
fn grow<T>(nelts: uint, loptr: &mut uint, elts: &mut Vec<Option<T>>) {
|
||||
assert_eq!(nelts, elts.len());
|
||||
let lo = *loptr;
|
||||
elts.reserve_exact(nelts);
|
||||
let newlen = elts.capacity();
|
||||
|
||||
/* fill with None */
|
||||
for _ in range(elts.len(), newlen) {
|
||||
elts.push(None);
|
||||
}
|
||||
|
||||
/*
|
||||
Move the shortest half into the newly reserved area.
|
||||
lo ---->|
|
||||
nelts ----------->|
|
||||
[o o o|o o o o o]
|
||||
A [. . .|o o o o o o o o|. . . . .]
|
||||
B [o o o|. . . . . . . .|o o o o o]
|
||||
*/
|
||||
|
||||
assert!(newlen - nelts/2 >= nelts);
|
||||
if lo <= (nelts - lo) { // A
|
||||
for i in range(0u, lo) {
|
||||
elts.as_mut_slice().swap(i, nelts + i);
|
||||
}
|
||||
} else { // B
|
||||
for i in range(lo, nelts) {
|
||||
elts.as_mut_slice().swap(i, newlen - nelts + i);
|
||||
}
|
||||
*loptr += newlen - nelts;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index in the underlying `Vec` for a given logical element index.
|
||||
fn raw_index(lo: uint, len: uint, index: uint) -> uint {
|
||||
if lo >= len - index {
|
||||
lo + index - len
|
||||
} else {
|
||||
lo + index
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: PartialEq> PartialEq for RingBuf<A> {
|
||||
fn eq(&self, other: &RingBuf<A>) -> bool {
|
||||
self.nelts == other.nelts &&
|
||||
self.len() == other.len() &&
|
||||
self.iter().zip(other.iter()).all(|(a, b)| a.eq(b))
|
||||
}
|
||||
fn ne(&self, other: &RingBuf<A>) -> bool {
|
||||
|
|
@ -707,22 +802,14 @@ impl<S: Writer, A: Hash<S>> Hash<S> for RingBuf<A> {
|
|||
impl<A> Index<uint, A> for RingBuf<A> {
|
||||
#[inline]
|
||||
fn index<'a>(&'a self, i: &uint) -> &'a A {
|
||||
let idx = self.raw_index(*i);
|
||||
match self.elts[idx] {
|
||||
None => panic!(),
|
||||
Some(ref v) => v,
|
||||
}
|
||||
self.get(*i).expect("Out of bounds access")
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> IndexMut<uint, A> for RingBuf<A> {
|
||||
#[inline]
|
||||
fn index_mut<'a>(&'a mut self, i: &uint) -> &'a mut A {
|
||||
let idx = self.raw_index(*i);
|
||||
match *(&mut self.elts[idx]) {
|
||||
None => panic!(),
|
||||
Some(ref mut v) => v
|
||||
}
|
||||
self.get_mut(*i).expect("Out of bounds access")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -888,36 +975,99 @@ mod tests {
|
|||
#[bench]
|
||||
fn bench_new(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let _: RingBuf<u64> = RingBuf::new();
|
||||
let ring: RingBuf<u64> = RingBuf::new();
|
||||
test::black_box(ring);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_push_back(b: &mut test::Bencher) {
|
||||
let mut deq = RingBuf::new();
|
||||
fn bench_push_back_100(b: &mut test::Bencher) {
|
||||
let mut deq = RingBuf::with_capacity(101);
|
||||
b.iter(|| {
|
||||
deq.push_back(0i);
|
||||
for i in range(0i, 100) {
|
||||
deq.push_back(i);
|
||||
}
|
||||
deq.head = 0;
|
||||
deq.tail = 0;
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_push_front(b: &mut test::Bencher) {
|
||||
let mut deq = RingBuf::new();
|
||||
fn bench_push_front_100(b: &mut test::Bencher) {
|
||||
let mut deq = RingBuf::with_capacity(101);
|
||||
b.iter(|| {
|
||||
deq.push_front(0i);
|
||||
for i in range(0i, 100) {
|
||||
deq.push_front(i);
|
||||
}
|
||||
deq.head = 0;
|
||||
deq.tail = 0;
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_grow(b: &mut test::Bencher) {
|
||||
let mut deq = RingBuf::new();
|
||||
fn bench_pop_back_100(b: &mut test::Bencher) {
|
||||
let mut deq: RingBuf<int> = RingBuf::with_capacity(101);
|
||||
|
||||
b.iter(|| {
|
||||
for _ in range(0i, 65) {
|
||||
deq.push_front(1i);
|
||||
deq.head = 100;
|
||||
deq.tail = 0;
|
||||
while !deq.is_empty() {
|
||||
test::black_box(deq.pop_back());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pop_front_100(b: &mut test::Bencher) {
|
||||
let mut deq: RingBuf<int> = RingBuf::with_capacity(101);
|
||||
|
||||
b.iter(|| {
|
||||
deq.head = 100;
|
||||
deq.tail = 0;
|
||||
while !deq.is_empty() {
|
||||
test::black_box(deq.pop_front());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_grow_1025(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut deq = RingBuf::new();
|
||||
for i in range(0i, 1025) {
|
||||
deq.push_front(i);
|
||||
}
|
||||
test::black_box(deq);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_iter_1000(b: &mut test::Bencher) {
|
||||
let ring: RingBuf<int> = range(0i, 1000).collect();
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
for &i in ring.iter() {
|
||||
sum += i;
|
||||
}
|
||||
test::black_box(sum);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_mut_iter_1000(b: &mut test::Bencher) {
|
||||
let mut ring: RingBuf<int> = range(0i, 1000).collect();
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
for i in ring.iter_mut() {
|
||||
sum += *i;
|
||||
}
|
||||
test::black_box(sum);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
enum Taggy {
|
||||
One(int),
|
||||
|
|
@ -1034,11 +1184,11 @@ mod tests {
|
|||
let mut d = RingBuf::new();
|
||||
d.push_back(0u64);
|
||||
d.reserve(50);
|
||||
assert!(d.capacity() >= 64);
|
||||
assert!(d.capacity() >= 51);
|
||||
let mut d = RingBuf::new();
|
||||
d.push_back(0u32);
|
||||
d.reserve(50);
|
||||
assert!(d.capacity() >= 64);
|
||||
assert!(d.capacity() >= 51);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1257,4 +1407,169 @@ mod tests {
|
|||
.collect();
|
||||
assert!(format!("{}", ringbuf).as_slice() == "[just, one, test, more]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop() {
|
||||
static mut drops: uint = 0;
|
||||
struct Elem;
|
||||
impl Drop for Elem {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drops += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
let mut ring = RingBuf::new();
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
drop(ring);
|
||||
|
||||
assert_eq!(unsafe {drops}, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_with_pop() {
|
||||
static mut drops: uint = 0;
|
||||
struct Elem;
|
||||
impl Drop for Elem {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drops += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
let mut ring = RingBuf::new();
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
|
||||
drop(ring.pop_back());
|
||||
drop(ring.pop_front());
|
||||
assert_eq!(unsafe {drops}, 2);
|
||||
|
||||
drop(ring);
|
||||
assert_eq!(unsafe {drops}, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_clear() {
|
||||
static mut drops: uint = 0;
|
||||
struct Elem;
|
||||
impl Drop for Elem {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drops += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
let mut ring = RingBuf::new();
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
ring.push_back(Elem);
|
||||
ring.push_front(Elem);
|
||||
ring.clear();
|
||||
assert_eq!(unsafe {drops}, 4);
|
||||
|
||||
drop(ring);
|
||||
assert_eq!(unsafe {drops}, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reserve_grow() {
|
||||
// test growth path A
|
||||
// [T o o H] -> [T o o H . . . . ]
|
||||
let mut ring = RingBuf::with_capacity(4);
|
||||
for i in range(0i, 3) {
|
||||
ring.push_back(i);
|
||||
}
|
||||
ring.reserve(7);
|
||||
for i in range(0i, 3) {
|
||||
assert_eq!(ring.pop_front(), Some(i));
|
||||
}
|
||||
|
||||
// test growth path B
|
||||
// [H T o o] -> [. T o o H . . . ]
|
||||
let mut ring = RingBuf::with_capacity(4);
|
||||
for i in range(0i, 1) {
|
||||
ring.push_back(i);
|
||||
assert_eq!(ring.pop_front(), Some(i));
|
||||
}
|
||||
for i in range(0i, 3) {
|
||||
ring.push_back(i);
|
||||
}
|
||||
ring.reserve(7);
|
||||
for i in range(0i, 3) {
|
||||
assert_eq!(ring.pop_front(), Some(i));
|
||||
}
|
||||
|
||||
// test growth path C
|
||||
// [o o H T] -> [o o H . . . . T ]
|
||||
let mut ring = RingBuf::with_capacity(4);
|
||||
for i in range(0i, 3) {
|
||||
ring.push_back(i);
|
||||
assert_eq!(ring.pop_front(), Some(i));
|
||||
}
|
||||
for i in range(0i, 3) {
|
||||
ring.push_back(i);
|
||||
}
|
||||
ring.reserve(7);
|
||||
for i in range(0i, 3) {
|
||||
assert_eq!(ring.pop_front(), Some(i));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get() {
|
||||
let mut ring = RingBuf::new();
|
||||
ring.push_back(0i);
|
||||
assert_eq!(ring.get(0), Some(&0));
|
||||
assert_eq!(ring.get(1), None);
|
||||
|
||||
ring.push_back(1);
|
||||
assert_eq!(ring.get(0), Some(&0));
|
||||
assert_eq!(ring.get(1), Some(&1));
|
||||
assert_eq!(ring.get(2), None);
|
||||
|
||||
ring.push_back(2);
|
||||
assert_eq!(ring.get(0), Some(&0));
|
||||
assert_eq!(ring.get(1), Some(&1));
|
||||
assert_eq!(ring.get(2), Some(&2));
|
||||
assert_eq!(ring.get(3), None);
|
||||
|
||||
assert_eq!(ring.pop_front(), Some(0));
|
||||
assert_eq!(ring.get(0), Some(&1));
|
||||
assert_eq!(ring.get(1), Some(&2));
|
||||
assert_eq!(ring.get(2), None);
|
||||
|
||||
assert_eq!(ring.pop_front(), Some(1));
|
||||
assert_eq!(ring.get(0), Some(&2));
|
||||
assert_eq!(ring.get(1), None);
|
||||
|
||||
assert_eq!(ring.pop_front(), Some(2));
|
||||
assert_eq!(ring.get(0), None);
|
||||
assert_eq!(ring.get(1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut ring = RingBuf::new();
|
||||
for i in range(0i, 3) {
|
||||
ring.push_back(i);
|
||||
}
|
||||
|
||||
match ring.get_mut(1) {
|
||||
Some(x) => *x = -1,
|
||||
None => ()
|
||||
};
|
||||
|
||||
assert_eq!(ring.get_mut(0), Some(&mut 0));
|
||||
assert_eq!(ring.get_mut(1), Some(&mut -1));
|
||||
assert_eq!(ring.get_mut(2), Some(&mut 2));
|
||||
assert_eq!(ring.get_mut(3), None);
|
||||
|
||||
assert_eq!(ring.pop_front(), Some(0));
|
||||
assert_eq!(ring.get_mut(0), Some(&mut -1));
|
||||
assert_eq!(ring.get_mut(1), Some(&mut 2));
|
||||
assert_eq!(ring.get_mut(2), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue