collect_array-0.1.3/.cargo_vcs_info.json0000644000000001120000000000000136510ustar { "git": { "sha1": "989b6f8d19fba0410bf3235ab311ab760578649e" } } collect_array-0.1.3/.github/workflows/miri.yml000064400000000000000000000010260000000000000175040ustar 00000000000000name: miri on: push: pull_request: schedule: - cron: "0 18 * * *" jobs: miri: name: Miri runs-on: ubuntu-latest strategy: fail-fast: false steps: - uses: actions/checkout@v2 - name: Fetch toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly components: miri - name: Run tests in miri uses: actions-rs/cargo@v1 with: command: miri args: test toolchain: nightly collect_array-0.1.3/.github/workflows/presubmit.yml000064400000000000000000000025520000000000000205630ustar 00000000000000name: presubmit on: push: pull_request: schedule: - cron: "0 18 * * *" jobs: presubmit: name: Presubmit runs-on: ubuntu-latest strategy: fail-fast: false matrix: toolchain: [stable, beta, nightly] steps: - uses: actions/checkout@v2 - name: Fetch toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} components: rustfmt, clippy target: thumbv6m-none-eabi - name: Run tests uses: actions-rs/cargo@v1 with: command: test toolchain: ${{ matrix.toolchain }} - name: Build no-std uses: actions-rs/cargo@v1 with: command: build args: --target thumbv6m-none-eabi toolchain: ${{ matrix.toolchain }} - name: Annotate commit with clippy warnings uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features toolchain: ${{ matrix.toolchain }} - name: Install cargo-audit binary crate uses: actions-rs/install@v0.1 with: crate: cargo-audit version: latest use-tool-cache: true - name: Security audit uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} collect_array-0.1.3/.gitignore000064400000000000000000000000100000000000000144040ustar 00000000000000/target collect_array-0.1.3/Cargo.toml0000644000000017010000000000000116540ustar # 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "collect_array" version = "0.1.3" authors = ["Daniel Wagner-Hall "] description = "Allows for collecting an Iterator into an exactly sized array." readme = "README.md" keywords = ["array", "iterators"] categories = ["rust-patterns", "no-std"] license = "BSD-3-Clause" repository = "https://github.com/illicitonion/collect_array" [badges.maintenance] status = "passively-maintained" collect_array-0.1.3/Cargo.toml.orig000064400000000000000000000006730000000000000153220ustar 00000000000000[package] name = "collect_array" version = "0.1.3" authors = ["Daniel Wagner-Hall "] edition = "2018" description = "Allows for collecting an Iterator into an exactly sized array." repository = "https://github.com/illicitonion/collect_array" readme = "README.md" keywords = ["array", "iterators"] categories = ["rust-patterns", "no-std"] license = "BSD-3-Clause" [badges] maintenance = { status = "passively-maintained" } collect_array-0.1.3/LICENSE000064400000000000000000000027210000000000000134340ustar 00000000000000Copyright (c) 2021, Daniel Wagner-Hall All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of collect_array nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. collect_array-0.1.3/README.md000064400000000000000000000011660000000000000137100ustar 00000000000000# collect_array Allows for collecting an Iterator into an exactly sized array. [![crates.io](https://img.shields.io/crates/v/collect_array.svg)](https://crates.io/crates/collect_array) [![Documentation](https://docs.rs/collect_array/badge.svg)](https://docs.rs/collect_array) [![Build Status](https://github.com/illicitonion/collect_array/actions/workflows/presubmit.yml/badge.svg)](https://github.com/illicitonion/collect_array/actions) ## Example ```rust use collect_array::CollectArrayResult; let result: CollectArrayResult<_, 2> = vec![1, 2].into_iter().collect(); assert_eq!(CollectArrayResult::Ok([1, 2]), result); ``` collect_array-0.1.3/src/lib.rs000064400000000000000000000321550000000000000143360ustar 00000000000000//! Allows for collecting an Iterator into an exactly sized array. //! //! # Example //! //! ``` //! use collect_array::CollectArrayResult; //! //! let v = vec![0, 1, 2]; //! let result: CollectArrayResult<_, 3> = v.into_iter().collect(); //! assert_eq!(CollectArrayResult::Ok([0, 1, 2]), result); //! ``` #![cfg_attr(not(test), no_std)] use core::{ mem::{self, forget, MaybeUninit}, ptr, }; /// The result of collecting an Iterator into an exactly-sized array, or having failed to. /// /// More than N elements may be consumed from the Iterator - if this is undesirable, consider /// calling [`Iterator#take`] before collecting. pub enum CollectArrayResult { /// Returned if the Iterator contained exactly N elements. Ok([T; N]), /// Returned if the Iterator contained more than N elements. /// The underlying Iterator may not be exhausted, and remaining values may not be accessible. TooManyElements { /// The N values which were read. values: [T; N], /// The next value after the Nth. next_value: T, }, /// Returned if the Iterator contained fewer than N elements. /// /// # Safety /// /// Only the first `init_count` elements will be init, the remaining elements must not be read. NotEnoughElements { /// The consumed values, only `init_count` of which will be init. values: [MaybeUninit; N], /// How many elements in `values` are init. init_count: usize, }, } impl CollectArrayResult { /// Returns the contained [`Ok`](Self::Ok) value, consuming the self value. pub fn unwrap(mut self) -> [T; N] { let a = match &mut self { Self::Ok(a) => unsafe { ptr::read(a) }, Self::TooManyElements { .. } => { panic!("called `CollectArrayResult::unwrap` with too many elements") } Self::NotEnoughElements { .. } => { panic!("called `CollectArrayResult::unwrap` without enough elements") } }; mem::forget(self); a } } impl PartialEq> for CollectArrayResult where T: PartialEq, { fn eq(&self, other: &CollectArrayResult) -> bool { match (self, other) { (Self::Ok(lhs), Self::Ok(rhs)) => lhs == rhs, (Self::TooManyElements { .. }, Self::TooManyElements { .. }) => false, ( Self::NotEnoughElements { values: lhs, init_count: lhs_count, }, Self::NotEnoughElements { values: rhs, init_count: rhs_count, }, ) if lhs_count == rhs_count => { for i in 0..*lhs_count { if let Some(lhs) = lhs.get(i) { if let Some(rhs) = rhs.get(i) { unsafe { if *lhs.as_ptr() != *rhs.as_ptr() { return false; } } } } } true } _ => false, } } } impl core::fmt::Debug for CollectArrayResult where T: core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Ok(arr) => arr.fmt(f), Self::TooManyElements { values, next_value: next } => write!( f, "CollectArrayResult::TooManyElements{{ values: {:?}, next: {:?} (possibly others...) }}", values, next, ), Self::NotEnoughElements { values, init_count } => { write!( f, "CollectArrayResult::NotEnoughElements{{ got: {:?}, expected: {:?}, values: [", init_count, N )?; let mut i = 0; while i < *init_count { if i > 0 { write!(f, ", ")?; } write!(f, "{:?}", unsafe { &*values.get(i).unwrap().as_ptr() })?; i += 1; } write!(f, "] }}")?; Ok(()) } } } } impl core::iter::FromIterator for CollectArrayResult { fn from_iter>(it: It) -> Self { struct PanicDropper<'a, T> { init: &'a mut [MaybeUninit], } impl<'a, T> Drop for PanicDropper<'a, T> { fn drop(&mut self) { let init_slice = ptr::slice_from_raw_parts_mut( self.init.as_mut_ptr() as *mut T, self.init.len(), ); unsafe { ptr::drop_in_place(init_slice) } } } // TODO: Use MaybeUninit::uninit_array or [MaybeUninit::::uninit(); N] when either stabilises. let mut values: [MaybeUninit; N] = unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }; let mut added = 0_usize; let mut iter = it.into_iter(); while added < N { let (init, uninit) = values.split_at_mut(added); let panic_dropper = PanicDropper { init }; let next = iter.next(); forget(panic_dropper); if let Some(value) = next { uninit[0] = MaybeUninit::new(value); added += 1; } else { break; } } if added == N { // TODO: Use MaybeUninit::array_assume_init when it stabilises (https://github.com/rust-lang/rust/issues/80908). let values = unsafe { (&values as *const _ as *const [T; N]).read() }; if let Some(next_value) = iter.next() { CollectArrayResult::TooManyElements { values, next_value } } else { CollectArrayResult::Ok(values) } } else { CollectArrayResult::NotEnoughElements { values, init_count: added, } } } } impl Drop for CollectArrayResult { fn drop(&mut self) { match self { Self::NotEnoughElements { values, init_count } => { let init_slice = ptr::slice_from_raw_parts_mut(values.as_mut_ptr() as *mut T, *init_count); unsafe { ptr::drop_in_place(init_slice) } } Self::Ok(_) => { // Automatically handled } Self::TooManyElements { values: _, next_value: _, } => { // Automatically handled } } } } #[cfg(test)] mod test { use crate::CollectArrayResult; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicUsize, Ordering}; use std::{panic, sync::Arc}; #[test] fn ok() { let input = vec![0_i32, 1_i32, 2_i32]; let output = input.into_iter().collect::>(); assert_eq!(output, CollectArrayResult::Ok([0, 1, 2])); } #[test] fn want_more() { let input = vec![0_i32, 1_i32, 2_i32]; let output = input.into_iter().collect::>(); assert_eq!( output, CollectArrayResult::NotEnoughElements { values: [ MaybeUninit::new(0), MaybeUninit::new(1), MaybeUninit::new(2), MaybeUninit::uninit() ], init_count: 3 } ); } #[test] fn want_fewer() { let input = vec![0_i32, 1_i32, 2_i32]; let output = input.into_iter().collect::>(); let want_arr = [0_i32, 1_i32]; let want_next = 2_i32; if let CollectArrayResult::TooManyElements { values, next_value: next, } = output { assert_eq!(values, want_arr); assert_eq!(next, want_next); } else { let want: CollectArrayResult<_, 2> = CollectArrayResult::TooManyElements { values: want_arr, next_value: want_next, }; panic!( "Saw wrong elements; expected {:?} but saw {:?}", want, output ); } } #[test] fn debug() { assert_eq!( "[0, 1, 2]", format!("{:?}", CollectArrayResult::Ok([0, 1, 2])) ); let not_enough: CollectArrayResult<_, 4> = vec![0, 1, 2].into_iter().collect(); assert_eq!( "CollectArrayResult::NotEnoughElements{ got: 3, expected: 4, values: [0, 1, 2] }", format!("{:?}", not_enough) ); assert_eq!( "CollectArrayResult::TooManyElements{ values: [0, 1], next: 2 (possibly others...) }", format!( "{:?}", CollectArrayResult::TooManyElements { values: [0, 1], next_value: 2 } ) ); } struct DropCounter(Arc); impl Drop for DropCounter { fn drop(&mut self) { self.0.fetch_add(1, Ordering::SeqCst); } } #[test] fn drop_ok() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok: CollectArrayResult<_, 3> = vec![ DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), ] .into_iter() .collect(); drop(ok); assert_eq!(3, Arc::try_unwrap(drop_count).unwrap().into_inner()); } #[test] fn drop_too_many() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok: CollectArrayResult<_, 1> = vec![ DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), ] .into_iter() .collect(); drop(ok); assert_eq!(3, Arc::try_unwrap(drop_count).unwrap().into_inner()); } #[test] fn drop_not_enough() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok: CollectArrayResult<_, 3> = vec![DropCounter(drop_count.clone())].into_iter().collect(); drop(ok); assert_eq!(1, Arc::try_unwrap(drop_count).unwrap().into_inner()); } #[test] fn drop_panic_during_collection() { let drop_count = Arc::new(AtomicUsize::new(0)); let result = panic::catch_unwind(|| { let mut first = true; std::iter::from_fn(|| { if first { first = false; Some(DropCounter(drop_count.clone())) } else { panic!("Panic'd on construction") } }) .collect::>() }); assert!(result.is_err()); assert_eq!(1, drop_count.load(Ordering::SeqCst)); } #[test] fn drop_panic_too_many() { let drop_count = Arc::new(AtomicUsize::new(0)); let result = panic::catch_unwind(|| { let mut first = true; std::iter::from_fn(|| { if first { first = false; Some(DropCounter(drop_count.clone())) } else { panic!("Panic'd on construction") } }) .collect::>() }); assert!(result.is_err()); assert_eq!(1, drop_count.load(Ordering::SeqCst)); } #[test] fn unwrap_ok() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok = vec![ DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), ] .into_iter() .collect::>() .unwrap(); drop(ok); assert_eq!(3, Arc::try_unwrap(drop_count).unwrap().into_inner()); } #[test] fn unwrap_too_many() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok = panic::catch_unwind(|| { vec![ DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), DropCounter(drop_count.clone()), ] .into_iter() .collect::>() .unwrap() }); assert!(ok.is_err()); assert_eq!(3, Arc::try_unwrap(drop_count).unwrap().into_inner()); } #[test] fn unwrap_not_enough() { let drop_count = Arc::new(AtomicUsize::new(0)); let ok = panic::catch_unwind(|| { vec![DropCounter(drop_count.clone())] .into_iter() .collect::>() .unwrap() }); assert!(ok.is_err()); assert_eq!(1, Arc::try_unwrap(drop_count).unwrap().into_inner()); } }