inplace-vec-builder-0.1.1/.cargo_vcs_info.json0000644000000001120000000000100146370ustar { "git": { "sha1": "af53b724809d406806322c4c386c8a99722fe816" } } inplace-vec-builder-0.1.1/.github/workflows/rust.yml000064400000000000000000000011230072674642500205770ustar 00000000000000name: Rust on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --all-features --locked --verbose - name: Run tests run: cargo test --all-features --locked --verbose lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: fmt run: cargo fmt --all -- --check - name: clippy run: cargo --locked clippy --all-features --all-targets -- -D warnings inplace-vec-builder-0.1.1/.gitignore000064400000000000000000000000250072674642500154520ustar 00000000000000**/target **/*.rs.bk inplace-vec-builder-0.1.1/Cargo.lock0000644000000011200000000000100126120ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "inplace-vec-builder" version = "0.1.1" dependencies = [ "smallvec", "testdrop", ] [[package]] name = "smallvec" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "testdrop" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d39ee32a48eb5325bd3927d4533ac58344af21def4a43ddfc3734c43aebbeae" inplace-vec-builder-0.1.1/Cargo.toml0000644000000017150000000000100126470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "inplace-vec-builder" version = "0.1.1" authors = ["RĂ¼diger Klaehn "] description = "Build a vec from a vec, in place" keywords = ["array", "vec", "inplace"] categories = ["data-structures"] license = "MIT OR Apache-2.0" repository = "https://github.com/rklaehn/inplace-vec-builder" resolver = "2" [dependencies.smallvec] version = "1.7.0" optional = true [dev-dependencies.testdrop] version = "0.1.2" [features] default = ["stdvec"] stdvec = [] inplace-vec-builder-0.1.1/Cargo.toml.orig000064400000000000000000000011160072674642500163530ustar 00000000000000[package] name = "inplace-vec-builder" version = "0.1.1" edition = "2021" authors = ["RĂ¼diger Klaehn "] description = "Build a vec from a vec, in place" repository = "https://github.com/rklaehn/inplace-vec-builder" license = "MIT OR Apache-2.0" keywords = ["array", "vec", "inplace"] categories = ["data-structures"] [features] stdvec = [] default = ["stdvec"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] smallvec = { version = "1.7.0", optional = true } [dev-dependencies] testdrop = "0.1.2" inplace-vec-builder-0.1.1/README.md000064400000000000000000000031170072674642500147460ustar 00000000000000# Inplace-Vec-Builder A small library to build a [Vec](https://doc.rust-lang.org/std/vec/struct.Vec.html) or [SmallVec](https://docs.rs/smallvec) out of itself without allocating. This is useful when writing in place operations that do not allocate. Imagine you have a vec that contains some numbers. You now want to apply some transformation on these elements, like mapping, filtering, adding some elements, and then store the result in the same place. The simplest way to do this would be something like this: ```rust let mut res = self .elements .iter() .filter(|x| **x > 5) .map(|x| *x * 2) .chain(std::iter::once(123)) .collect(); std::mem::swap(&mut self.elements, &mut res); ``` But this does allocate a new vector. Usually not a big deal, but if this is some very frequently used code, you want to avoid it. Note that in many cases where you do filtering combined with a transformation, [retain](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain) can be used. If that is the case using retain is of course preferable. This crate provides a helper that allows doing something like the above without allocations. It is fairly low level, since it is intended to be used from other libraries. ```rust let mut t = InPlaceVecBuilder::from(&mut self.elements); while let Some(elem) = t.pop_front() { if elem > 5 { t.push(elem * 2); } } t.push(123); ``` # Features - stdvec (default): std Vec support - smallvec: SmallVec support inplace-vec-builder-0.1.1/examples/demo.rs000064400000000000000000000013670072674642500166040ustar 00000000000000use inplace_vec_builder::InPlaceVecBuilder; struct Collection { elements: Vec, } impl Collection { fn op(&mut self) { let mut res = self .elements .iter() .filter(|x| **x > 5) .map(|x| *x * 2) .chain(std::iter::once(123)) .collect(); std::mem::swap(&mut self.elements, &mut res); } fn inplace_op(&mut self) { let mut t = InPlaceVecBuilder::from(&mut self.elements); while let Some(elem) = t.pop_front() { if elem > 5 { t.push(elem * 2); } } t.push(123); } } fn main() { let mut x = Collection { elements: Vec::new(), }; x.op(); x.inplace_op(); } inplace-vec-builder-0.1.1/src/lib.rs000064400000000000000000000004330072674642500153700ustar 00000000000000#![doc = include_str!("../README.md")] #[cfg(feature = "smallvec")] mod small_vec_builder; #[cfg(feature = "stdvec")] mod vec_builder; #[cfg(feature = "smallvec")] pub use small_vec_builder::InPlaceSmallVecBuilder; #[cfg(feature = "stdvec")] pub use vec_builder::InPlaceVecBuilder; inplace-vec-builder-0.1.1/src/small_vec_builder.rs000064400000000000000000000205670072674642500203070ustar 00000000000000//! A data structure for in place modification of smallvecs. #![deny(missing_docs)] #![allow(dead_code)] use core::fmt::Debug; use smallvec::{Array, SmallVec}; /// builds a SmallVec out of itself pub struct InPlaceSmallVecBuilder<'a, A: Array> { /// the underlying vector, possibly containing some uninitialized values in the middle! v: &'a mut SmallVec, /// the end of the target area t1: usize, /// the start of the source area s0: usize, } impl<'a, T: Debug, A: Array> Debug for InPlaceSmallVecBuilder<'a, A> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let InPlaceSmallVecBuilder { s0, t1, v } = self; let s1 = v.len(); let cap = v.capacity(); write!( f, "InPlaceSmallVecBuilder(0..{},{}..{},{})", t1, s0, s1, cap ) } } /// initializes the source part of this flip buffer with the given vector. /// The target part is initially empty. impl<'a, A: Array> From<&'a mut SmallVec> for InPlaceSmallVecBuilder<'a, A> { fn from(value: &'a mut SmallVec) -> Self { InPlaceSmallVecBuilder { v: value, s0: 0, t1: 0, } } } impl<'a, A: Array> InPlaceSmallVecBuilder<'a, A> { /// The current target part as a slice pub fn target_slice(&self) -> &[A::Item] { &self.v[..self.t1] } /// The current source part as a slice pub fn source_slice(&self) -> &[A::Item] { &self.v[self.s0..] } /// The current source part as a slice pub fn source_slice_mut(&mut self) -> &mut [A::Item] { &mut self.v[self.s0..] } /// ensure that we have at least `capacity` space. #[inline] fn reserve(&mut self, capacity: usize) { // ensure we have space! if self.t1 + capacity > self.s0 { let v = &mut self.v; let s0 = self.s0; let s1 = v.len(); let sn = s1 - s0; // delegate to the underlying vec for the grow logic v.reserve(capacity); // move the source to the end of the vec let cap = v.capacity(); // just move source to the end without any concern about dropping unsafe { copy(v.as_mut_ptr(), s0, cap - sn, sn); v.set_len(cap); } // move s0 self.s0 = cap - sn; } } /// Take at most `n` elements from `iter` to the target #[inline] pub fn extend_from_iter>(&mut self, mut iter: I, n: usize) { if n > 0 { self.reserve(n); for _ in 0..n { if let Some(value) = iter.next() { self.push_unsafe(value) } } } } /// Push a single value to the target pub fn push(&mut self, value: A::Item) { // ensure we have space! self.reserve(1); self.push_unsafe(value); } fn push_unsafe(&mut self, value: A::Item) { unsafe { std::ptr::write(self.v.as_mut_ptr().add(self.t1), value) } self.t1 += 1; } /// Consume `n` elements from the source. If `take` is true they will be added to the target, /// else they will be dropped. #[inline] pub fn consume(&mut self, n: usize, take: bool) { let n = std::cmp::min(n, self.source_slice().len()); let v = self.v.as_mut_ptr(); if take { if self.t1 != self.s0 { unsafe { copy(v, self.s0, self.t1, n); } } self.t1 += n; self.s0 += n; } else { for _ in 0..n { unsafe { self.s0 += 1; std::ptr::drop_in_place(v.add(self.s0 - 1)); } } } } /// Skip up to `n` elements from source without adding them to the target. /// They will be immediately dropped! pub fn skip(&mut self, n: usize) { let n = std::cmp::min(n, self.source_slice().len()); let v = self.v.as_mut_ptr(); for _ in 0..n { unsafe { self.s0 += 1; std::ptr::drop_in_place(v.add(self.s0 - 1)); } } } /// Take up to `n` elements from source to target. /// If n is larger than the size of the remaining source, this will only copy all remaining elements in source. pub fn take(&mut self, n: usize) { let n = std::cmp::min(n, self.source_slice().len()); if self.t1 != self.s0 { unsafe { copy(self.v.as_mut_ptr(), self.s0, self.t1, n); } } self.t1 += n; self.s0 += n; } /// Takes the next element from the source, if it exists pub fn pop_front(&mut self) -> Option { if self.s0 < self.v.len() { self.s0 += 1; Some(unsafe { std::ptr::read(self.v.as_ptr().add(self.s0 - 1)) }) } else { None } } fn drop_source(&mut self) { // use truncate to get rid of the source part, if any, calling drop as needed self.v.truncate(self.s0); // use set_len to get rid of the gap part between t1 and s0, not calling drop! unsafe { self.v.set_len(self.t1); } self.s0 = self.t1; // shorten the source part } } #[inline] unsafe fn copy(v: *mut T, from: usize, to: usize, n: usize) { // if to < from { // for i in 0..n { // std::ptr::write(v.add(to + i), std::ptr::read(v.add(from + i))); // } // } else { // for i in (0..n).rev() { // std::ptr::write(v.add(to + i), std::ptr::read(v.add(from + i))); // } // } std::ptr::copy(v.add(from), v.add(to), n); } /// the purpose of drop is to clean up and make the SmallVec that we reference into a normal /// SmallVec again. impl<'a, A: Array> Drop for InPlaceSmallVecBuilder<'a, A> { fn drop(&mut self) { // drop the source part. self.drop_source(); } } #[cfg(test)] mod tests { extern crate testdrop; use super::*; use testdrop::{Item, TestDrop}; type Array<'a> = [Item<'a>; 2]; fn everything_dropped<'a, F>(td: &'a TestDrop, n: usize, f: F) where F: Fn(SmallVec>, SmallVec>), { let mut a: SmallVec> = SmallVec::new(); let mut b: SmallVec> = SmallVec::new(); let mut ids: Vec = Vec::new(); for _ in 0..n { let (id, item) = td.new_item(); a.push(item); ids.push(id); } for _ in 0..n { let (id, item) = td.new_item(); b.push(item); ids.push(id); } f(a, b); for id in ids { td.assert_drop(id); } } #[test] fn drop_just_source() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let _: InPlaceSmallVecBuilder = (&mut a).into(); }) } #[test] fn target_push_gap() { everything_dropped(&TestDrop::new(), 10, |mut a, b| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); for x in b.into_iter() { res.push(x); } }) } #[test] fn source_move_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); res.take(3); }) } #[test] fn source_move_all() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); res.take(10); }) } #[test] fn source_drop_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); res.skip(3); }) } #[test] fn source_drop_all() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); res.skip(10); }) } #[test] fn source_pop_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceSmallVecBuilder = (&mut a).into(); res.pop_front(); res.pop_front(); res.pop_front(); }) } } inplace-vec-builder-0.1.1/src/vec_builder.rs000064400000000000000000000200770072674642500171130ustar 00000000000000//! A data structure for in place modification of vecs. #![deny(missing_docs)] use core::fmt::Debug; /// builds a SmallVec out of itself pub struct InPlaceVecBuilder<'a, T> { /// the underlying vector, possibly containing some uninitialized values in the middle! v: &'a mut Vec, /// the end of the target area t1: usize, /// the start of the source area s0: usize, } impl<'a, T: Debug> Debug for InPlaceVecBuilder<'a, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let InPlaceVecBuilder { s0, t1, v } = self; let s1 = v.len(); let cap = v.capacity(); write!( f, "InPlaceSmallVecBuilder(0..{},{}..{},{})", t1, s0, s1, cap ) } } /// initializes the source part of this flip buffer with the given vector. /// The target part is initially empty. impl<'a, T> From<&'a mut Vec> for InPlaceVecBuilder<'a, T> { fn from(value: &'a mut Vec) -> Self { InPlaceVecBuilder { v: value, s0: 0, t1: 0, } } } impl<'a, T> InPlaceVecBuilder<'a, T> { /// The current target part as a slice pub fn target_slice(&self) -> &[T] { &self.v[..self.t1] } /// The current source part as a slice pub fn source_slice(&self) -> &[T] { &self.v[self.s0..] } /// The current source part as a slice pub fn source_slice_mut(&mut self) -> &mut [T] { &mut self.v[self.s0..] } /// ensure that we have at least `capacity` space. #[inline] fn reserve(&mut self, capacity: usize) { // ensure we have space! if self.t1 + capacity > self.s0 { let v = &mut self.v; let s0 = self.s0; let s1 = v.len(); let sn = s1 - s0; // delegate to the underlying vec for the grow logic v.reserve(capacity); // move the source to the end of the vec let cap = v.capacity(); // just move source to the end without any concern about dropping unsafe { copy(v.as_mut_ptr(), s0, cap - sn, sn); v.set_len(cap); } // move s0 self.s0 = cap - sn; } } /// Take at most `n` elements from `iter` to the target #[inline] pub fn extend_from_iter>(&mut self, mut iter: I, n: usize) { if n > 0 { self.reserve(n); for _ in 0..n { if let Some(value) = iter.next() { self.push_unsafe(value) } } } } /// Push a single value to the target pub fn push(&mut self, value: T) { // ensure we have space! self.reserve(1); self.push_unsafe(value); } fn push_unsafe(&mut self, value: T) { unsafe { std::ptr::write(self.v.as_mut_ptr().add(self.t1), value) } self.t1 += 1; } /// Consume `n` elements from the source. If `take` is true they will be added to the target, /// else they will be dropped. #[inline] pub fn consume(&mut self, n: usize, take: bool) { let n = std::cmp::min(n, self.source_slice().len()); let v = self.v.as_mut_ptr(); if take { if self.t1 != self.s0 { unsafe { copy(v, self.s0, self.t1, n); } } self.t1 += n; self.s0 += n; } else { for _ in 0..n { unsafe { self.s0 += 1; std::ptr::drop_in_place(v.add(self.s0 - 1)); } } } } /// Skip up to `n` elements from source without adding them to the target. /// They will be immediately dropped! pub fn skip(&mut self, n: usize) { let n = std::cmp::min(n, self.source_slice().len()); let v = self.v.as_mut_ptr(); for _ in 0..n { unsafe { self.s0 += 1; std::ptr::drop_in_place(v.add(self.s0 - 1)); } } } /// Take up to `n` elements from source to target. /// If n is larger than the size of the remaining source, this will only copy all remaining elements in source. pub fn take(&mut self, n: usize) { let n = std::cmp::min(n, self.source_slice().len()); if self.t1 != self.s0 { unsafe { copy(self.v.as_mut_ptr(), self.s0, self.t1, n); } } self.t1 += n; self.s0 += n; } /// Takes the next element from the source, if it exists pub fn pop_front(&mut self) -> Option { if self.s0 < self.v.len() { self.s0 += 1; Some(unsafe { std::ptr::read(self.v.as_ptr().add(self.s0 - 1)) }) } else { None } } fn drop_source(&mut self) { // use truncate to get rid of the source part, if any, calling drop as needed self.v.truncate(self.s0); // use set_len to get rid of the gap part between t1 and s0, not calling drop! unsafe { self.v.set_len(self.t1); } self.s0 = self.t1; // shorten the source part } } #[inline] unsafe fn copy(v: *mut T, from: usize, to: usize, n: usize) { // if to < from { // for i in 0..n { // std::ptr::write(v.add(to + i), std::ptr::read(v.add(from + i))); // } // } else { // for i in (0..n).rev() { // std::ptr::write(v.add(to + i), std::ptr::read(v.add(from + i))); // } // } std::ptr::copy(v.add(from), v.add(to), n); } /// the purpose of drop is to clean up and make the SmallVec that we reference into a normal /// SmallVec again. impl<'a, T> Drop for InPlaceVecBuilder<'a, T> { fn drop(&mut self) { // drop the source part. self.drop_source(); } } #[cfg(test)] mod tests { extern crate testdrop; use super::*; use testdrop::{Item, TestDrop}; fn everything_dropped<'a, F>(td: &'a TestDrop, n: usize, f: F) where F: Fn(Vec>, Vec>), { let mut a: Vec> = Vec::new(); let mut b: Vec> = Vec::new(); let mut ids: Vec = Vec::new(); for _ in 0..n { let (id, item) = td.new_item(); a.push(item); ids.push(id); } for _ in 0..n { let (id, item) = td.new_item(); b.push(item); ids.push(id); } f(a, b); for id in ids { td.assert_drop(id); } } #[test] fn drop_just_source() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let _: InPlaceVecBuilder = (&mut a).into(); }) } #[test] fn target_push_gap() { everything_dropped(&TestDrop::new(), 10, |mut a, b| { let mut res: InPlaceVecBuilder = (&mut a).into(); for x in b.into_iter() { res.push(x); } }) } #[test] fn source_move_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceVecBuilder = (&mut a).into(); res.take(3); }) } #[test] fn source_move_all() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceVecBuilder = (&mut a).into(); res.take(10); }) } #[test] fn source_drop_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceVecBuilder = (&mut a).into(); res.skip(3); }) } #[test] fn source_drop_all() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceVecBuilder = (&mut a).into(); res.skip(10); }) } #[test] fn source_pop_some() { everything_dropped(&TestDrop::new(), 10, |mut a, _| { let mut res: InPlaceVecBuilder = (&mut a).into(); res.pop_front(); res.pop_front(); res.pop_front(); }) } }