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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#![no_std]
#![doc(html_root_url = "https://docs.rs/coca/0.3.0")]
#![cfg_attr(docs_rs, feature(doc_cfg))]
#![cfg_attr(feature = "unstable", feature(unsize))]
#![cfg_attr(feature = "unstable", feature(set_ptr_value))]
#![warn(missing_docs)]
#![warn(clippy::pedantic)]
#![allow(
    clippy::inline_always,
    clippy::missing_errors_doc,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::wildcard_imports,
)]

//! The `coca` crate provides collection types and other facilities for managing
//! memory without using the [`alloc` crate](https://doc.rust-lang.org/alloc/index.html).
//! 
//! ## Data Structures with Constant Capacity
//! 
//! Typical container implementations manage their own memory behind the scenes,
//! calling the global allocator (or a user-provided one, using the as yet
//! unstable [`Allocator` API](alloc::alloc::Allocator)) as needed when they
//! are modified.
//! 
//! This is convenient, but it does have some drawbacks to be aware of:
//! 
//! * reallocation may be slow, especially if the data structure is large and
//!   a lot of data has to be moved around,
//! * memory usage is essentially unbounded and must be carefully managed, should
//!   this pose a problem,
//! * such implementations simply cannot be used when no allocator is available,
//!   as may be the case in embedded systems.
//! 
//! In contrast, the data structures provided by `coca` do **not** work this way.
//! They operate on a given block of memory, and never reallocate. This means
//! that operations that grow a data structure may fail if the available space
//! is insufficient. For all such operations, two methods are provided: one,
//! returning a [`Result`](core::result::Result), has its name prefixed with
//! `try_`, the other, without the name prefix, being a wrapper that panics in
//! the error case.
//! 
//! Client code, then, has to either guarantee that the given memory block's
//! capacity will never be exceeded, or handle the failure case gracefully.
//! 
//! ## The `Storage` Abstraction
//! 
//! In `coca`, there is no single way of supplying a data structure with working
//! memory. Instead, most containers have a generic type parameter `S` bound by
//! the [`Storage` trait](storage::Storage), which is the type of the memory
//! block to be used. `Storage`, in turn, has a type parameter `R`, bound by
//! the [`LayoutSpec` trait](storage::LayoutSpec), which is used to describe
//! the memory [`Layout`](core::alloc::Layout) required by that container.
//! 
//! For instance, data structures built on an array (such as [`Vec`](collections::vec::Vec)
//! or [`String`](string::String)) require `S: Storage<ArrayLayout<T>>`, which is
//! implemented for standard arrays and slices, among others, while more complex
//! data structures (such as [`DirectPool`](collections::pool::direct::DirectPool))
//! have unique layout requirements (in this case `S: Storage<DirectPoolLayout<T, H>>`)
//! which are only fulfilled by purpose-built types.
//! 
//! No matter the layout requirements, for each data structure, the following
//! storage strategies are available:
//! 
//! * `InlineStorage`, defined as a [partially initialized array](storage::InlineStorage)
//!   or as purpose-built `struct`s for non-array-like data structures (e.g.
//!   [`collections::pool::direct::InlineStorage`]), requires the capacity to
//!   be truly `const`, i.e. statically known at compile time; this allows
//!   storing the contents inline with the top-level `struct` type, with no
//!   indirection, and thus entirely on the stack, for example.
//! * [`ArenaStorage`] is a (pointer, capacity) pair referencing an
//!   [arena-allocated](arena) block of memory, bounding the lifetime of the
//!   data structure to the lifetime of the `Arena`, but supports dynamically
//!   chosen capacities.
//! * Likewise, [`AllocStorage`](storage::AllocStorage) references a block of
//!   memory from the global allocator, requiring the `alloc` crate.
//! 
//! Note that, depending on the choice of storage type, available functionality
//! may differ slightly. For example, the [`Clone`] trait is only implemented
//! on data structures using `InlineStorage` or `AllocStorage`, but not
//! `ArenaStorage`.
//! 
//! Since concrete type names quickly become unwieldy in this scheme, `coca`
//! provides type aliases such as [`InlineVec`](collections::InlineVec),
//! [`ArenaVec`](collections::ArenaVec), and [`AllocVec`](collections::AllocVec)
//! for all of its data structures.
//! 
//! ## The `Capacity` Trait
//! 
//! Compared to the standard implementations, most of `coca`'s data structures
//! have one more additional type parameter, which is bound by the
//! [`Capacity` trait](storage::Capacity). This type used to index into the
//! data structure and to represent its size at runtime. It generally defaults
//! to `usize`, but `Capacity` is also implemented for `u8`, `u16`, `u32` and
//! `u64`.
//! 
//! This gives client code even more control over the exact size of the data
//! structure, but it has one additional advantage: using the [`index_type`]
//! macro, new types implementing the `Capacity` trait can be generated, which
//! then allows using different index types with different collections,
//! potentially preventing accidental use of the wrong index.
//! 
//! ## Cargo Features
//! 
//! - `alloc`: Enables an optional dependency on the `alloc` crate and adds
//!   the [`AllocStorage`](storage::AllocStorage) type, as well as other trait
//!   implementations and convenience functions for using the global allocator.
//! - `unstable`: Adds the [`object`] module providing a statically-sized
//!   container for dynamically-sized types. This relies on the unstable
//!   `feature(unsize)` and `feature(set_ptr_value)` and thus requires a nightly
//!   compiler.
//! - `profile`: Adds memory profiling in arena allocators. See the
//!   [module-level documentation](arena#memory-profiling) for details.
//! 
//! None of these features are enabled by default.

