Skip to content

Commit ffc1118

Browse files
committed
auto merge of #19569 : murphm8/rust/ring_buf_insert, r=Gankro
This is a first pass at insert on RingBuf. I tried to keep it as simple as possible. I'm not sure of the performance implications of doing one copy vs. copying multiple times but moving a smaller amount of memory. I chose to stick with one copy, even if the amount of memory I have to move is larger. I believe this is part of #18424 @gankro mentioned this was missing.
2 parents 9146a91 + 40f28c7 commit ffc1118

File tree

1 file changed

+267
-1
lines changed

1 file changed

+267
-1
lines changed

src/libcollections/ring_buf.rs

+267-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,21 @@ impl<T> RingBuf<T> {
9999
/// Returns the index in the underlying buffer for a given logical element index.
100100
#[inline]
101101
fn wrap_index(&self, idx: uint) -> uint { wrap_index(idx, self.cap) }
102+
103+
/// Copies a contiguous block of memory len long from src to dst
104+
#[inline]
105+
fn copy(&self, dst: uint, src: uint, len: uint) {
106+
unsafe {
107+
debug_assert!(dst + len <= self.cap, "dst={} src={} len={} cap={}", dst, src, len,
108+
self.cap);
109+
debug_assert!(src + len <= self.cap, "dst={} src={} len={} cap={}", dst, src, len,
110+
self.cap);
111+
ptr::copy_memory(
112+
self.ptr.offset(dst as int),
113+
self.ptr.offset(src as int) as *const T,
114+
len);
115+
}
116+
}
102117
}
103118

104119
impl<T> RingBuf<T> {
@@ -648,6 +663,213 @@ impl<T> RingBuf<T> {
648663
unsafe { Some(self.buffer_read(head)) }
649664
}
650665
}
666+
667+
/// Inserts an element at position `i` within the ringbuf. Whichever
668+
/// end is closer to the insertion point will be moved to make room,
669+
/// and all the affected elements will be moved to new positions.
670+
///
671+
/// # Panics
672+
///
673+
/// Panics if `i` is greater than ringbuf's length
674+
///
675+
/// # Example
676+
/// ```rust
677+
/// use std::collections::RingBuf;
678+
///
679+
/// let mut buf = RingBuf::new();
680+
/// buf.push_back(10i);
681+
/// buf.push_back(12);
682+
/// buf.insert(1,11);
683+
/// assert_eq!(Some(&11), buf.get(1));
684+
/// ```
685+
pub fn insert(&mut self, i: uint, t: T) {
686+
assert!(i <= self.len(), "index out of bounds");
687+
if self.is_full() {
688+
self.reserve(1);
689+
debug_assert!(!self.is_full());
690+
}
691+
692+
// Move the least number of elements in the ring buffer and insert
693+
// the given object
694+
//
695+
// At most len/2 - 1 elements will be moved. O(min(n, n-i))
696+
//
697+
// There are three main cases:
698+
// Elements are contiguous
699+
// - special case when tail is 0
700+
// Elements are discontiguous and the insert is in the tail section
701+
// Elements are discontiguous and the insert is in the head section
702+
//
703+
// For each of those there are two more cases:
704+
// Insert is closer to tail
705+
// Insert is closer to head
706+
//
707+
// Key: H - self.head
708+
// T - self.tail
709+
// o - Valid element
710+
// I - Insertion element
711+
// A - The element that should be after the insertion point
712+
// M - Indicates element was moved
713+
714+
let idx = self.wrap_index(self.tail + i);
715+
716+
let distance_to_tail = i;
717+
let distance_to_head = self.len() - i;
718+
719+
let contiguous = self.tail <= self.head;
720+
721+
match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) {
722+
(true, true, _) if i == 0 => {
723+
// push_front
724+
//
725+
// T
726+
// I H
727+
// [A o o o o o o . . . . . . . . .]
728+
//
729+
// H T
730+
// [A o o o o o o o . . . . . I]
731+
//
732+
733+
self.tail = self.wrap_index(self.tail - 1);
734+
},
735+
(true, true, _) => {
736+
// contiguous, insert closer to tail:
737+
//
738+
// T I H
739+
// [. . . o o A o o o o . . . . . .]
740+
//
741+
// T H
742+
// [. . o o I A o o o o . . . . . .]
743+
// M M
744+
//
745+
// contiguous, insert closer to tail and tail is 0:
746+
//
747+
//
748+
// T I H
749+
// [o o A o o o o . . . . . . . . .]
750+
//
751+
// H T
752+
// [o I A o o o o o . . . . . . . o]
753+
// M M
754+
755+
let old_tail = self.tail;
756+
self.tail = self.wrap_index(self.tail - 1);
757+
758+
self.copy(self.tail, old_tail, 1);
759+
self.copy(old_tail, old_tail + 1, i);
760+
},
761+
(true, false, _) => {
762+
// contiguous, insert closer to head:
763+
//
764+
// T I H
765+
// [. . . o o o o A o o . . . . . .]
766+
//
767+
// T H
768+
// [. . . o o o o I A o o . . . . .]
769+
// M M M
770+
771+
let old_head = self.head;
772+
self.head = self.wrap_index(self.head + 1);
773+
self.copy(idx + 1, idx, old_head - idx);
774+
},
775+
(false, true, true) => {
776+
// discontiguous, tail section, insert closer to tail:
777+
//
778+
// H T I
779+
// [o o o o o o . . . . . o o A o o]
780+
//
781+
// H T
782+
// [o o o o o o . . . . o o I A o o]
783+
// M M
784+
785+
let old_tail = self.tail;
786+
self.tail = self.tail - 1;
787+
self.copy(self.tail, old_tail, i);
788+
},
789+
(false, false, true) => {
790+
// discontiguous, tail section, insert closer to head:
791+
//
792+
// H T I
793+
// [o o . . . . . . . o o o o o A o]
794+
//
795+
// H T
796+
// [o o o . . . . . . o o o o o I A]
797+
// M M M M
798+
799+
let old_head = self.head;
800+
self.head = self.head + 1;
801+
802+
// copy elements up to new head
803+
self.copy(1, 0, old_head);
804+
805+
// copy last element into empty spot at bottom of buffer
806+
self.copy(0, self.cap - 1, 1);
807+
808+
// move elements from idx to end forward not including ^ element
809+
self.copy(idx + 1, idx, self.cap - 1 - idx);
810+
},
811+
(false, true, false) if idx == 0 => {
812+
// discontiguous, head section, insert is closer to tail,
813+
// and is at index zero in the internal buffer:
814+
//
815+
// I H T
816+
// [A o o o o o o o o o . . . o o o]
817+
//
818+
// H T
819+
// [A o o o o o o o o o . . o o o I]
820+
// M M M
821+
822+
let old_tail = self.tail;
823+
self.tail = self.tail - 1;
824+
// copy elements up to new tail
825+
self.copy(old_tail - 1, old_tail, i);
826+
827+
// copy last element into empty spot at bottom of buffer
828+
self.copy(self.cap - 1, 0, 1);
829+
},
830+
(false, true, false) => {
831+
// discontiguous, head section, insert closer to tail:
832+
//
833+
// I H T
834+
// [o o o A o o o o o o . . . o o o]
835+
//
836+
// H T
837+
// [o o I A o o o o o o . . o o o o]
838+
// M M M M M M
839+
840+
let old_tail = self.tail;
841+
self.tail = self.tail - 1;
842+
// copy elements up to new tail
843+
self.copy(old_tail - 1, old_tail, i);
844+
845+
// copy last element into empty spot at bottom of buffer
846+
self.copy(self.cap - 1, 0, 1);
847+
848+
// move elements from idx-1 to end forward not including ^ element
849+
self.copy(0, 1, idx - 1);
850+
}
851+
(false, false, false) => {
852+
// discontiguous, head section, insert closer to head:
853+
//
854+
// I H T
855+
// [o o o o A o o . . . . . . o o o]
856+
//
857+
// H T
858+
// [o o o o I A o o . . . . . o o o]
859+
// M M M
860+
861+
let old_head = self.head;
862+
self.head = self.head + 1;
863+
self.copy(idx + 1, idx, old_head - idx);
864+
}
865+
}
866+
867+
// tail might've been changed so we need to recalculate
868+
let new_idx = self.wrap_index(self.tail + i);
869+
unsafe {
870+
self.buffer_write(new_idx, t);
871+
}
872+
}
651873
}
652874

