imbl_sized_chunks/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! # Sized Chunks
6//!
7//! This crate contains three fixed size low level array like data structures,
8//! primarily intended for use in [immutable.rs], but fully supported as a
9//! standalone crate.
10//!
11//! Their sizing information is encoded in the type using const generics.
12//! For example, `SparseChunk<A, 32>` would give you a sparse array with
13//! room for 32 elements of type `A`. You can also omit the size, as they all
14//! default to a size of 64, so `SparseChunk<A>` would be a sparse array with a
15//! capacity of 64.
16//!
17//! All data structures always allocate the same amount of space, as determined
18//! by their capacity, regardless of how many elements they contain, and when
19//! they run out of space, they will panic.
20//!
21//! ## Data Structures
22//!
23//! | Type | Description | Push | Pop | Deref to `&[A]` |
24//! | ---- | ----------- | ---- | --- | --------------- |
25//! | [`Chunk`][Chunk] | Contiguous array | O(1)/O(n) | O(1) | Yes |
26//! | [`RingBuffer`][RingBuffer] | Non-contiguous array | O(1) | O(1) | No |
27//! | [`SparseChunk`][SparseChunk] | Sparse array | N/A | N/A | No |
28//!
29//! The [`Chunk`][Chunk] and [`RingBuffer`][RingBuffer] are very similar in
30//! practice, in that they both work like a plain array, except that you can
31//! push to either end with some expectation of performance. The difference is
32//! that [`RingBuffer`][RingBuffer] always allows you to do this in constant
33//! time, but in order to give that guarantee, it doesn't lay out its elements
34//! contiguously in memory, which means that you can't dereference it to a slice
35//! `&[A]`.
36//!
37//! [`Chunk`][Chunk], on the other hand, will shift its contents around when
38//! necessary to accommodate a push to a full side, but is able to guarantee a
39//! contiguous memory layout in this way, so it can always be dereferenced into
40//! a slice. Performance wise, repeated pushes to the same side will always run
41//! in constant time, but a push to one side followed by a push to the other
42//! side will cause the latter to run in linear time if there's no room (which
43//! there would only be if you've popped from that side).
44//!
45//! To choose between them, you can use the following rules:
46//! - I only ever want to push to the back: you don't need this crate, try
47//! [`ArrayVec`][ArrayVec].
48//! - I need to push to either side but probably not both on the same array: use
49//! [`Chunk`][Chunk].
50//! - I need to push to both sides and I don't need slices: use
51//! [`RingBuffer`][RingBuffer].
52//! - I need to push to both sides but I do need slices: use [`Chunk`][Chunk].
53//!
54//! Finally, [`SparseChunk`][SparseChunk] is a more efficient version of
55//! `Vec<Option<A>>`: each index is either inhabited or not, but instead of
56//! using the `Option` discriminant to decide which is which, it uses a compact
57//! bitmap. You can also think of `SparseChunk<A, N>` as a `BTreeMap<usize, A>`
58//! where the `usize` must be less than `N`, but without the performance
59//! overhead. Its API is also more consistent with a map than an array - there's
60//! no push, pop, append, etc, just insert, remove and lookup.
61//!
62//! # [`InlineArray`][InlineArray]
63//!
64//! Finally, there's [`InlineArray`][InlineArray], which is a simple vector that's
65//! sized to fit inside any `Sized` type that's big enough to hold a size counter
66//! and at least one instance of the array element type. This can be a useful
67//! optimisation when implementing a list like data structure with a nontrivial
68//! set of pointers in its full form, where you could plausibly fit several
69//! elements inside the space allocated for the pointers. `imbl::Vector` is a
70//! good example of that, and the use case for which [`InlineArray`][InlineArray]
71//! was implemented.
72//!
73//! # Feature Flags
74//!
75//! The following feature flags are available:
76//!
77//! | Feature | Description |
78//! | ------- | ----------- |
79//! | `arbitrary` | Provides [`Arbitrary`][Arbitrary] implementations from the [`arbitrary`][arbitrary_crate] crate. Requires the `std` flag. |
80//! | `refpool` | Provides [`PoolDefault`][PoolDefault] and [`PoolClone`][PoolClone] implemetations from the [`refpool`][refpool] crate. |
81//! | `ringbuffer` | Enables the [`RingBuffer`][RingBuffer] data structure. |
82//! | `std` | Without this flag (enabled by default), the crate will be `no_std`, and absent traits relating to `std::collections` and `std::io`. |
83//!
84//! [immutable.rs]: https://immutable.rs/
85//! [typenum]: https://docs.rs/typenum/
86//! [Chunk]: crate::Chunk
87//! [RingBuffer]: crate::RingBuffer
88//! [SparseChunk]: crate::SparseChunk
89//! [InlineArray]: crate::InlineArray
90//! [ArrayVec]: https://docs.rs/arrayvec/
91//! [Arbitrary]: https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html
92//! [arbitrary_crate]: https://docs.rs/arbitrary
93//! [refpool]: https://docs.rs/refpool
94//! [PoolDefault]: https://docs.rs/refpool/latest/refpool/trait.PoolDefault.html
95//! [PoolClone]: https://docs.rs/refpool/latest/refpool/trait.PoolClone.html
96
97#![forbid(rust_2018_idioms)]
98#![deny(nonstandard_style)]
99#![warn(unreachable_pub, missing_docs)]
100#![cfg_attr(test, deny(warnings))]
101#![cfg_attr(not(any(feature = "std", test)), no_std)]
102
103pub mod inline_array;
104pub mod sized_chunk;
105pub mod sparse_chunk;
106
107#[cfg(test)]
108mod tests;
109
110#[cfg(feature = "arbitrary")]
111mod arbitrary;
112
113pub use crate::inline_array::InlineArray;
114pub use crate::sized_chunk::Chunk;
115pub use crate::sparse_chunk::SparseChunk;
116
117#[cfg(feature = "ringbuffer")]
118pub mod ring_buffer;
119#[cfg(feature = "ringbuffer")]
120pub use crate::ring_buffer::RingBuffer;
121
122#[cfg(test)]
123mod covariance_tests {
124 #![allow(unused_assignments, unused_variables, dead_code)]
125
126 use super::*;
127
128 // Check that all our types are covariant in their parameter.
129 macro_rules! assert_covariant {
130 ($name:ty, $param:ident) => {
131 const _: () = {
132 type Tmp<$param> = $name;
133 fn assign<'a, 'b: 'a>(src: Tmp<&'b i32>, mut dst: Tmp<&'a i32>) {
134 dst = src;
135 }
136 };
137 };
138 }
139
140 assert_covariant!(InlineArray<T, Vec<T>>, T);
141 assert_covariant!(Chunk<T, 64>, T);
142 assert_covariant!(SparseChunk<T, 64>, T);
143
144 #[cfg(feature = "ringbuffer")]
145 assert_covariant!(RingBuffer<T, 64>, T);
146}