Skip to content

Commit dc930b1

Browse files
committed
add split_off to RingBuf
1 parent 80627cd commit dc930b1

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

src/libcollections/ring_buf.rs

+102
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,70 @@ impl<T> RingBuf<T> {
12891289

12901290
return elem;
12911291
}
1292+
1293+
/// Splits the collection into two at the given index.
1294+
///
1295+
/// Returns a newly allocated `Self`. `self` contains elements `[0, at)`,
1296+
/// and the returned `Self` contains elements `[at, len)`.
1297+
///
1298+
/// Note that the capacity of `self` does not change.
1299+
///
1300+
/// # Panics
1301+
///
1302+
/// Panics if `at > len`
1303+
///
1304+
/// # Examples
1305+
/// ```rust
1306+
/// let mut buf: RingBuf<_> = vec![1,2,3].into_iter().collect();
1307+
/// let buf2 = buf.split_off(1);
1308+
/// // buf = [1], buf2 = [2, 3]
1309+
/// assert_eq!(buf.len(), 1);
1310+
/// assert_eq!(buf2.len(), 2);
1311+
/// ```
1312+
#[inline]
1313+
#[unstable(feature = "collections",
1314+
reason = "new API, waiting for dust to settle")]
1315+
pub fn split_off(&mut self, at: usize) -> Self {
1316+
let len = self.len();
1317+
assert!(at <= len, "`at` out of bounds");
1318+
1319+
let other_len = len - at;
1320+
let mut other = RingBuf::with_capacity(other_len);
1321+
1322+
unsafe {
1323+
let (first_half, second_half) = self.as_slices();
1324+
1325+
let first_len = first_half.len();
1326+
let second_len = second_half.len();
1327+
if at < first_len {
1328+
// `at` lies in the first half.
1329+
let amount_in_first = first_len - at;
1330+
1331+
ptr::copy_nonoverlapping_memory(other.ptr,
1332+
first_half.as_ptr().offset(at as isize),
1333+
amount_in_first);
1334+
1335+
// just take all of the second half.
1336+
ptr::copy_nonoverlapping_memory(other.ptr.offset(amount_in_first as isize),
1337+
second_half.as_ptr(),
1338+
second_len);
1339+
} else {
1340+
// `at` lies in the second half, need to factor in the elements we skipped
1341+
// in the first half.
1342+
let offset = at - first_len;
1343+
let amount_in_second = second_len - offset;
1344+
ptr::copy_nonoverlapping_memory(other.ptr,
1345+
second_half.as_ptr().offset(offset as isize),
1346+
amount_in_second);
1347+
}
1348+
}
1349+
1350+
// Cleanup where the ends of the buffers are
1351+
self.head = self.wrap_index(self.head - other_len);
1352+
other.head = other.wrap_index(other_len);
1353+
1354+
other
1355+
}
12921356
}
12931357

12941358
impl<T: Clone> RingBuf<T> {
@@ -2711,4 +2775,42 @@ mod tests {
27112775
assert_eq!(ring.len() as i32, cap);
27122776
assert_eq!(ring.capacity() as i32, cap);
27132777
}
2778+
2779+
#[test]
2780+
fn test_split_off() {
2781+
// This test checks that every single combination of tail position, length, and
2782+
// split position is tested. Capacity 15 should be large enough to cover every case.
2783+
2784+
let mut tester = RingBuf::with_capacity(15);
2785+
// can't guarantee we got 15, so have to get what we got.
2786+
// 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else
2787+
// this test isn't covering what it wants to
2788+
let cap = tester.capacity();
2789+
2790+
// len is the length *before* splitting
2791+
for len in 0..cap {
2792+
// index to split at
2793+
for at in 0..len + 1 {
2794+
// 0, 1, 2, .., at - 1 (may be empty)
2795+
let expected_self = iter::count(0, 1).take(at).collect();
2796+
// at, at + 1, .., len - 1 (may be empty)
2797+
let expected_other = iter::count(at, 1).take(len - at).collect();
2798+
2799+
for tail_pos in 0..cap {
2800+
tester.tail = tail_pos;
2801+
tester.head = tail_pos;
2802+
for i in 0..len {
2803+
tester.push_back(i);
2804+
}
2805+
let result = tester.split_off(at);
2806+
assert!(tester.tail < tester.cap);
2807+
assert!(tester.head < tester.cap);
2808+
assert!(result.tail < result.cap);
2809+
assert!(result.head < result.cap);
2810+
assert_eq!(tester, expected_self);
2811+
assert_eq!(result, expected_other);
2812+
}
2813+
}
2814+
}
2815+
}
27142816
}

0 commit comments

Comments
 (0)