diff --git a/src/libcollections/ring_buf.rs b/src/libcollections/ring_buf.rs index 2751156429083..6f396f539403f 100644 --- a/src/libcollections/ring_buf.rs +++ b/src/libcollections/ring_buf.rs @@ -99,6 +99,21 @@ impl RingBuf { /// 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) } + + /// Copies a contiguous block of memory len long from src to dst + #[inline] + fn copy(&self, dst: uint, src: uint, len: uint) { + unsafe { + debug_assert!(dst + len <= self.cap, "dst={} src={} len={} cap={}", dst, src, len, + self.cap); + debug_assert!(src + len <= self.cap, "dst={} src={} len={} cap={}", dst, src, len, + self.cap); + ptr::copy_memory( + self.ptr.offset(dst as int), + self.ptr.offset(src as int) as *const T, + len); + } + } } impl RingBuf { @@ -648,6 +663,213 @@ impl RingBuf { unsafe { Some(self.buffer_read(head)) } } } + + /// Inserts an element at position `i` within the ringbuf. Whichever + /// end is closer to the insertion point will be moved to make room, + /// and all the affected elements will be moved to new positions. + /// + /// # Panics + /// + /// Panics if `i` is greater than ringbuf's length + /// + /// # Example + /// ```rust + /// use std::collections::RingBuf; + /// + /// let mut buf = RingBuf::new(); + /// buf.push_back(10i); + /// buf.push_back(12); + /// buf.insert(1,11); + /// assert_eq!(Some(&11), buf.get(1)); + /// ``` + pub fn insert(&mut self, i: uint, t: T) { + assert!(i <= self.len(), "index out of bounds"); + if self.is_full() { + self.reserve(1); + debug_assert!(!self.is_full()); + } + + // Move the least number of elements in the ring buffer and insert + // the given object + // + // At most len/2 - 1 elements will be moved. O(min(n, n-i)) + // + // There are three main cases: + // Elements are contiguous + // - special case when tail is 0 + // Elements are discontiguous and the insert is in the tail section + // Elements are discontiguous and the insert is in the head section + // + // For each of those there are two more cases: + // Insert is closer to tail + // Insert is closer to head + // + // Key: H - self.head + // T - self.tail + // o - Valid element + // I - Insertion element + // A - The element that should be after the insertion point + // M - Indicates element was moved + + let idx = self.wrap_index(self.tail + i); + + let distance_to_tail = i; + let distance_to_head = self.len() - i; + + let contiguous = self.tail <= self.head; + + match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { + (true, true, _) if i == 0 => { + // push_front + // + // T + // I H + // [A o o o o o o . . . . . . . . .] + // + // H T + // [A o o o o o o o . . . . . I] + // + + self.tail = self.wrap_index(self.tail - 1); + }, + (true, true, _) => { + // contiguous, insert closer to tail: + // + // T I H + // [. . . o o A o o o o . . . . . .] + // + // T H + // [. . o o I A o o o o . . . . . .] + // M M + // + // contiguous, insert closer to tail and tail is 0: + // + // + // T I H + // [o o A o o o o . . . . . . . . .] + // + // H T + // [o I A o o o o o . . . . . . . o] + // M M + + let old_tail = self.tail; + self.tail = self.wrap_index(self.tail - 1); + + self.copy(self.tail, old_tail, 1); + self.copy(old_tail, old_tail + 1, i); + }, + (true, false, _) => { + // contiguous, insert closer to head: + // + // T I H + // [. . . o o o o A o o . . . . . .] + // + // T H + // [. . . o o o o I A o o . . . . .] + // M M M + + let old_head = self.head; + self.head = self.wrap_index(self.head + 1); + self.copy(idx + 1, idx, old_head - idx); + }, + (false, true, true) => { + // discontiguous, tail section, insert closer to tail: + // + // H T I + // [o o o o o o . . . . . o o A o o] + // + // H T + // [o o o o o o . . . . o o I A o o] + // M M + + let old_tail = self.tail; + self.tail = self.tail - 1; + self.copy(self.tail, old_tail, i); + }, + (false, false, true) => { + // discontiguous, tail section, insert closer to head: + // + // H T I + // [o o . . . . . . . o o o o o A o] + // + // H T + // [o o o . . . . . . o o o o o I A] + // M M M M + + let old_head = self.head; + self.head = self.head + 1; + + // copy elements up to new head + self.copy(1, 0, old_head); + + // copy last element into empty spot at bottom of buffer + self.copy(0, self.cap - 1, 1); + + // move elements from idx to end forward not including ^ element + self.copy(idx + 1, idx, self.cap - 1 - idx); + }, + (false, true, false) if idx == 0 => { + // discontiguous, head section, insert is closer to tail, + // and is at index zero in the internal buffer: + // + // I H T + // [A o o o o o o o o o . . . o o o] + // + // H T + // [A o o o o o o o o o . . o o o I] + // M M M + + let old_tail = self.tail; + self.tail = self.tail - 1; + // copy elements up to new tail + self.copy(old_tail - 1, old_tail, i); + + // copy last element into empty spot at bottom of buffer + self.copy(self.cap - 1, 0, 1); + }, + (false, true, false) => { + // discontiguous, head section, insert closer to tail: + // + // I H T + // [o o o A o o o o o o . . . o o o] + // + // H T + // [o o I A o o o o o o . . o o o o] + // M M M M M M + + let old_tail = self.tail; + self.tail = self.tail - 1; + // copy elements up to new tail + self.copy(old_tail - 1, old_tail, i); + + // copy last element into empty spot at bottom of buffer + self.copy(self.cap - 1, 0, 1); + + // move elements from idx-1 to end forward not including ^ element + self.copy(0, 1, idx - 1); + } + (false, false, false) => { + // discontiguous, head section, insert closer to head: + // + // I H T + // [o o o o A o o . . . . . . o o o] + // + // H T + // [o o o o I A o o . . . . . o o o] + // M M M + + let old_head = self.head; + self.head = self.head + 1; + self.copy(idx + 1, idx, old_head - idx); + } + } + + // tail might've been changed so we need to recalculate + let new_idx = self.wrap_index(self.tail + i); + unsafe { + self.buffer_write(new_idx, t); + } + } } /// Returns the index in the underlying buffer for a given logical element index. @@ -878,6 +1100,7 @@ impl fmt::Show for RingBuf { #[cfg(test)] mod tests { + use core::iter; use self::Taggy::*; use self::Taggypar::*; use std::fmt::Show; @@ -1102,7 +1325,6 @@ mod tests { }) } - #[deriving(Clone, PartialEq, Show)] enum Taggy { One(int), @@ -1666,4 +1888,48 @@ mod tests { assert_eq!(ring.get_mut(1), Some(&mut 2)); assert_eq!(ring.get_mut(2), None); } + + #[test] + fn test_insert() { + // This test checks that every single combination of tail position, length, and + // insertion position is tested. Capacity 7 should be large enough to cover every case. + + let mut tester = RingBuf::with_capacity(7); + // can't guarantee we got 7, so have to get what we got. + // 7 would be great, but we will definitely get 2^k - 1, for k >= 3, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + + // len is the length *after* insertion + for len in range(1, cap) { + // 0, 1, 2, .., len - 1 + let expected = iter::count(0, 1).take(len).collect(); + for tail_pos in range(0, cap) { + for to_insert in range(0, len) { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in range(0, len) { + if i != to_insert { + tester.push_back(i); + } + } + tester.insert(to_insert, to_insert); + assert_eq!(tester, expected); + } + } + } + } + + #[test] + fn test_front() { + let mut ring = RingBuf::new(); + ring.push_back(10i); + ring.push_back(20i); + assert_eq!(ring.front(), Some(&10)); + ring.pop_front(); + assert_eq!(ring.front(), Some(&20)); + ring.pop_front(); + assert_eq!(ring.front(), None); + } }