Skip to content

Commit fdca55f

Browse files
committed
Remove Item and move generation into SparseMap
Lower version bump to minor
1 parent 8ac7231 commit fdca55f

3 files changed

Lines changed: 39 additions & 128 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sparse_map"
3-
version = "0.2.0"
3+
version = "0.1.1"
44
edition = "2024"
55
license = "MIT OR Apache-2.0"
66
description = "A sparse map with stable generational keys."

src/lib.rs

Lines changed: 37 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use alloc::vec::Vec;
2121
/// - Keys are invalidated once their value is removed.
2222
#[derive(Debug)]
2323
pub struct SparseMap<T> {
24-
buffer: Vec<Item<T>>,
24+
buffer: Vec<Option<T>>,
25+
generations: Vec<u32>,
2526
empty_slots: Vec<usize>,
2627
}
2728

@@ -37,56 +38,37 @@ impl<T> SparseMap<T> {
3738
/// its generation counter is incremented to invalidate old keys.
3839
#[must_use = "The returned key is the only way to reference back the inserted value!"]
3940
pub fn insert(&mut self, value: T) -> Key {
40-
self.alloc_slot(
41-
value,
42-
|value, item| item.replace(value),
43-
|value| Item::new(value),
44-
)
41+
self.insert_with_key(|_, _| value)
4542
}
4643

4744
/// Similar to [`Self::insert()`] but provides a [`Key`] before
4845
/// inserting the value.
49-
pub fn insert_with_key<F>(&mut self, f: F) -> Key
46+
fn insert_with_key<F>(&mut self, create: F) -> Key
5047
where
5148
F: FnOnce(&mut Self, Key) -> T,
5249
{
53-
let key = self.alloc_slot(
54-
(),
55-
|_, item| item.replace_empty(),
56-
|_| Item::new_empty(),
57-
);
50+
if let Some(index) = self.empty_slots.pop() {
51+
// Increment the generation counter.
52+
let mut generation = self.generations[index];
53+
generation = generation.wrapping_add(1);
54+
self.generations[index] = generation;
5855

59-
let value = f(self, key);
60-
self.buffer[key.index].inner = Some(value);
56+
let key = Key::new(index, generation);
6157

62-
key
63-
}
58+
let item = create(self, key);
59+
self.buffer[index] = Some(item);
6460

65-
/// Returns [`Key`], reusing a vacant slot or allocating a new one.
66-
/// The caller decides how the slot is initialized.
67-
///
68-
/// `value`: The inner value to be inserted.
69-
/// `replace`: Determine how a vacant slot will be replaced.
70-
/// `create`: Determine how a new item will be created.
71-
fn alloc_slot<V, R, C>(
72-
&mut self,
73-
value: V,
74-
replace: R,
75-
create: C,
76-
) -> Key
77-
where
78-
R: FnOnce(V, &mut Item<T>) -> u32,
79-
C: FnOnce(V) -> Item<T>,
80-
{
81-
if let Some(index) = self.empty_slots.pop() {
82-
let generation = replace(value, &mut self.buffer[index]);
83-
Key::new(index, generation)
61+
key
8462
} else {
8563
let index = self.buffer.len();
86-
let item = create(value);
87-
let generation = item.generation;
88-
self.buffer.push(item);
89-
Key::new(index, generation)
64+
self.generations.insert(index, 0);
65+
66+
let key = Key::new(index, 0);
67+
68+
let item = create(self, key);
69+
self.buffer.insert(index, Some(item));
70+
71+
key
9072
}
9173
}
9274

@@ -104,8 +86,9 @@ impl<T> SparseMap<T> {
10486
/// key if present.
10587
pub fn get(&self, key: &Key) -> Option<&T> {
10688
let item = self.buffer.get(key.index)?;
107-
if item.generation == key.generation {
108-
return item.inner.as_ref();
89+
let generation = self.generations.get(key.index)?;
90+
if *generation == key.generation {
91+
return item.as_ref();
10992
}
11093

11194
None
@@ -115,8 +98,9 @@ impl<T> SparseMap<T> {
11598
/// if present.
11699
pub fn get_mut(&mut self, key: &Key) -> Option<&mut T> {
117100
let item = self.buffer.get_mut(key.index)?;
118-
if item.generation == key.generation {
119-
return item.inner.as_mut();
101+
let generation = self.generations.get(key.index)?;
102+
if *generation == key.generation {
103+
return item.as_mut();
120104
}
121105

122106
None
@@ -163,16 +147,19 @@ impl<T> SparseMap<T> {
163147
// SAFETY: We already checked that the key contains a value.
164148
let mut value = self.buffer[key.index].take().unwrap();
165149
let result = f(self, &mut value);
166-
self.buffer[key.index].inner = Some(value);
150+
self.buffer[key.index] = Some(value);
167151

168152
Some(result)
169153
}
170154

171155
/// Returns `true` if the key currently refers to a live value.
172156
pub fn contains(&self, key: &Key) -> bool {
173-
self.buffer.get(key.index).is_some_and(|item| {
174-
item.inner.is_some() && item.generation == key.generation
175-
})
157+
self.buffer
158+
.get(key.index)
159+
.zip(self.generations.get(key.index))
160+
.is_some_and(|(item, generation)| {
161+
item.is_some() && *generation == key.generation
162+
})
176163
}
177164

178165
/// Returns the number of live values stored in the map.
@@ -192,6 +179,7 @@ impl<T> Default for SparseMap<T> {
192179
fn default() -> Self {
193180
Self {
194181
buffer: Vec::new(),
182+
generations: Vec::new(),
195183
empty_slots: Vec::new(),
196184
}
197185
}
@@ -241,68 +229,6 @@ impl Display for Key {
241229
}
242230
}
243231

244-
/// A generational slot used internally by [`SparseMap`].
245-
///
246-
/// Each `Item` represents a single indexable slot that may or may not
247-
/// contain a value. The `generation` counter is incremented whenever
248-
/// the slot’s occupancy changes, invalidating any previously issued
249-
/// [`Key`] referring to this index.
250-
///
251-
/// # Invariants
252-
///
253-
/// - `inner.is_some()`: the slot is live.
254-
/// - `inner.is_none()`: the slot is vacant and reusable.
255-
/// - `generation` is monotonically increasing (wrapping on overflow).
256-
#[derive(Debug)]
257-
struct Item<T> {
258-
/// Stored value for this slot, if any.
259-
inner: Option<T>,
260-
/// Generation counter for stale-key detection.
261-
generation: u32,
262-
}
263-
264-
impl<T> Item<T> {
265-
/// Creates a new occupied slot with generation `0`.
266-
const fn new(value: T) -> Self {
267-
Self {
268-
inner: Some(value),
269-
generation: 0,
270-
}
271-
}
272-
273-
/// Creates a new vacant slot with generation `0`.
274-
const fn new_empty() -> Self {
275-
Self {
276-
inner: None,
277-
generation: 0,
278-
}
279-
}
280-
281-
/// Takes the value out of the slot without modifying its
282-
/// generation.
283-
const fn take(&mut self) -> Option<T> {
284-
self.inner.take()
285-
}
286-
287-
/// Replaces the slot’s value and increments its generation.
288-
///
289-
/// Returns the new generation.
290-
fn replace(&mut self, value: T) -> u32 {
291-
self.inner.replace(value);
292-
self.generation = self.generation.wrapping_add(1);
293-
self.generation
294-
}
295-
296-
/// Empties the slot and increments its generation.
297-
///
298-
/// Returns the new generation.
299-
fn replace_empty(&mut self) -> u32 {
300-
self.inner = None;
301-
self.generation = self.generation.wrapping_add(1);
302-
self.generation
303-
}
304-
}
305-
306232
#[cfg(test)]
307233
mod tests {
308234
use super::*;
@@ -317,21 +243,6 @@ mod tests {
317243
assert_eq!(map.len(), 1);
318244
}
319245

320-
#[test]
321-
fn insert_with_key_receives_valid_key_before_insert() {
322-
let mut map = SparseMap::new();
323-
324-
let key = map.insert_with_key(|map, key| {
325-
// Key must already be valid and point to the slot.
326-
assert_eq!(key.index, 0);
327-
assert!(map.buffer[key.index].inner.is_none());
328-
329-
42
330-
});
331-
332-
assert_eq!(map.get(&key), Some(&42));
333-
}
334-
335246
#[test]
336247
fn insert_and_insert_with_key_behave_equivalently() {
337248
let mut map = SparseMap::new();
@@ -342,8 +253,8 @@ mod tests {
342253
assert_eq!(k1.index, 0);
343254
assert_eq!(k2.index, 1);
344255

345-
assert_eq!(map.buffer[k1.index].inner, Some(1));
346-
assert_eq!(map.buffer[k2.index].inner, Some(2));
256+
assert_eq!(map.buffer[k1.index], Some(1));
257+
assert_eq!(map.buffer[k2.index], Some(2));
347258
}
348259

349260
#[test]

0 commit comments

Comments
 (0)