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
//! Allocation helpers.
//!
//! Port of OpenFst's `memory.h` and `arc-arena.h`.
//!
//! # What `memory.h` is for, and what carries over
//!
//! Upstream's `memory.h` exists to keep node-based STL containers from calling
//! the global allocator once per element. It provides a bump arena
//! (`MemoryArena`), a free-list pool (`MemoryPool`), collections of both keyed
//! by `sizeof(T)`, and two STL allocators built on them (`BlockAllocator`,
//! `PoolAllocator`).
//!
//! | Upstream | sicada |
//! | --- | --- |
//! | `MemoryPool<T>` | [`MemoryPool<T>`], a [`slab::Slab`] |
//! | `MemoryArena<T>` | superseded; see below |
//! | `BlockAllocator<T>` | not applicable |
//! | `PoolAllocator<T>` | not applicable |
//! | `MemoryArenaCollection`, `MemoryPoolCollection` | not applicable |
//!
//! `MemoryPool` is a real dependency where objects are freed out of order:
//! `cache.rs` evicts arbitrary states, so its slab's freed slots are genuinely
//! reused. A slab gives the same O(1) allocate/free with slot reuse, keyed by
//! an index rather than a pointer, without any unsafe code.
//!
//! It is *not* the answer where upstream reaches for it out of habit. Upstream
//! pools `DfsState` in `dfs-visit.h` and `ArcIterator` in `visit.h`, but both
//! are pushed and popped in strict last-in-first-out order: a pool standing in
//! for a stack, because C++ has no way to hold a non-movable iterator in a
//! `std::vector`. sicada's `dfs_visit.rs` uses a `Vec` directly, which is the
//! same allocation behaviour with none of the indirection.
//!
//! SICADA-OPT: the two STL allocators have no sicada counterpart because the
//! containers that needed them do not exist here. `PoolAllocator` is used
//! upstream by `bi-table.h`'s `std::unordered_set`, by `cache.h`'s
//! `std::list<StateId>`, and by its state hash map, all node-per-element
//! structures whose allocation traffic the pool is there to absorb. sicada uses
//! `hashbrown`/`rustc-hash` open-addressed tables and `Vec`, which allocate in
//! blocks rather than per element, so the pool has nothing to amortize. Adding
//! an allocator layer would be pure overhead, and custom allocators are not
//! available on stable Rust anyway (`allocator_api` is unstable).
//!
//! `MemoryArena` and `BlockAllocator` have no users at all in `openfst/lib`:
//! `BlockAllocator` is the arena's only consumer, and nothing constructs one.
//! The one arena sicada actually needs is the contiguous arc allocator, which is
//! [`ArcArena`], ported from `arc-arena.h`.
pub use ;
use Slab;
/// Pool of same-typed objects with O(1) allocation and free-slot reuse.
///
/// Corresponds to OpenFst's `MemoryPool<T>`. Objects are addressed by an index
/// rather than a pointer: `insert` returns a key, `remove` returns the value and
/// releases the slot for the next `insert`.
pub type MemoryPool<T> = ;