Skip to main content

jstrict/object/
index_map.rs

1use super::{Entry, Key};
2use core::hash::{BuildHasher, Hash};
3use hashbrown::hash_table::Entry as TableEntry;
4use hashbrown::{DefaultHashBuilder, HashTable};
5
6/// Key lookup equivalence.
7///
8/// Lets an [`Object`](super::Object) be queried with any type that compares
9/// equal to its [`Key`], such as `str` or `String`. The blanket impl covers
10/// every `Q` a `Key` can be borrowed as.
11pub trait Equivalent<K: ?Sized> {
12	/// Checks if `self` is equivalent to `key`.
13	fn equivalent(&self, key: &K) -> bool;
14}
15
16impl<Q: ?Sized + Eq, K: ?Sized> Equivalent<K> for Q
17where
18	K: std::borrow::Borrow<Q>,
19{
20	fn equivalent(&self, key: &K) -> bool {
21		self == key.borrow()
22	}
23}
24
25fn equivalent_key<'a, Q>(entries: &'a [Entry], k: &'a Q) -> impl 'a + Fn(&Indexes) -> bool
26where
27	Q: ?Sized + Equivalent<Key>,
28{
29	move |indexes| k.equivalent(&entries[indexes.rep].key)
30}
31
32fn make_hasher() -> impl Fn(&Indexes) -> u64 {
33	|indexes| indexes.hash
34}
35
36#[derive(Clone, Debug)]
37pub struct Indexes {
38	/// Index of the first entry with the considered key (the representative).
39	rep: usize,
40
41	/// Other indexes with this key.
42	other: Vec<usize>,
43
44	/// Cached hash of the key shared by every index in this slot. Used by
45	/// `make_hasher` so table grow does not re-hash keys.
46	hash: u64,
47}
48
49impl PartialEq for Indexes {
50	fn eq(&self, other: &Self) -> bool {
51		self.rep == other.rep && self.other == other.other
52	}
53}
54
55impl Eq for Indexes {}
56
57impl Indexes {
58	const fn new(rep: usize, hash: u64) -> Self {
59		Self {
60			rep,
61			other: Vec::new(),
62			hash,
63		}
64	}
65
66	pub fn len(&self) -> usize {
67		1 + self.other.len()
68	}
69
70	pub const fn first(&self) -> usize {
71		self.rep
72	}
73
74	pub fn is_redundant(&self) -> bool {
75		!self.other.is_empty()
76	}
77
78	pub fn redundant(&self) -> Option<usize> {
79		self.other.first().copied()
80	}
81
82	pub fn redundants(&self) -> &[usize] {
83		&self.other
84	}
85
86	fn insert(&mut self, mut index: usize) {
87		if index != self.rep {
88			if index < self.rep {
89				core::mem::swap(&mut index, &mut self.rep);
90			}
91
92			if let Err(i) = self.other.binary_search(&index) {
93				self.other.insert(i, index)
94			}
95		}
96	}
97
98	/// Removes the given index, unless it is the last remaining index.
99	///
100	/// Returns `true` if the index has been removed or not in the list,
101	/// and `false` if it was the last index (and hence not removed).
102	fn remove(&mut self, index: usize) -> bool {
103		if self.rep == index {
104			if self.other.is_empty() {
105				false
106			} else {
107				self.rep = self.other.remove(0);
108				true
109			}
110		} else {
111			if let Ok(i) = self.other.binary_search(&index) {
112				self.other.remove(i);
113			}
114
115			true
116		}
117	}
118
119	/// Decreases all index greater than `index` by one.
120	pub fn shift_down(&mut self, index: usize) {
121		if self.rep > index {
122			self.rep -= 1
123		}
124
125		for i in &mut self.other {
126			if *i > index {
127				*i -= 1
128			}
129		}
130	}
131
132	/// Increases all index greater than or equal to `index` by one.
133	pub fn shift_up(&mut self, index: usize) {
134		if self.rep >= index {
135			self.rep += 1
136		}
137
138		for i in &mut self.other {
139			if *i >= index {
140				*i += 1
141			}
142		}
143	}
144
145	pub fn iter(&self) -> super::Indexes<'_> {
146		super::Indexes::Some {
147			first: Some(self.rep),
148			other: self.other.iter(),
149		}
150	}
151}
152
153impl<'a> IntoIterator for &'a Indexes {
154	type Item = usize;
155	type IntoIter = super::Indexes<'a>;
156
157	fn into_iter(self) -> Self::IntoIter {
158		self.iter()
159	}
160}
161
162#[derive(Clone)]
163pub struct IndexMap<S = DefaultHashBuilder> {
164	hash_builder: S,
165	table: HashTable<Indexes>,
166}
167
168impl<S: Default> Default for IndexMap<S> {
169	fn default() -> Self {
170		Self {
171			hash_builder: S::default(),
172			table: HashTable::new(),
173		}
174	}
175}
176
177impl<S> IndexMap<S> {
178	pub fn new() -> Self
179	where
180		S: Default,
181	{
182		Self::default()
183	}
184
185	pub fn with_capacity(capacity: usize) -> Self
186	where
187		S: Default,
188	{
189		Self {
190			hash_builder: S::default(),
191			table: HashTable::with_capacity(capacity),
192		}
193	}
194
195	pub fn contains_duplicate_keys(&self) -> bool {
196		self.table.iter().any(Indexes::is_redundant)
197	}
198}
199
200impl<S: BuildHasher> IndexMap<S> {
201	pub fn get<Q>(&self, entries: &[Entry], key: &Q) -> Option<&Indexes>
202	where
203		Q: ?Sized + Hash + Equivalent<Key>,
204	{
205		let hash = self.hash_builder.hash_one(key);
206		self.table.find(hash, equivalent_key(entries, key))
207	}
208
209	/// Associates the given `key` to `index`.
210	///
211	/// Returns `true` if no index was already associated to the key.
212	///
213	/// Performs a single hash and a single probe regardless of whether
214	/// the key already exists.
215	pub fn insert(&mut self, entries: &[Entry], index: usize) -> bool {
216		let key = &entries[index].key;
217		let hash = self.hash_builder.hash_one(key);
218		match self
219			.table
220			.entry(hash, equivalent_key(entries, key), make_hasher())
221		{
222			TableEntry::Occupied(mut occupied) => {
223				occupied.get_mut().insert(index);
224				false
225			}
226			TableEntry::Vacant(vacant) => {
227				vacant.insert(Indexes::new(index, hash));
228				true
229			}
230		}
231	}
232
233	/// Single-hash key probe. If the key already maps to an index, returns
234	/// `Some(rep)` where `rep` is the first index for this key. Otherwise
235	/// reserves a slot for `new_index` and returns `None`. When `None` is
236	/// returned, the caller MUST push the corresponding entry to `entries`
237	/// at position `new_index` before any other `IndexMap` operation.
238	pub fn lookup_or_insert<Q>(
239		&mut self,
240		entries: &[Entry],
241		new_index: usize,
242		key: &Q,
243	) -> Option<usize>
244	where
245		Q: ?Sized + Hash + Equivalent<Key>,
246	{
247		let hash = self.hash_builder.hash_one(key);
248		match self
249			.table
250			.entry(hash, equivalent_key(entries, key), make_hasher())
251		{
252			TableEntry::Occupied(occupied) => Some(occupied.get().first()),
253			TableEntry::Vacant(vacant) => {
254				vacant.insert(Indexes::new(new_index, hash));
255				None
256			}
257		}
258	}
259
260	/// Removes the association between the given key and index.
261	pub fn remove(&mut self, entries: &[Entry], index: usize) {
262		let key = &entries[index].key;
263		let hash = self.hash_builder.hash_one(key);
264		if let Ok(mut occupied) = self.table.find_entry(hash, equivalent_key(entries, key)) {
265			if !occupied.get_mut().remove(index) {
266				occupied.remove();
267			}
268		}
269	}
270
271	/// Decreases all index greater than `index` by one everywhere in the table.
272	pub fn shift_down(&mut self, index: usize) {
273		for indexes in self.table.iter_mut() {
274			indexes.shift_down(index);
275		}
276	}
277
278	/// Increases all index greater than or equal to `index` by one everywhere in the table.
279	pub fn shift_up(&mut self, index: usize) {
280		for indexes in self.table.iter_mut() {
281			indexes.shift_up(index);
282		}
283	}
284
285	/// Rebuilds the whole table from `entries`, which **must** be sorted by key.
286	///
287	/// Sorting groups equal keys into contiguous runs, which lets this skip work
288	/// that [`insert`](Self::insert) has to do per entry:
289	///
290	/// - each distinct key is hashed once, not once per entry sharing it;
291	/// - each run needs a single `insert_unique` rather than a probe per entry,
292	///   because a run cannot collide with an already-inserted key;
293	/// - indexes within a run arrive in ascending order, so `other` is filled by
294	///   `extend` instead of the `binary_search` + `Vec::insert` pair in
295	///   [`Indexes::insert`], which is quadratic in the length of the run.
296	pub fn rebuild_sorted(&mut self, entries: &[Entry]) {
297		self.table.clear();
298		self.table.reserve(entries.len(), make_hasher());
299
300		let mut run_start = 0;
301		while run_start < entries.len() {
302			let key = &entries[run_start].key;
303			let mut run_end = run_start + 1;
304			while run_end < entries.len() && entries[run_end].key == *key {
305				run_end += 1;
306			}
307
308			let hash = self.hash_builder.hash_one(key);
309			let mut indexes = Indexes::new(run_start, hash);
310			indexes.other.reserve_exact(run_end - run_start - 1);
311			indexes.other.extend(run_start + 1..run_end);
312			self.table.insert_unique(hash, indexes, make_hasher());
313
314			run_start = run_end;
315		}
316	}
317}
318
319#[cfg(test)]
320mod tests {
321	use super::*;
322	use crate::Value;
323
324	#[test]
325	fn insert() {
326		let entries = [
327			Entry::new("a".into(), Value::Null),
328			Entry::new("b".into(), Value::Null),
329			Entry::new("a".into(), Value::Null),
330		];
331
332		let mut indexes: IndexMap = IndexMap::default();
333		indexes.insert(&entries, 2);
334		indexes.insert(&entries, 1);
335		indexes.insert(&entries, 0);
336
337		let mut a = indexes.get(&entries, "a").unwrap().iter();
338		let mut b = indexes.get(&entries, "b").unwrap().iter();
339
340		assert_eq!(a.next(), Some(0));
341		assert_eq!(a.next(), Some(2));
342		assert_eq!(a.next(), None);
343		assert_eq!(b.next(), Some(1));
344		assert_eq!(b.next(), None);
345		assert_eq!(indexes.get(&entries, "c"), None)
346	}
347
348	#[test]
349	fn remove1() {
350		let entries = [
351			Entry::new("a".into(), Value::Null),
352			Entry::new("b".into(), Value::Null),
353			Entry::new("a".into(), Value::Null),
354		];
355
356		let mut indexes: IndexMap = IndexMap::default();
357		indexes.insert(&entries, 2);
358		indexes.insert(&entries, 1);
359		indexes.insert(&entries, 0);
360
361		indexes.remove(&entries, 1);
362		indexes.remove(&entries, 0);
363
364		let mut a = indexes.get(&entries, "a").unwrap().iter();
365
366		assert_eq!(a.next(), Some(2));
367		assert_eq!(a.next(), None);
368		assert_eq!(indexes.get(&entries, "b"), None)
369	}
370
371	#[test]
372	fn remove2() {
373		let entries = [
374			Entry::new("a".into(), Value::Null),
375			Entry::new("b".into(), Value::Null),
376			Entry::new("a".into(), Value::Null),
377		];
378
379		let mut indexes: IndexMap = IndexMap::default();
380		indexes.insert(&entries, 2);
381		indexes.insert(&entries, 1);
382		indexes.insert(&entries, 0);
383
384		indexes.remove(&entries, 0);
385		indexes.remove(&entries, 1);
386		indexes.remove(&entries, 2);
387
388		assert_eq!(indexes.get(&entries, "a"), None);
389		assert_eq!(indexes.get(&entries, "b"), None)
390	}
391}