653875
/// Returns the index in the underlying buffer for a given logical element index.
@@ -878,6 +1100,7 @@ impl<T: fmt::Show> fmt::Show for RingBuf<T> {
8781100

8791101
#[cfg(test)]
8801102
mod tests {
1103+
use core::iter;
8811104
use self::Taggy::*;
8821105
use self::Taggypar::*;
8831106
use std::fmt::Show;
@@ -1102,7 +1325,6 @@ mod tests {
11021325
})
11031326
}
11041327

1105-
11061328
#[deriving(Clone, PartialEq, Show)]
11071329
enum Taggy {
11081330
One(int),
@@ -1666,4 +1888,48 @@ mod tests {
16661888
assert_eq!(ring.get_mut(1), Some(&mut 2));
16671889
assert_eq!(ring.get_mut(2), None);
16681890
}
1891+
1892+
#[test]
1893+
fn test_insert() {
1894+
// This test checks that every single combination of tail position, length, and
1895+
// insertion position is tested. Capacity 7 should be large enough to cover every case.
1896+
1897+
let mut tester = RingBuf::with_capacity(7);
1898+
// can't guarantee we got 7, so have to get what we got.
1899+
// 7 would be great, but we will definitely get 2^k - 1, for k >= 3, or else
1900+
// this test isn't covering what it wants to
1901+
let cap = tester.capacity();
1902+
1903+
1904+
// len is the length *after* insertion
1905+
for len in range(1, cap) {
1906+
// 0, 1, 2, .., len - 1
1907+
let expected = iter::count(0, 1).take(len).collect();
1908+
for tail_pos in range(0, cap) {
1909+
for to_insert in range(0, len) {
1910+
tester.tail = tail_pos;
1911+
tester.head = tail_pos;
1912+
for i in range(0, len) {
1913+
if i != to_insert {
1914+
tester.push_back(i);
1915+
}
1916+
}
1917+
tester.insert(to_insert, to_insert);
1918+
assert_eq!(tester, expected);
1919+
}
1920+
}
1921+
}
1922+
}
1923+
1924+
#[test]
1925+
fn test_front() {
1926+
let mut ring = RingBuf::new();
1927+
ring.push_back(10i);
1928+
ring.push_back(20i);
1929+
assert_eq!(ring.front(), Some(&10));
1930+
ring.pop_front();
1931+
assert_eq!(ring.front(), Some(&20));
1932+
ring.pop_front();
1933+
assert_eq!(ring.front(), None);
1934+
}
16691935
}

0 commit comments

Comments
 (0)