#[cfg(feature = "alloc")]
#[doc(hidden)]
pub extern crate alloc;

pub mod arena;
pub mod collections;
pub mod storage;
pub mod string;

#[cfg(feature = "unstable")]
#[cfg_attr(docs_rs, doc(cfg(feature = "unstable")))]
pub mod object;

#[cfg(feature = "unstable")]
#[cfg_attr(docs_rs, doc(cfg(feature = "unstable")))]
pub use crate::object::InlineObject;

use crate::string::String;
use crate::storage::{ArenaStorage, ArrayLayout, InlineStorage, SliceStorage};

/// A string using any mutable byte slice for storage.
/// 
/// # Examples
/// ```
/// let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); 8];
/// let str = coca::SliceString::<'_, usize>::from(&mut buf[..6]);
/// 
/// assert_eq!(str.capacity(), 6);
/// ```
pub type SliceString<'a, I = usize> = String<SliceStorage<'a, u8>, I>;
/// A string using an arena-allocated byte slice for storage.
/// 
/// # Examples
/// ```
/// use coca::arena::Arena;
/// use coca::ArenaString;
/// use core::mem::MaybeUninit;
///
/// let mut backing_region = [MaybeUninit::uninit(); 160];
/// let mut arena = Arena::from(&mut backing_region[..]);
///
/// let s: ArenaString<'_, usize> = arena.try_with_capacity(100).unwrap();
/// assert!(arena.try_with_capacity::<_, ArenaString<'_, usize>>(100).is_none());
/// ```
pub type ArenaString<'a, I = usize> = String<ArenaStorage<'a, ArrayLayout<u8>>, I>;

#[cfg(feature = "alloc")]
#[cfg_attr(docs_rs, doc(cfg(feature = "alloc")))]
/// A string using a heap-allocated slice for storage.
/// 
/// # Examples
/// ```
/// let mut s = coca::AllocString::with_capacity(16usize);
/// s.push_str("Hello, ");
/// s.push_str("World!");
/// 
/// assert_eq!(s, "Hello, World!");
/// ```
pub type AllocString<I = usize> = String<crate::storage::AllocStorage<ArrayLayout<u8>>, I>;

/// A string using an inline array for storage.
/// 
/// # Examples
/// ```
/// let mut s = coca::InlineString::<255, u8>::new();
/// assert_eq!(s.capacity(), 255);
/// assert!(s.is_empty());
/// ```
pub type InlineString<const C: usize, I = usize> = String<InlineStorage<u8, C>, I>;

/// The error type for many operations on data structures with constant capacity.
/// 
/// When working with data structures of limited capacity, insertions may fail
/// due to insufficient remaining space. In `coca`, insertion methods generally
/// have a name starting with `try`, and return a [`Result`](core::result::Result).
/// For convenience, panicking wrappers without the `try` prefix are also provided.
/// 
/// In many cases, such as e.g. [`Vec::try_push`](crate::collections::vec::Vec::try_push),
/// the value to be inserted is returned back to the caller when the operation fails;
/// in some cases, this is unnecessary (e.g. when ownership is not transferred, as with
/// [`Vec::try_extend_from_slice`](crate::collections::vec::Vec::try_extend_from_slice))
/// or would result in unwieldy type signatures. Such methods use this unit error type
/// instead.
#[derive(Copy, Clone, Debug, Default)]
pub struct CapacityError;

impl CapacityError {
    #[inline(always)]
    pub(crate) fn new<T>() -> core::result::Result<T, CapacityError> {
        Err(Self)
    }
}

/// A specialized [`Result`](core::result::Result) type for operations on data structures with constant capacity.
/// 
/// This type is broadly used across `coca` for most operations which grow a data structure.
/// 
/// This type definition is generally used to avoid writing out [`CapacityError`] directly and is otherwise a direct mapping to [`core::result::Result`].
pub type Result<T> = core::result::Result<T, CapacityError>;

#[cfg(test)]
mod test_utils {
    use core::cell::Cell;

    #[cfg(target_pointer_width = "64")]
    pub(crate) const RNG_SEED: [u8; 32] = [
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
    ];
    #[cfg(not(target_pointer_width = "64"))]
    pub(crate) const RNG_SEED: [u8; 16] = [
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
    ];

    #[derive(Debug)]
    pub(crate) struct DropCounter {
        drop_count: Cell<usize>,
    }

    impl DropCounter {
        pub(crate) fn new() -> Self {
            DropCounter { drop_count: Cell::new(0), }
        }

        pub(crate) fn new_droppable<T>(&self, value: T) -> Droppable<'_, T> {
            Droppable { counter: self, value }
        }

        pub(crate) fn dropped(&self) -> usize {
            self.drop_count.get()
        }
    }

    #[derive(Debug)]
    pub(crate) struct Droppable<'a, T = ()>{
        counter: &'a DropCounter,
        pub value: T,
    }

    impl<'a, T> Drop for Droppable<'a, T> {
        fn drop(&mut self) {
            let new_drop_count = self.counter.drop_count.get() + 1;
            self.counter.drop_count.set(new_drop_count);
        }
    }
}