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
//! Caches strings efficiently, with minimal memory footprint and associates them with unique symbols.
//! These symbols allow constant time comparisons and look-ups to the underlying interned strings.
//!
//! ### Example: Interning & Symbols
//!
//! ```
//! // An interner with default symbol type and hasher
//! use string_hash_interner::DefaultStringInterner;
//!
//! let mut interner = DefaultStringInterner::default();
//! let sym0 = interner.intern("Elephant");
//! let sym1 = interner.intern("Tiger");
//! let sym2 = interner.intern("Horse");
//! let sym3 = interner.intern("Tiger");
//! assert_ne!(sym0, sym1);
//! assert_ne!(sym0, sym2);
//! assert_ne!(sym1, sym2);
//! assert_eq!(sym1, sym3); // same!
//! ```
//!
//! ### Example: Creation by `FromIterator`
//!
//! ```
//! # use string_hash_interner::DefaultStringInterner;
//! let interner = ["Elephant", "Tiger", "Horse", "Tiger"]
//! .into_iter()
//! .collect::<DefaultStringInterner>();
//! ```
//!
//! ### Example: Look-up
//!
//! ```
//! # use string_hash_interner::DefaultStringInterner;
//! let mut interner = DefaultStringInterner::default();
//! let sym = interner.intern("Banana");
//! assert_eq!(interner.resolve(sym), Some("Banana"));
//! ```
//!
//! ### Example: Iteration
//!
//! ```
//! # use string_hash_interner::{DefaultStringInterner, Symbol};
//! let interner = DefaultStringInterner::from_iter(["Earth", "Water", "Fire", "Air"]);
//! for (sym, str) in &interner {
//! println!("{} = {}", sym.to_usize(), str);
//! }
//! ```
//!
//! ### Example: Use different symbols and hashers
//!
//! ```
//! # use string_hash_interner::StringInterner;
//! use string_hash_interner::symbol::SymbolU16;
//! use fxhash::FxBuildHasher;
//! let mut interner = StringInterner::<SymbolU16, FxBuildHasher>::new();
//! let sym = interner.intern("Fire Fox");
//! assert_eq!(interner.resolve(sym), Some("Fire Fox"));
//! assert_eq!(size_of_val(&sym), 2);
//! ```
//!
//! ### Example: Intern different types of strings
//!
//! ```
//! use string_hash_interner::Interner;
//! use std::ffi::CStr;
//!
//! let strings = <Interner<CStr>>::from_iter([c"Earth", c"Water", c"Fire", c"Air"]);
//!
//! for (_sym, str) in &strings {
//! println!("This is a C string: {:?}", str);
//! }
//! ```
//!
//! ### Example: Use cached hashes for faster hashmap lookups
//!
//! ```
//! # use string_hash_interner::DefaultStringInterner;
//! # use string_hash_interner::DefaultHashBuilder;
//! # use hashbrown::hash_map::RawEntryMut;
//! // `DefaultHashBuilder` uses random state, so we need to use
//! // the same instance in order for hashes to match.
//! let build_hasher = DefaultHashBuilder::default();
//!
//! let mut hashmap = hashbrown::HashMap::with_hasher(build_hasher);
//! hashmap.extend([("Earth", 1), ("Water", 2), ("Fire", 3), ("Air", 4)]);
//!
//! let mut interner = DefaultStringInterner::with_hasher(build_hasher);
//! let sym = interner.intern("Water");
//!
//! // Now, if we need to lookup the entry in the hashmap and we
//! // only have the symbol, we don't need to recompute the hash.
//!
//! let string = interner.resolve(sym).unwrap();
//! let hash = interner.get_hash(sym).unwrap();
//!
//! let (k, v) = hashmap
//! .raw_entry()
//! .from_key_hashed_nocheck(hash, string)
//! .unwrap();
//!
//! assert_eq!(*k, "Water");
//! assert_eq!(*v, 2)
//! ```
//!
//! ### Example: Hashmap with only interned strings
//!
//! ```
//! # use string_hash_interner::symbol::DefaultSymbol;
//! # use string_hash_interner::DefaultStringInterner;
//! # use hashbrown::hash_map::RawEntryMut;
//! let mut interner = DefaultStringInterner::default();
//!
//! let symbols = ["Earth", "Water", "Fire", "Air", "Air", "Water"].map(|s| interner.intern(s));
//!
//! // Now, using symbols we can fill the hashmap without ever recomputing hashes.
//!
//! // Use `()` as a hasher, as we'll be using cached hashes.
//! let mut counts = hashbrown::HashMap::<DefaultSymbol, usize, ()>::default();
//!
//! for symbol in symbols {
//! // SAFETY: we now these symbols are coming from this interner
//! let hash = unsafe { interner.get_hash_unchecked(symbol) };
//! let hasher = |sym: &DefaultSymbol| unsafe { interner.get_hash_unchecked(*sym) };
//!
//! match counts.raw_entry_mut().from_key_hashed_nocheck(hash, &symbol) {
//! RawEntryMut::Occupied(mut entry) => {
//! *entry.get_mut() += 1;
//! }
//! RawEntryMut::Vacant(entry) => {
//! entry.insert_with_hasher(hash, symbol, 1, hasher);
//! }
//! }
//! }
//!
//! for (sym, count) in &counts {
//! println!("{:?} appeared {} times", interner.resolve(*sym).unwrap(), count);
//! }
//! ```
//!
extern crate alloc;
extern crate std;
pub use ;
pub use DefaultHashBuilder;
/// [`Interner`] for [`str`]'s.
pub type StringInterner<S = DefaultSymbol, H = DefaultHashBuilder> = ;
/// [`StringInterner`] with default Symbol and Hasher.
pub type DefaultStringInterner = StringInterner;