kollect/lib.rs
1//! # 🗂️ `kollect`
2//!
3//! A set of collections optimized for game development usecases.
4//!
5//! # Provided Collections
6//!
7//! - [`LinearMap`], useful when you want a collection that behaves as a key-value map but you will only ever have
8//! up to a few tens of elements. For small numbers of elements and small size of contained key/value types, this
9//! will likely be faster and take up less memory than other map types, but its random access operations have O(len)
10//! complexity rather than O(1) as with hash-based maps.
11//! - [`UnorderedMap`], useful when you want a general-purpose key-value map, you plan to do random access
12//! lookup by key more frequently than iteration of the contained elements, and you don't care about order of
13//! those elements.
14//! - [`OrderedMap`], useful when you want a key-value map including a set order of element pairs, or when
15//! you plan to iterate over the contained elements more frequently than you do random access lookup by key.
16//!
17//! - [`LinearSet`], useful in the same situation as [`LinearMap`] but when you're operating on a set of values rather
18//! than a map.
19//! - [`UnorderedSet`], useful when you want set-like operations, will do more random access than iteration,
20//! and will fill with a medium to high number of elements.
21//! - [`OrderedSet`], useful when you want set-like operations and need a defined order of elements, or you
22//! plan to iterate over the contained elements more frequently than do random access lookup on them.
23//!
24//! # Usage table
25//!
26//! Number of elements | Access pattern | Need defined order | Choose
27//! ---|---|---|---
28//! Less than ~128 | Any | Any | [`LinearMap`]/[`LinearSet`]
29//! More than ~128 | More Random Access | No | [`UnorderedMap`]/[`UnorderedSet`]
30//! More than ~128 | More Iteration | No | [`OrderedMap`]/[`OrderedSet`]
31//! More than ~128 | Any | Yes | [`OrderedMap`]/[`OrderedSet`]
32//!
33//! # Specialized hashers
34//!
35//! The [`specialized_hashers`] module contains some helpful specialized hasher variants that can speed up your
36//! hash-based collections when you have knowledge about the keys/elements being used.
37//!
38//! For example, if your keys in a map or set are small and primitive-integer-only, then you may want to use a
39//! [`BuildPrimitiveHasher`]-based alias of one of the hash-based collections ([`UnorderedPrimitiveMap`], etc.).
40//!
41//! Alternatively, if you are keying a map or creating a collection of elements which are themselves already a
42//! hash value (or which otherwise qualify as [`NoHashable`]), then you may want to use one of the [`BuildNoHashHasher`]-based
43//! aliases of one of the hash-based collections ([`UnorderedNoHashMap`], etc.).
44//!
45//! # `serde_as_seq` for maps
46//!
47//! When the `serde` feature is enabled, there are adapters usable with `#[serde(with = "path::to::module")]` at `kollect::some_map::serde_as_seq`
48//! which will serialize and deserialize those maps as sequences of `(k, v)` pair elements rather than as native serde Maps.
49//! This is useful with JSON because JSON maps can only have string keys, and if you try to serialize a map to JSON which has a key type
50//! that can't be serialized as a string, the serde JSON impl will simply panic at runtime. Using `serde_as_seq` will circumvent this issue
51//! and allow you to serialize a wider range of key types.
52//!
53//! # Feature flags
54//!
55//! `kollect` uses a set of [feature flags] to optionally reduce the number of dependencies.
56//!
57//! The following optional features are available:
58//!
59//! Name | Description | Default?
60//! ---|---|---
61//! `speedy` | Enables [`speedy`] support for most types | No
62//! `serde` | Enables [`serde`] support for most types | No
63//!
64//! [`speedy`]: https://crates.io/crates/speedy
65//! [`serde`]: https://crates.io/crates/serde
66//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html
67
68#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
69#![cfg_attr(test, allow(clippy::float_cmp))]
70
71use core::hash::Hash;
72
73use specialized_hashers::BuildNoHashHasher;
74use specialized_hashers::BuildPrimitiveHasher;
75
76/// The default hasher used by hash-based collections in this crate.
77pub type BuildHasher = foldhash::fast::RandomState;
78
79/// Provides a fast, general purpose, key-value map with **no** defined order of elements
80pub mod unordered_map;
81
82#[doc(inline)]
83pub use unordered_map::UnorderedMap;
84
85/// Type-alias of [`UnorderedMap`] that is useful when you are using small keys consisting of only a few primitive types.
86pub type UnorderedPrimitiveMap<K, V> = UnorderedMap<K, V, BuildPrimitiveHasher>;
87
88/// Provides a fast, general purpose, deduplicated set type with **no** defined order of elements
89pub mod unordered_set;
90#[doc(inline)]
91pub use unordered_set::UnorderedSet;
92
93/// Type-alias of [`UnorderedSet`] that is useful when you are using small elements consisting of only a few primitive types.
94pub type UnorderedPrimitiveSet<T> = UnorderedSet<T, BuildPrimitiveHasher>;
95
96/// Provides a fast, general purpose, key-value map **with** a defined order of elements.
97pub mod ordered_map;
98#[doc(inline)]
99pub use ordered_map::OrderedMap;
100
101/// Type-alias of [`OrderedMap`] that is useful when you are using small keys consisting of only a few primitive types.
102pub type OrderedPrimitiveMap<K, V> = OrderedMap<K, V, BuildPrimitiveHasher>;
103
104/// Provides a fast, general purpose, deduplicated set type **with* a defined order of elements.
105pub mod ordered_set;
106#[doc(inline)]
107pub use ordered_set::OrderedSet;
108
109/// Type-alias of [`OrderedSet`] that is useful when you are using small elements consisting of only a few primitive types.
110pub type OrderedPrimitiveSet<T> = OrderedSet<T, BuildPrimitiveHasher>;
111
112/// A key-value map specialized for small numbers of elements, implemented by searching linearly in a vector.
113pub mod linear_map;
114#[doc(inline)]
115pub use linear_map::LinearMap;
116
117/// A set specialized for small numbers of elements, implemented by searching linearly in a vector.
118pub mod linear_set;
119#[doc(inline)]
120pub use linear_set::LinearSet;
121
122/// Type-alias of [`UnorderedMap`] that is useful when you are using keys that are [`NoHashable`]
123pub type UnorderedNoHashMap<K, V> = UnorderedMap<K, V, BuildNoHashHasher<K>>;
124
125/// Type-alias of [`UnorderedSet`] that is useful when you are using elements that are [`NoHashable`]
126pub type UnorderedNoHashSet<T> = UnorderedSet<T, BuildNoHashHasher<T>>;
127
128/// Type-alias of [`OrderedMap`] that is useful when you are using keys that are [`NoHashable`]
129pub type OrderedNoHashMap<K, V> = OrderedMap<K, V, BuildNoHashHasher<K>>;
130
131/// Type-alias of [`OrderedSet`] that is useful when you are using elements that are [`NoHashable`]
132pub type OrderedNoHashSet<T> = OrderedSet<T, BuildNoHashHasher<T>>;
133
134/// Contains [`BuildHasher`][core::hash::BuildHasher]s (which can be used with other hash-based collections provided by this crate)
135/// specialized for specific types of elements or situations.
136pub mod specialized_hashers;
137
138#[doc(inline)]
139pub use specialized_hashers::NoHashable;
140
141#[cfg(test)]
142mod tests;
143
144#[macro_use]
145mod internal_macros;
146
147const STATIC_RANDOM_SEED: u64 = 0x86c11a44c63f4f2f;
148
149pub static FIXED_BUILD_HASHER: foldhash::fast::FixedState =
150 foldhash::fast::FixedState::with_seed(STATIC_RANDOM_SEED);
151
152#[inline(always)]
153pub fn get_fixed_hasher() -> foldhash::fast::FoldHasher {
154 use core::hash::BuildHasher;
155 FIXED_BUILD_HASHER.build_hasher()
156}
157
158#[inline(always)]
159pub fn hash_one_fixed<H: Hash>(one: H) -> u64 {
160 use core::hash::Hasher;
161 let mut hasher = foldhash::fast::FoldHasher::with_seed(
162 STATIC_RANDOM_SEED,
163 foldhash::SharedSeed::global_fixed(),
164 );
165 one.hash(&mut hasher);
166 hasher.finish()
167}
168
169#[cold]
170#[inline(never)]
171fn panic_key_already_existed() {
172 panic!("Failed to insert to map as the key already existed");
173}