diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..ea84e0b --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,42 @@ +name: CI pipeline + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Build and test + run: cargo test --verbose + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: clippy + - name: Run clippy + run: cargo clippy --all-targets -- -D warnings + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: rustfmt + - name: Run rustfmt + run: cargo fmt --all -- --check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7fad178 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Release-plz + +on: + push: + branches: + - master + +jobs: + + # Release unpublished packages. + release: + name: Release-plz release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - &checkout + name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + - &install-rust + name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Create a PR with the new versions and changelog, preparing the next release. + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - *checkout + - *install-rust + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index ac78129..552cf6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5 +3,5 @@ version = 4 [[package]] -name = "minivec" +name = "lessvec" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6505ecf..72f676e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,18 @@ [package] -name = "minivec" +name = "lessvec" version = "0.1.0" edition = "2024" description = "A custom Vec implementation using the Rust standard library." +authors = ["Ayush Chauhan (bakayu) "] readme = "README.md" +categories = ["data-structures", "collections"] license = "MIT" +repository = "https://github.com/bakayu/lessvec.git" +exclude = ["target", ".github"] +homepage = "https://github.com/bakayu/lessvec" +documentation = "https://docs.rs/lessvec" [dependencies] + +[package.metadata.docs.rs] +all-features = true diff --git a/README.md b/README.md index e69de29..021353c 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,20 @@ +# lessvec + +A minimal, educational Vec-like collection implemented with only the Rust standard library. + +[![crates.io](https://img.shields.io/crates/v/lessvec.svg)](https://crates.io/crates/lessvec) [![docs.rs](https://docs.rs/lessvec/badge.svg)](https://docs.rs/lessvec) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://github.com/OWNER/REPO/actions/workflows/CI.yml/badge.svg)](https://github.com/OWNER/REPO/actions/workflows/CI.yml) [![GitHub tag](https://img.shields.io/github/v/tag/OWNER/REPO.svg)](https://github.com/OWNER/REPO/releases) + +## Quick example + +```rust +use lessvec::LessVec; + +let mut v = LessVec::new(); +v.push(1); +v.push(2); +assert_eq!(v.as_slice(), &[1, 2]); +``` + +## License + +[MIT LICENSE](./LICENSE) diff --git a/examples/basics.rs b/examples/basics.rs new file mode 100644 index 0000000..3ee8879 --- /dev/null +++ b/examples/basics.rs @@ -0,0 +1,52 @@ +use lessvec::LessVec; + +fn main() { + // Basic push/pop + println!(">>> Executing: push(1); push(2)"); + let mut v = LessVec::new(); + v.push(1); + v.push(2); + println!("initial: {:?}", v.as_slice()); + println!(); + + // Reserve capacity + println!(">>> Executing: reserve(8) and then print len/capacity"); + v.reserve(8); + println!("len = {}, capacity = {}", v.len(), v.capacity()); + println!(); + + // Insert / remove + println!(">>> Executing: insert(1, 5); remove(1)"); + v.insert(1, 5); + println!("after insert: {:?}", v.as_slice()); + let removed = v.remove(1); + println!("removed element = {}", removed); + println!(); + + // Mutate via as_mut_slice + println!(">>> Executing: as_mut_slice()[0] = 42"); + v.as_mut_slice()[0] = 42; + println!("after mutation: {:?}", v.as_slice()); + println!(); + + // Drain elements + println!(">>> Executing: push(100); drain()"); + v.push(100); + let drained: Vec<_> = v.drain().collect(); + println!("drained: {:?}", drained); + println!( + "after drain: len = {}, capacity = {}", + v.len(), + v.capacity() + ); + println!(); + + // Consume via into_iter + println!(">>> Executing: into_iter()"); + let mut v2 = LessVec::new(); + v2.push(10); + v2.push(20); + let collected: Vec<_> = v2.into_iter().collect(); + println!("into_iter collected: {:?}", collected); + println!(); +} diff --git a/src/lib.rs b/src/lib.rs index 5277d08..15a8e8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,54 +1,85 @@ -use std::alloc::{self, Layout}; -use std::mem::ManuallyDrop; -use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; -use std::{mem, ptr}; +//! A minimal `Vec`-like collection implemented with manual allocation. +//! +//! This crate provides `LessVec`, a small, educational reimplementation of +//! `std::vec::Vec` following patterns from the Rustonomicon. It's intended +//! for learning and small use-cases, not as a drop-in replacement for `Vec`. +//! +//! # Examples +//! ``` +//! use lessvec::LessVec; +//! +//! let mut v = LessVec::new(); +//! v.push(1); +//! v.push(2); +//! assert_eq!(&*v, &[1, 2]); +//! assert_eq!(v.pop(), Some(2)); +//! ``` +//! +//! See individual method docs for more examples. +use std::{ + alloc::{self, Layout}, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr::{self, NonNull}, +}; -pub struct Vec { +struct RawVec { ptr: NonNull, cap: usize, - len: usize, } -unsafe impl Send for Vec {} -unsafe impl Sync for Vec {} +unsafe impl Send for RawVec {} +unsafe impl Sync for RawVec {} -impl Vec { - pub fn new() -> Self { - assert!(mem::size_of::() != 0, "Can't handle ZSTs yet"); - Vec { +impl RawVec { + fn new() -> Self { + let cap = if mem::size_of::() == 0 { + usize::MAX + } else { + 0 + }; + + // `NonNull::dangling` doubles as "unallocated" and "zero-sized allocation" + RawVec { ptr: NonNull::dangling(), - cap: 0, - len: 0, + cap, } } -} -impl Vec { fn grow(&mut self) { + // since we set the capacity to usize::MAX when T has size 0, getting + // to here means Vec is overfull. + assert!(mem::size_of::() != 0, "capacity overflow"); + let (new_cap, new_layout) = if self.cap == 0 { - (1, Layout::array::(1)) + (1, Layout::array::(1).unwrap()) } else { - // shouldn't overflow since self.cap <= isize::MAX + // `new_cap` will not overflow because `self.cap <= isize::MAX`. let new_cap = 2 * self.cap; - (new_cap, Layout::array::(new_cap)) + + // `Layout::array` checks that the number of bytes is <= usize::MAX, + // but this is redundant since old_layout.size() <= isize::MAX, + // `unwrap` should never fail. + let new_layout = Layout::array::(new_cap).unwrap(); + (new_cap, new_layout) }; - // `Layout::array` checks that the number of bytes allocated is in 1..=isize::MAX - // and will error otherwise. An allocation of 0 bytes isn't possible because of the - // above condition. - let new_layout = new_layout.expect("Allocation too large"); + // Ensure that the new allocation doesn't overflow; <= isize::MAX bytes. + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large" + ); let new_ptr = if self.cap == 0 { unsafe { alloc::alloc(new_layout) } } else { let old_layout = Layout::array::(self.cap).unwrap(); let old_ptr = self.ptr.as_ptr() as *mut u8; - unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } }; - // new_ptr will be null if allocation fails, abort + // new_ptr becomes null if allocation fails, abort. self.ptr = match NonNull::new(new_ptr as *mut T) { Some(p) => p, None => alloc::handle_alloc_error(new_layout), @@ -57,157 +88,636 @@ impl Vec { self.cap = new_cap; } + fn grow_to(&mut self, new_cap: usize) { + if mem::size_of::() == 0 { + return; + } + if new_cap <= self.cap { + return; + } + + let new_layout = Layout::array::(new_cap).unwrap(); + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large" + ); + + let new_ptr = if self.cap == 0 { + unsafe { alloc::alloc(new_layout) } + } else { + let old_layout = Layout::array::(self.cap).unwrap(); + let old_tr = self.ptr.as_ptr() as *mut u8; + unsafe { alloc::realloc(old_tr, old_layout, new_layout.size()) } + }; + + self.ptr = match NonNull::new(new_ptr as *mut T) { + Some(p) => p, + None => alloc::handle_alloc_error(new_layout), + }; + + self.cap = new_cap; + } +} + +impl Drop for RawVec { + fn drop(&mut self) { + if self.cap != 0 && std::mem::size_of::() > 0 { + let layout = std::alloc::Layout::array::(self.cap).unwrap(); + unsafe { + std::alloc::dealloc(self.ptr.as_ptr() as *mut _, layout); + } + } + } +} + +/// A minimal growable contiguous vector. +/// +/// `LessVec` stores elements in a heap buffer and supports a small set of +/// `Vec`-like operations. Use the methods below to manipulate the collection. +/// +/// # Examples +/// +/// ``` +/// use lessvec::LessVec; +/// +/// let mut v = LessVec::new(); +/// v.push(10); +/// v.push(20); +/// assert_eq!(v.len(), 2); +/// assert!(!v.is_empty()); +/// assert!(v.capacity() >= 2); +/// ``` +pub struct LessVec { + buf: RawVec, + len: usize, +} + +unsafe impl Send for LessVec {} +unsafe impl Sync for LessVec {} + +impl Default for LessVec { + fn default() -> Self { + Self::new() + } +} + +impl LessVec { + fn ptr(&self) -> *mut T { + self.buf.ptr.as_ptr() + } + + fn cap(&self) -> usize { + self.buf.cap + } + + /// Creates a new, empty `LessVec`. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let v: LessVec = LessVec::new(); + /// assert_eq!(v.len(), 0); + /// ``` + pub fn new() -> Self { + LessVec { + buf: RawVec::new(), + len: 0, + } + } + + /// Returns the number of elements in the vector. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// assert_eq!(v.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the vector contains no elements. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// assert!(v.is_empty()); + /// v.push(1); + /// assert!(!v.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the number of elements the `LessVec` can hold without reallocating. + /// + /// Note: capacity may be larger than the number of elements. Calling + /// `reserve` or `reserve_exact` increases capacity. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); + /// v.reserve(5); + /// assert!(v.capacity() >= 5); + /// ``` + pub fn capacity(&self) -> usize { + self.cap() + } + + /// Clears the vector, removing all values. + /// + /// This drops each element in the vector. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + pub fn clear(&mut self) { + while self.pop().is_some() {} + } + + /// Returns a slice containing all elements of the vector. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// assert_eq!(v.as_slice(), &[1]); + /// ``` + pub fn as_slice(&self) -> &[T] { + self + } + + /// Returns a mutable slice containing all elements of the vector. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// v.as_mut_slice()[0] = 2; + /// assert_eq!(v.as_slice(), &[2]); + /// ``` + pub fn as_mut_slice(&mut self) -> &mut [T] { + &mut *self + } + + /// Ensures the `LessVec` can hold at least `additional` more elements without reallocating. + /// + /// This grows capacity exponentially where necessary. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); + /// v.reserve(10); + /// assert!(v.capacity() >= 10); + /// ``` + pub fn reserve(&mut self, additional: usize) { + let required = self.len.checked_add(additional).expect("capacity overflow"); + if self.cap() >= required { + return; + } + while self.cap() < required { + self.buf.grow(); + } + } + + /// Ensures the `LessVec` has capacity for exactly `additional` more elements (no fewer). + /// + /// Unlike `reserve`, this attempts to allocate the exact requested capacity in one go. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); + /// v.reserve_exact(3); + /// assert!(v.capacity() >= 3); + /// ``` + pub fn reserve_exact(&mut self, additional: usize) { + let required = self.len.checked_add(additional).expect("capacity overflow"); + if self.cap() >= required { + return; + } + self.buf.grow_to(required); + } + + /// Appends an element to the back of the collection. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(5); + /// assert_eq!(v.len(), 1); + /// assert_eq!(v.as_slice(), &[5]); + /// ``` pub fn push(&mut self, elem: T) { - if self.len == self.cap { - self.grow(); + if self.len == self.cap() { + self.buf.grow(); } unsafe { - ptr::write(self.ptr.as_ptr().add(self.len), elem); + ptr::write(self.ptr().add(self.len), elem); } // This operation will not fail, we will get OOM first. self.len += 1; } + /// Removes the last element and returns it, or `None` if empty. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(10); + /// assert_eq!(v.pop(), Some(10)); + /// assert_eq!(v.pop(), None); + /// ``` pub fn pop(&mut self) -> Option { if self.len == 0 { None } else { self.len -= 1; - unsafe { Some(ptr::read(self.ptr.as_ptr().add(self.len))) } + unsafe { Some(ptr::read(self.ptr().add(self.len))) } } } + /// Inserts an element at `index`, shifting elements to the right. + /// + /// # Panics + /// + /// Panics if `index > len`. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// v.push(3); + /// v.insert(1, 2); + /// assert_eq!(v.as_slice(), &[1, 2, 3]); + /// ``` pub fn insert(&mut self, index: usize, elem: T) { assert!(index <= self.len, "index out of bounds"); - if self.len == self.cap { - self.grow(); + if self.len == self.cap() { + self.buf.grow(); } unsafe { ptr::copy( - self.ptr.as_ptr().add(index), - self.ptr.as_ptr().add(index + 1), + self.ptr().add(index), + self.ptr().add(index + 1), self.len - index, ); - ptr::write(self.ptr.as_ptr().add(index), elem); + ptr::write(self.ptr().add(index), elem); } self.len += 1; } + /// Removes and returns the element at `index`, shifting elements left. + /// + /// # Panics + /// + /// Panics if `index >= len`. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(1); + /// v.push(2); + /// assert_eq!(v.remove(0), 1); + /// assert_eq!(v.as_slice(), &[2]); + /// ``` pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len, "index out of bounds"); unsafe { self.len -= 1; - let result = ptr::read(self.ptr.as_ptr().add(index)); + let result = ptr::read(self.ptr().add(index)); ptr::copy( - self.ptr.as_ptr().add(index + 1), - self.ptr.as_ptr().add(index), + self.ptr().add(index + 1), + self.ptr().add(index), self.len - index, ); result } } + + /// Removes all elements and returns an iterator that yields the removed elements. + /// + /// The vector's length is set to zero immediately; elements are yielded by the returned iterator. + /// + /// # Examples + /// + /// ``` + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); + /// v.push(10); + /// v.push(20); + /// let drained: Vec<_> = v.drain().collect(); + /// assert_eq!(drained, vec![10, 20]); + /// assert!(v.is_empty()); + /// ``` + pub fn drain(&'_ mut self) -> Drain<'_, T> { + let iter = unsafe { RawValIter::new(self) }; + + self.len = 0; + + Drain { + iter, + vec: PhantomData, + } + } } -impl Drop for Vec { +impl Drop for LessVec { fn drop(&mut self) { if self.len != 0 { - while let Some(_) = self.pop() {} - let layout = Layout::array::(self.cap).unwrap(); - unsafe { - alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); - } + // deallocation is handled by RawVec + while self.pop().is_some() {} } } } -impl Deref for Vec { +impl Deref for LessVec { type Target = [T]; fn deref(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + unsafe { std::slice::from_raw_parts(self.ptr(), self.len) } } } -impl DerefMut for Vec { +impl DerefMut for LessVec { fn deref_mut(&mut self) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + unsafe { std::slice::from_raw_parts_mut(self.ptr(), self.len) } } } -pub struct IntoIter { - buf: NonNull, - cap: usize, +struct RawValIter { start: *const T, end: *const T, } -impl IntoIterator for Vec { - type Item = T; - type IntoIter = IntoIter; - fn into_iter(self) -> IntoIter { - let vec = ManuallyDrop::new(self); - - let ptr = vec.ptr; - let cap = vec.cap; - let len = vec.len; - - IntoIter { - buf: ptr, - cap, - start: ptr.as_ptr(), - end: if cap == 0 { - ptr.as_ptr() +impl RawValIter { + // unsafe construct because it has no associated lifetimes. This is + // necessary to store RawValIter as in the same struct as its actual + // allocation. OK to use since it's a private implementation detail. + unsafe fn new(slice: &[T]) -> Self { + RawValIter { + start: slice.as_ptr(), + end: if mem::size_of::() == 0 { + ((slice.as_ptr() as usize) + slice.len()) as *const _ + } else if slice.is_empty() { + slice.as_ptr() } else { - unsafe { ptr.as_ptr().add(len) } + unsafe { slice.as_ptr().add(slice.len()) } }, } } } -impl Iterator for IntoIter { +impl Iterator for RawValIter { type Item = T; fn next(&mut self) -> Option { if self.start == self.end { None } else { unsafe { - let result = ptr::read(self.start); - self.start = self.start.offset(1); - Some(result) + if mem::size_of::() == 0 { + self.start = (self.start as usize + 1) as *const _; + Some(ptr::read(NonNull::::dangling().as_ptr())) + } else { + let old_ptr = self.start; + self.start = self.start.offset(1); + Some(ptr::read(old_ptr)) + } } } } fn size_hint(&self) -> (usize, Option) { - let len = (self.end as usize - self.start as usize) / mem::size_of::(); + let elem_size = mem::size_of::(); + let len = + (self.end as usize - self.start as usize) / if elem_size == 0 { 1 } else { elem_size }; + (len, Some(len)) } } -impl DoubleEndedIterator for IntoIter { +impl DoubleEndedIterator for RawValIter { fn next_back(&mut self) -> Option { if self.start == self.end { None } else { unsafe { - self.end = self.end.offset(-1); - Some(ptr::read(self.end)) + if mem::size_of::() == 0 { + self.end = (self.end as usize - 1) as *const _; + Some(ptr::read(NonNull::::dangling().as_ptr())) + } else { + self.end = self.end.offset(-1); + Some(ptr::read(self.end)) + } } } } } +/// Iterator that yields values by value when consuming a `LessVec`. +/// +/// # Examples +/// +/// ``` +/// use lessvec::LessVec; +/// let mut v = LessVec::new(); +/// v.push(1); +/// v.push(2); +/// let out: Vec<_> = v.into_iter().collect(); +/// assert_eq!(out, vec![1, 2]); +/// ``` +pub struct IntoIter { + _buf: RawVec, + iter: RawValIter, +} + +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + impl Drop for IntoIter { fn drop(&mut self) { - if self.cap != 0 { - for _ in &mut *self {} - let layout = Layout::array::(self.cap).unwrap(); - unsafe { - alloc::dealloc(self.buf.as_ptr() as *mut u8, layout); - } + for _ in &mut *self {} + } +} + +impl IntoIterator for LessVec { + type Item = T; + type IntoIter = IntoIter; + fn into_iter(self) -> IntoIter { + unsafe { + let iter = RawValIter::new(&self); + + let buf = ptr::read(&self.buf); + mem::forget(self); + + IntoIter { _buf: buf, iter } } } } + +/// An iterator produced by `LessVec::drain`. +/// +/// Iterates over and yields the drained elements by value. +/// +/// # Examples +/// +/// ``` +/// use lessvec::LessVec; +/// let mut v = LessVec::new(); +/// v.push(1); +/// v.push(2); +/// let drained: Vec<_> = v.drain().collect(); +/// assert_eq!(drained, vec![1, 2]); +/// ``` +pub struct Drain<'a, T: 'a> { + vec: PhantomData<&'a mut LessVec>, + iter: RawValIter, +} + +impl<'a, T> Iterator for Drain<'a, T> { + type Item = T; + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, T> DoubleEndedIterator for Drain<'a, T> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a, T> Drop for Drain<'a, T> { + fn drop(&mut self) { + for _ in &mut *self {} + } +} + +#[cfg(test)] +mod tests { + use crate::LessVec; + + #[test] + fn push_pop_roundtrip() { + let mut v = LessVec::new(); + v.push(1); + v.push(2); + v.push(3); + + assert_eq!(v.pop(), Some(3)); + assert_eq!(v.pop(), Some(2)); + assert_eq!(v.pop(), Some(1)); + assert_eq!(v.pop(), None); + } + + #[test] + fn insert_remove() { + let mut v = LessVec::new(); + v.push(1); + v.push(3); + v.insert(1, 2); + + assert_eq!(&*v, &[1, 2, 3]); + assert_eq!(v.remove(1), 2); + assert_eq!(&*v, &[1, 3]); + } + + #[test] + fn drain_consumes_elements() { + let mut v = LessVec::new(); + v.push(10); + v.push(20); + v.push(30); + + let drained: Vec<_> = v.drain().collect(); + assert_eq!(drained, vec![10, 20, 30]); + assert_eq!(&*v, &[]); + } + + #[test] + fn into_iter_works() { + let mut v = LessVec::new(); + v.push(1); + v.push(2); + v.push(3); + + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1, 2, 3]); + } + + #[test] + fn reserve_and_capacity() { + let mut v: LessVec = LessVec::new(); + v.reserve(5); + assert!(v.capacity() >= 5); + v.reserve_exact(10); + assert!(v.capacity() >= 10); + } + + #[test] + fn clear_works() { + let mut v = LessVec::new(); + v.push(1); + v.clear(); + assert!(v.is_empty()); + } + + #[test] + fn as_slice_and_mut() { + let mut v = LessVec::new(); + v.push(1); + assert_eq!(v.as_slice(), &[1]); + v.as_mut_slice()[0] = 2; + assert_eq!(v.as_slice(), &[2]); + } +}