compact_strings/
fixed_compact_bytestrings.rs

1use core::{fmt::Debug, ops::Index};
2
3use alloc::vec::Vec;
4
5use crate::FixedCompactStrings;
6
7/// An even more compact but limited representation of a list of bytestrings.
8///
9/// Strings are stored contiguously in a vector of bytes, with their starting indices
10/// being stored separately.
11///
12/// Limitations include being unable to mutate bytestrings stored in the vector.
13///
14/// # Examples
15/// ```
16/// # use compact_strings::FixedCompactBytestrings;
17/// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
18///
19/// cmpbytes.push(b"One");
20/// cmpbytes.push(b"Two");
21/// cmpbytes.push(b"Three");
22///
23/// cmpbytes.remove(1);
24///
25/// assert_eq!(cmpbytes.get(0), Some(b"One".as_slice()));
26/// assert_eq!(cmpbytes.get(1), Some(b"Three".as_slice()));
27/// assert_eq!(cmpbytes.get(2), None);
28/// ```
29pub struct FixedCompactBytestrings {
30    pub(crate) data: Vec<u8>,
31    pub(crate) starts: Vec<usize>,
32}
33
34impl FixedCompactBytestrings {
35    /// Constructs a new, empty [`FixedCompactBytestrings`].
36    ///
37    /// The [`FixedCompactBytestrings`] will not allocate until bytestrings are pushed into it.
38    ///
39    /// # Examples
40    /// ```
41    /// # use compact_strings::FixedCompactBytestrings;
42    /// let mut cmpbytes = FixedCompactBytestrings::new();
43    /// ```
44    #[must_use]
45    pub const fn new() -> Self {
46        Self {
47            data: Vec::new(),
48            starts: Vec::new(),
49        }
50    }
51
52    /// Constructs a new, empty [`FixedCompactBytestrings`] with at least the specified capacities in each
53    /// vector.
54    ///
55    /// - `data_capacity`: The capacity of the data vector where the bytes of the bytestrings are stored.
56    /// - `capacity_meta`: The capacity of the meta vector where the starting indices
57    /// of the bytestrings are stored.
58    ///
59    /// The [`FixedCompactBytestrings`] will be able to hold at least *`data_capacity`* bytes worth of bytestrings
60    /// without reallocating the data vector, and at least *`capacity_meta`* of starting indices
61    /// without reallocating the meta vector. This method is allowed to allocate for more bytes
62    /// than the capacities. If a capacity is 0, the vector will not allocate.
63    ///
64    /// It is important to note that although the data and meta vectors have the
65    /// minimum capacities specified, they will have a zero *length*.
66    ///
67    /// If it is important to know the exact allocated capacity of the data vector, always use the
68    /// [`capacity`] method after construction.
69    ///
70    /// [`capacity`]: FixedCompactBytestrings::capacity
71    ///
72    /// # Examples
73    /// ```
74    /// # use compact_strings::FixedCompactBytestrings;
75    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
76    ///
77    /// assert_eq!(cmpbytes.len(), 0);
78    /// assert!(cmpbytes.capacity() >= 20);
79    /// assert!(cmpbytes.capacity_meta() >= 3);
80    /// ```
81    #[must_use]
82    pub fn with_capacity(data_capacity: usize, capacity_meta: usize) -> Self {
83        Self {
84            data: Vec::with_capacity(data_capacity),
85            starts: Vec::with_capacity(capacity_meta),
86        }
87    }
88
89    /// Appends a bytestring to the back of the [`FixedCompactBytestrings`].
90    ///
91    /// # Examples
92    /// ```
93    /// # use compact_strings::FixedCompactBytestrings;
94    /// let mut cmpbytes = FixedCompactBytestrings::new();
95    /// cmpbytes.push(b"One");
96    /// cmpbytes.push(b"Two");
97    /// cmpbytes.push(b"Three");
98    ///
99    /// assert_eq!(cmpbytes.get(0), Some(b"One".as_slice()));
100    /// assert_eq!(cmpbytes.get(1), Some(b"Two".as_slice()));
101    /// assert_eq!(cmpbytes.get(2), Some(b"Three".as_slice()));
102    /// assert_eq!(cmpbytes.get(3), None);
103    /// ```
104    pub fn push<S>(&mut self, bytestring: S)
105    where
106        S: AsRef<[u8]>,
107    {
108        let bytestr = bytestring.as_ref();
109        self.starts.push(self.data.len());
110        self.data.extend_from_slice(bytestr);
111    }
112
113    /// Returns a reference to the bytestring stored in the [`FixedCompactBytestrings`] at that position.
114    ///
115    /// # Examples
116    /// ```
117    /// # use compact_strings::FixedCompactBytestrings;
118    /// let mut cmpbytes = FixedCompactBytestrings::new();
119    /// cmpbytes.push(b"One");
120    /// cmpbytes.push(b"Two");
121    /// cmpbytes.push(b"Three");
122    ///
123    /// assert_eq!(cmpbytes.get(0), Some(b"One".as_slice()));
124    /// assert_eq!(cmpbytes.get(1), Some(b"Two".as_slice()));
125    /// assert_eq!(cmpbytes.get(2), Some(b"Three".as_slice()));
126    /// assert_eq!(cmpbytes.get(3), None);
127    /// ```
128    #[must_use]
129    pub fn get(&self, index: usize) -> Option<&[u8]> {
130        let &start = self.starts.get(index)?;
131        let &next = self
132            .starts
133            .get(index.checked_add(1)?)
134            .unwrap_or(&self.data.len());
135
136        if cfg!(feature = "no_unsafe") {
137            self.data.get(start..next)
138        } else {
139            unsafe { Some(self.data.get_unchecked(start..next)) }
140        }
141    }
142
143    /// Returns a reference to the bytestring stored in the [`FixedCompactBytestrings`] at that position, without
144    /// doing bounds checking.
145    ///
146    /// # Safety
147    /// Calling this method with an out-of-bounds index is undefined behavior even if the resulting reference is not used.
148    ///
149    /// # Examples
150    /// ```
151    /// # use compact_strings::FixedCompactBytestrings;
152    /// let mut cmpbytes = FixedCompactBytestrings::new();
153    /// cmpbytes.push(b"One");
154    /// cmpbytes.push(b"Two");
155    /// cmpbytes.push(b"Three");
156    ///
157    /// unsafe {
158    ///     assert_eq!(cmpbytes.get_unchecked(0), b"One".as_slice());
159    ///     assert_eq!(cmpbytes.get_unchecked(1), b"Two".as_slice());
160    ///     assert_eq!(cmpbytes.get_unchecked(2), b"Three".as_slice());
161    /// }
162    /// ```
163    #[must_use]
164    #[cfg(not(feature = "no_unsafe"))]
165    pub unsafe fn get_unchecked(&self, index: usize) -> &[u8] {
166        let start = *self.starts.get_unchecked(index);
167        let next = *self.starts.get(index + 1).unwrap_or(&self.data.len());
168        self.data.get_unchecked(start..next)
169    }
170
171    /// Returns the number of bytestrings in the [`FixedCompactBytestrings`], also referred to as its 'length'.
172    ///
173    /// # Examples
174    /// ```
175    /// # use compact_strings::FixedCompactBytestrings;
176    /// let mut cmpbytes = FixedCompactBytestrings::new();
177    ///
178    /// cmpbytes.push(b"One");
179    /// cmpbytes.push(b"Two");
180    /// cmpbytes.push(b"Three");
181    ///
182    /// assert_eq!(cmpbytes.len(), 3);
183    /// ```
184    #[inline]
185    #[must_use]
186    pub fn len(&self) -> usize {
187        self.starts.len()
188    }
189
190    /// Returns true if the [`FixedCompactBytestrings`] contains no bytestrings.
191    ///
192    /// # Examples
193    /// ```
194    /// # use compact_strings::FixedCompactBytestrings;
195    /// let mut cmpbytes = FixedCompactBytestrings::new();
196    /// assert!(cmpbytes.is_empty());
197    ///
198    /// cmpbytes.push(b"One");
199    ///
200    /// assert!(!cmpbytes.is_empty());
201    /// ```
202    #[inline]
203    #[must_use]
204    pub fn is_empty(&self) -> bool {
205        self.len() == 0
206    }
207
208    /// Returns the number of bytes the data vector can store without reallocating.
209    ///
210    /// # Examples
211    /// ```
212    /// # use compact_strings::FixedCompactBytestrings;
213    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
214    ///
215    /// cmpbytes.push(b"One");
216    ///
217    /// assert!(cmpbytes.capacity() >= 20);
218    /// ```
219    #[inline]
220    #[must_use]
221    pub fn capacity(&self) -> usize {
222        self.data.capacity()
223    }
224
225    /// Returns the number of starting indices can store without reallocating.
226    ///
227    /// # Examples
228    /// ```
229    /// # use compact_strings::FixedCompactBytestrings;
230    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
231    ///
232    /// cmpbytes.push(b"One");
233    /// cmpbytes.push(b"Two");
234    /// cmpbytes.push(b"Three");
235    /// assert!(cmpbytes.capacity_meta() >= 3);
236    ///
237    /// cmpbytes.push(b"Three");
238    /// assert!(cmpbytes.capacity_meta() > 3);
239    /// ```
240    #[inline]
241    #[must_use]
242    pub fn capacity_meta(&self) -> usize {
243        self.starts.capacity()
244    }
245
246    /// Clears the [`FixedCompactBytestrings`], removing all bytestrings.
247    ///
248    /// Note that this method has no effect on the allocated capacity of the vectors.
249    ///
250    /// # Examples
251    /// ```
252    /// # use compact_strings::FixedCompactBytestrings;
253    /// let mut cmpbytes = FixedCompactBytestrings::new();
254    ///
255    /// cmpbytes.push(b"One");
256    /// cmpbytes.push(b"Two");
257    /// cmpbytes.push(b"Three");
258    /// cmpbytes.clear();
259    ///
260    /// assert!(cmpbytes.is_empty());
261    /// ```
262    pub fn clear(&mut self) {
263        self.data.clear();
264        self.starts.clear();
265    }
266
267    /// Shrinks the capacity of the data vector, which stores the bytes of the held bytestrings, as much as possible.
268    ///
269    /// It will drop down as close as possible to the length but the allocator
270    /// may still inform the vector that there is space for a few more elements.
271    ///
272    /// # Examples
273    /// ```
274    /// # use compact_strings::FixedCompactBytestrings;
275    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
276    ///
277    /// cmpbytes.push(b"One");
278    /// cmpbytes.push(b"Two");
279    /// cmpbytes.push(b"Three");
280    ///
281    /// assert!(cmpbytes.capacity() >= 20);
282    /// cmpbytes.shrink_to_fit();
283    /// assert!(cmpbytes.capacity() >= 3);
284    /// ```
285    #[inline]
286    pub fn shrink_to_fit(&mut self) {
287        self.data.shrink_to_fit();
288    }
289
290    /// Shrinks the capacity of the info vector, which stores the starting indices of
291    /// the held bytestrings, as much as possible.
292    ///
293    /// It will drop down as close as possible to the length but the allocator
294    /// may still inform the vector that there is space for a few more elements.
295    ///
296    /// # Examples
297    /// ```
298    /// # use compact_strings::FixedCompactBytestrings;
299    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 10);
300    ///
301    /// cmpbytes.push(b"One");
302    /// cmpbytes.push(b"Two");
303    /// cmpbytes.push(b"Three");
304    ///
305    /// assert!(cmpbytes.capacity_meta() >= 10);
306    /// cmpbytes.shrink_to_fit();
307    /// assert!(cmpbytes.capacity_meta() >= 3);
308    /// ```
309    #[inline]
310    pub fn shrink_meta_to_fit(&mut self) {
311        self.starts.shrink_to_fit();
312    }
313
314    /// Shrinks the capacity of the data vector, which stores the bytes of the held bytestrings, with a lower bound.
315    ///
316    /// The capacity will remain at least as large as both the length and the supplied value.
317    ///
318    /// If the current capacity is less than the lower limit, this is a no-op.
319    ///
320    /// # Examples
321    /// ```
322    /// # use compact_strings::FixedCompactBytestrings;
323    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 4);
324    ///
325    /// cmpbytes.push(b"One");
326    /// cmpbytes.push(b"Two");
327    /// cmpbytes.push(b"Three");
328    ///
329    /// assert!(cmpbytes.capacity() >= 20);
330    /// cmpbytes.shrink_to(4);
331    /// assert!(cmpbytes.capacity() >= 4);
332    /// ```
333    #[inline]
334    pub fn shrink_to(&mut self, min_capacity: usize) {
335        self.data.shrink_to(min_capacity);
336    }
337
338    /// Shrinks the capacity of the meta vector, which starting indices of the held bytestrings,
339    /// with a lower bound.
340    ///
341    /// The capacity will remain at least as large as both the length and the supplied value.
342    ///
343    /// If the current capacity is less than the lower limit, this is a no-op.
344    ///
345    /// # Examples
346    /// ```
347    /// # use compact_strings::FixedCompactBytestrings;
348    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 10);
349    ///
350    /// cmpbytes.push(b"One");
351    /// cmpbytes.push(b"Two");
352    /// cmpbytes.push(b"Three");
353    ///
354    /// assert!(cmpbytes.capacity_meta() >= 10);
355    /// cmpbytes.shrink_meta_to(4);
356    /// assert!(cmpbytes.capacity_meta() >= 4);
357    /// ```
358    #[inline]
359    pub fn shrink_meta_to(&mut self, min_capacity: usize) {
360        self.starts.shrink_to(min_capacity);
361    }
362
363    /// Removes the bytes of the bytestring and data pointing to the bytestring is stored.
364    ///
365    /// Note: This does not shrink the vectors where the bytes of the bytestring and data to the bytestring
366    /// are stored. You may shrink the data vector with [`shrink_to`] and [`shrink_to_fit`] and the
367    /// meta vector with [`shrink_meta_to`] and [`shrink_meta_to_fit`].
368    ///
369    /// Note: Because this shifts over the remaining elements in both data and meta vectors, it
370    /// has a worst-case performance of *O*(*n*).
371    ///
372    /// [`shrink_to`]: FixedCompactBytestrings::shrink_to
373    /// [`shrink_to_fit`]: FixedCompactBytestrings::shrink_to_fit
374    /// [`shrink_meta_to`]: FixedCompactBytestrings::shrink_meta_to
375    /// [`shrink_meta_to_fit`]: FixedCompactBytestrings::shrink_meta_to_fit
376    ///
377    /// # Examples
378    /// ```
379    /// # use compact_strings::FixedCompactBytestrings;
380    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
381    ///
382    /// cmpbytes.push(b"One");
383    /// cmpbytes.push(b"Two");
384    /// cmpbytes.push(b"Three");
385    ///
386    /// cmpbytes.remove(1);
387    ///
388    /// assert_eq!(cmpbytes.get(0), Some(b"One".as_slice()));
389    /// assert_eq!(cmpbytes.get(1), Some(b"Three".as_slice()));
390    /// assert_eq!(cmpbytes.get(2), None);
391    /// ```
392    #[track_caller]
393    pub fn remove(&mut self, index: usize) {
394        #[cold]
395        #[inline(never)]
396        #[track_caller]
397        fn assert_failed(index: usize, len: usize) -> ! {
398            panic!("removal index (is {index}) should be < len (is {len})");
399        }
400
401        let len = self.len();
402        if index >= len {
403            assert_failed(index, len);
404        }
405
406        let inner_len = self.data.len();
407        let start = self.starts.remove(index);
408        let next = *self.starts.get(index).unwrap_or(&inner_len);
409        let len = next - start;
410
411        for s in self.starts.iter_mut().skip(index) {
412            *s -= len;
413        }
414
415        if cfg!(feature = "no_unsafe") {
416            self.data.copy_within(start + len..inner_len, start);
417        } else {
418            unsafe {
419                let ptr = self.data.as_mut_ptr().add(start);
420
421                core::ptr::copy(ptr.add(len), ptr, inner_len - start - len);
422
423                self.data.set_len(inner_len - len);
424            }
425        }
426    }
427
428    /// Returns an iterator over the slice.
429    ///
430    /// The iterator yields all items from start to end.
431    ///
432    /// # Examples
433    ///
434    /// ```
435    /// # use compact_strings::FixedCompactBytestrings;
436    /// let mut cmpbytes = FixedCompactBytestrings::with_capacity(20, 3);
437    /// cmpbytes.push(b"One");
438    /// cmpbytes.push(b"Two");
439    /// cmpbytes.push(b"Three");
440    /// let mut iterator = cmpbytes.iter();
441    ///
442    /// assert_eq!(iterator.next(), Some(b"One".as_slice()));
443    /// assert_eq!(iterator.next(), Some(b"Two".as_slice()));
444    /// assert_eq!(iterator.next(), Some(b"Three".as_slice()));
445    /// assert_eq!(iterator.next(), None);
446    /// ```
447    #[inline]
448    pub fn iter(&self) -> Iter<'_> {
449        Iter::new(self)
450    }
451}
452
453impl Clone for FixedCompactBytestrings {
454    fn clone(&self) -> Self {
455        let mut data = Vec::with_capacity(self.starts.windows(2).map(|s| s[1] - s[0]).sum());
456        let mut meta = Vec::with_capacity(self.starts.len());
457
458        for bytes in self {
459            meta.push(bytes.len());
460            data.extend_from_slice(bytes);
461        }
462
463        Self { data, starts: meta }
464    }
465}
466
467impl PartialEq for FixedCompactBytestrings {
468    fn eq(&self, other: &Self) -> bool {
469        let len = self.len();
470        if len != other.len() {
471            return false;
472        }
473
474        for idx in 0..len {
475            if self[idx] != other[idx] {
476                return false;
477            }
478        }
479
480        true
481    }
482}
483
484impl Debug for FixedCompactBytestrings {
485    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
486        f.debug_list().entries(self.iter()).finish()
487    }
488}
489
490impl<S> Extend<S> for FixedCompactBytestrings
491where
492    S: AsRef<[u8]>,
493{
494    #[inline]
495    fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
496        for s in iter {
497            self.push(&s);
498        }
499    }
500}
501
502impl Index<usize> for FixedCompactBytestrings {
503    type Output = [u8];
504
505    #[inline]
506    fn index(&self, index: usize) -> &Self::Output {
507        self.get(index).unwrap()
508    }
509}
510
511/// Iterator over bytestrings in a [`FixedCompactBytestrings`]
512///
513/// # Examples
514/// ```
515/// # use compact_strings::FixedCompactBytestrings;
516/// let mut cmpbytes = FixedCompactBytestrings::new();
517/// cmpbytes.push(b"One");
518/// cmpbytes.push(b"Two");
519/// cmpbytes.push(b"Three");
520///
521/// let mut iter = cmpbytes.into_iter();
522/// assert_eq!(iter.next(), Some(b"One".as_slice()));
523/// assert_eq!(iter.next(), Some(b"Two".as_slice()));
524/// assert_eq!(iter.next(), Some(b"Three".as_slice()));
525/// assert_eq!(iter.next(), None);
526/// ```
527#[must_use = "Iterators are lazy and do nothing unless consumed"]
528pub struct Iter<'a> {
529    data: &'a [u8],
530    starts: core::slice::Iter<'a, usize>,
531}
532
533impl<'a> Iter<'a> {
534    #[inline]
535    pub fn new(inner: &'a FixedCompactBytestrings) -> Self {
536        Self {
537            data: &inner.data,
538            starts: inner.starts.iter(),
539        }
540    }
541}
542
543impl<'a> Iterator for Iter<'a> {
544    type Item = &'a [u8];
545
546    fn next(&mut self) -> Option<Self::Item> {
547        let &start = self.starts.next()?;
548        let &end = self.starts.clone().next().unwrap_or(&self.data.len());
549
550        if cfg!(feature = "no_unsafe") {
551            self.data.get(start..end)
552        } else {
553            unsafe { Some(self.data.get_unchecked(start..end)) }
554        }
555    }
556
557    fn nth(&mut self, n: usize) -> Option<Self::Item> {
558        let &start = self.starts.nth(n)?;
559        let &end = self.starts.clone().next().unwrap_or(&self.data.len());
560
561        if cfg!(feature = "no_unsafe") {
562            self.data.get(start..end)
563        } else {
564            unsafe { Some(self.data.get_unchecked(start..end)) }
565        }
566    }
567
568    #[inline]
569    fn count(self) -> usize
570    where
571        Self: Sized,
572    {
573        self.len()
574    }
575
576    #[inline]
577    fn last(mut self) -> Option<Self::Item>
578    where
579        Self: Sized,
580    {
581        self.next_back()
582    }
583
584    #[inline]
585    fn size_hint(&self) -> (usize, Option<usize>) {
586        self.starts.size_hint()
587    }
588}
589
590impl<'a> DoubleEndedIterator for Iter<'a> {
591    fn next_back(&mut self) -> Option<Self::Item> {
592        let &start = self.starts.next_back()?;
593        let end = self.data.len();
594
595        let out = if cfg!(feature = "no_unsafe") {
596            self.data.get(start..end)
597        } else {
598            unsafe { Some(self.data.get_unchecked(start..end)) }
599        };
600        self.data = &self.data[..start];
601
602        out
603    }
604
605    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
606        let mut fork = self.starts.clone();
607        let &start = self.starts.nth_back(n)?;
608        let end = if n == 0 {
609            self.data.len()
610        } else {
611            *fork.nth_back(n - 1)?
612        };
613
614        let out = if cfg!(feature = "no_unsafe") {
615            self.data.get(start..end)
616        } else {
617            unsafe { Some(self.data.get_unchecked(start..end)) }
618        };
619        self.data = &self.data[..start];
620
621        out
622    }
623}
624
625impl ExactSizeIterator for Iter<'_> {
626    #[inline]
627    fn len(&self) -> usize {
628        self.starts.len()
629    }
630}
631
632impl<'a> IntoIterator for &'a FixedCompactBytestrings {
633    type Item = &'a [u8];
634
635    type IntoIter = Iter<'a>;
636
637    #[inline]
638    fn into_iter(self) -> Self::IntoIter {
639        self.iter()
640    }
641}
642
643impl<S> FromIterator<S> for FixedCompactBytestrings
644where
645    S: AsRef<[u8]>,
646{
647    fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
648        let iter = iter.into_iter();
649        let meta_capacity = match iter.size_hint() {
650            (a, Some(b)) if a == b => a,
651            _ => 0,
652        };
653
654        let mut out = FixedCompactBytestrings::with_capacity(0, meta_capacity);
655        for s in iter {
656            out.push(s);
657        }
658
659        out
660    }
661}
662
663impl<S, I> From<I> for FixedCompactBytestrings
664where
665    S: AsRef<[u8]>,
666    I: IntoIterator<Item = S>,
667{
668    #[inline]
669    fn from(value: I) -> Self {
670        FromIterator::from_iter(value)
671    }
672}
673
674impl From<FixedCompactStrings> for FixedCompactBytestrings {
675    fn from(value: FixedCompactStrings) -> Self {
676        value.0
677    }
678}
679
680#[cfg(test)]
681mod tests {
682    use crate::FixedCompactBytestrings;
683
684    #[test]
685    fn exact_size_iterator() {
686        let mut cmpbytes = FixedCompactBytestrings::new();
687
688        cmpbytes.push(b"One");
689        cmpbytes.push(b"Two");
690        cmpbytes.push(b"Three");
691
692        let mut iter = cmpbytes.iter();
693        assert_eq!(iter.len(), 3);
694        let _ = iter.next();
695        assert_eq!(iter.len(), 2);
696        let _ = iter.next();
697        assert_eq!(iter.len(), 1);
698        let _ = iter.next();
699        assert_eq!(iter.len(), 0);
700        let _ = iter.next();
701        assert_eq!(iter.len(), 0);
702    }
703
704    #[test]
705    fn double_ended_iterator() {
706        let mut cmpbytes = FixedCompactBytestrings::new();
707
708        cmpbytes.push(b"One");
709        cmpbytes.push(b"Two");
710        cmpbytes.push(b"Three");
711        cmpbytes.push(b"Four");
712
713        let mut iter = cmpbytes.iter();
714        assert_eq!(iter.next(), Some(b"One".as_slice()));
715        assert_eq!(iter.next_back(), Some(b"Four".as_slice()));
716        assert_eq!(iter.next(), Some(b"Two".as_slice()));
717        assert_eq!(iter.next_back(), Some(b"Three".as_slice()));
718        assert_eq!(iter.next(), None);
719        assert_eq!(iter.next_back(), None);
720    }
721}