1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#![allow(non_snake_case)]
#![deny(missing_docs)]
#![feature(generic_associated_types)]
#![feature(doc_cfg)]
#![no_std]

//! [`ParallelVec`] is a generic collection of contiguously stored heterogenous values with
//!  an API similar to that of a `Vec<(T1, T2, ...)>` but stores the data laid out as a
//! separate slice per field, using a [structures of arrays](https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays)
//! layout. The advantage of this layout is that cache utilization may be signifgantly improved
//! when iterating over the data.
//!
//! This approach is common to game engines, and Entity-Component-Systems in particular but is
//! applicable anywhere that cache coherency and memory bandwidth are important for performance.
//!
//! Unlike a struct of `Vec`s, only one length and capacity field is stored, and only one contiguous
//! allocation is made for the entire data structs. Upon reallocation, a struct of `Vec` may apply
//! additional allocation pressure. `ParallelVec` only allocates once per resize.
//!
//! ## Example
//! ```rust
//! use parallel_vec::ParallelVec;
//!
//! /// Some 'entity' data.
//! # #[derive(Copy, Clone)]
//! struct Position { x: f64, y: f64 }
//! # #[derive(Copy, Clone)]
//! struct Velocity { dx: f64, dy: f64 }
//! struct ColdData { /* Potentially many fields omitted here */ }
//!
//! # use std::ops::Add;
//! # impl Add<Velocity> for Position { type Output=Self; fn add(self, other: Velocity) -> Self { Self { x: self.x + other.dx, y: self.y + other.dy } } }
//! // Create a vec of entities
//! let mut entities: ParallelVec<(Position, Velocity, ColdData)> = ParallelVec::new();
//! entities.push((Position {x: 1.0, y: 2.0}, Velocity { dx: 0.0, dy: 0.5 }, ColdData {}));
//! entities.push((Position {x: 0.0, y: 2.0}, Velocity { dx: 0.5, dy: 0.5 }, ColdData {}));
//!
//! // Update entities. This loop only loads position and velocity data, while skipping over
//! // the ColdData which is not necessary for the physics simulation.
//! for (position, velocity, _) in entities.iter_mut() {
//!     *position = *position + *velocity;
//! }
//!
//! // Remove an entity
//! entities.swap_remove(0);
//! ```
//!
//! ## Nightly
//! This crate requires use of GATs and therefore requires the following nightly features:
//! * `generic_associated_types`
//!
//! ## `no_std` Support
//! By default, this crate requires the standard library. Disabling the default features
//! enables this crate to compile in `#![no_std]` environments. There must be a set global
//! allocator and heap support for this crate to work.
//!
//!  ## `serde` Support
//! `ParallelVec` can be serialized if it's parameters can be serialized. This is disabled by
//! default. Use the `serde` feature to enable support for serialization and deserialization.

extern crate alloc;

#[cfg(any(test, feature = "std"))]
#[macro_use]
extern crate std;

/// A collection of iterators types for [`ParallelVec`].
pub mod iter;
/// Implementations for [`ParallelParam`].
pub mod param;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
mod serde;
mod slice;
mod vec;

#[cfg(feature = "serde")]
pub use crate::serde::*;

pub use param::ParallelParam;
pub use slice::{ParallelSlice, ParallelSliceMut};
pub use vec::ParallelVec;

/// Error when attempting to convert types to [`ParallelVec`].
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum ParallelVecConversionError {
    /// The provided inputs were not the same length.
    UnevenLengths,
}

#[inline(always)]
pub(crate) fn assert_in_bounds(idx: usize, len: usize) {
    assert!(idx < len, "Index out of bounds: {} (len: {})", idx, len);
}

#[inline(always)]
pub(crate) fn assert_in_bounds_inclusive(idx: usize, len: usize) {
    assert!(idx <= len, "Index out of bounds: {} (len: {})", idx, len);
}

#[inline(always)]
pub(crate) fn out_of_bounds(idx: usize, len: usize) {
    panic!("Index out of bounds: {} (len: {})", idx, len);
}