Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
* Added Entry API. ([#50])
* Added `Arena::vacant_entry` ([#57]) for creating an Entry without a key.
* Added `Arena::next_index` ([#58]) for finding the next index without mutating the Arena.
* Implemented `FromIterator<(Index, T)>` and `Extend<(Index, T)>` for `Arena`. ([#56])

[#19]: https://github.com/LPGhatguy/thunderdome/issues/19
[#43]: https://github.com/LPGhatguy/thunderdome/pull/43
[#50]: https://github.com/LPGhatguy/thunderdome/issues/50
[#54]: https://github.com/LPGhatguy/thunderdome/pull/57
[#58]: https://github.com/LPGhatguy/thunderdome/pull/58
[#56]: https://github.com/LPGhatguy/thunderdome/pull/56

## [0.6.1] - 2023-06-24
* Added `Index::DANGLING`.
Expand Down
131 changes: 131 additions & 0 deletions src/arena.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::convert::TryInto;
use core::iter::FromIterator;
use core::mem::replace;
use core::ops;

Expand Down Expand Up @@ -696,6 +697,36 @@ impl<T> Default for Arena<T> {
}
}

impl<T> Extend<(Index, T)> for Arena<T> {
/// Insert every (Index, T) pair from the iterator into the arena.
///
/// Internally, insertion is done with [Arena::insert_at]. If a slot is already occupied, its generation and value are replaced with those of iterator's element.
///
/// Iterators whose `Index` are in ascending order (what every iterator over an
/// [`Arena`] in this crate yields) — are appended in linear time, which is fast.
/// Out-of-order slots may need to walk the arena's free list, which is worse.
fn extend<I: IntoIterator<Item = (Index, T)>>(&mut self, iter: I) {
let iter = iter.into_iter();
self.reserve(iter.size_hint().0);

for (index, value) in iter {
self.insert_at(index, value);
}
}
}

impl<T> FromIterator<(Index, T)> for Arena<T> {
/// Build an arena from an iterator of `(Index, T)` pairs, placing each value in the slot named
/// by its index.
///
/// See [`Extend::extend`] for how duplicate slots and ordering are handled.
fn from_iter<I: IntoIterator<Item = (Index, T)>>(iter: I) -> Self {
let mut arena = Arena::new();
arena.extend(iter);
arena
}
}

impl<T> IntoIterator for Arena<T> {
type Item = (Index, T);
type IntoIter = IntoIter<T>;
Expand Down Expand Up @@ -872,6 +903,106 @@ mod test {
}
}

#[test]
fn from_iter_empty() {
let arena: Arena<u32> = core::iter::empty().collect();
assert!(arena.is_empty());
}

#[test]
fn from_iter_roundtrip() {
let mut arena = Arena::new();
let one = arena.insert(1);
let two = arena.insert(2);
let three = arena.insert(3);

// Leave a hole in the middle so the round trip has to handle a vacant slot.
assert_eq!(arena.remove(two), Some(2));

let rebuilt: Arena<u32> = arena.into_iter().collect();
assert_eq!(rebuilt.len(), 2);
assert_eq!(rebuilt.get(one), Some(&1));
assert_eq!(rebuilt.get(three), Some(&3));
assert_eq!(rebuilt.get(two), None);
}

#[test]
fn from_iter_sparse() {
let first = Index {
slot: 5,
generation: Generation::from_u32(7).unwrap(),
};
let second = Index {
slot: 1,
generation: Generation::from_u32(2).unwrap(),
};

// Deliberately out of order, with gaps on either side of `second`.
let mut arena: Arena<u32> = IntoIterator::into_iter([(first, 50), (second, 10)]).collect();
assert_eq!(arena.len(), 2);
assert_eq!(arena.get(first), Some(&50));
assert_eq!(arena.get(second), Some(&10));

// The free list must still be intact: every vacant slot should be handed out by `insert`
// before the arena grows past the six slots it already has.
for _ in 0..4 {
let index = arena.insert(0);
assert!(index.slot() < 6, "unexpected slot {}", index.slot());
}

assert_eq!(arena.len(), 6);
assert_eq!(arena.get(first), Some(&50));
assert_eq!(arena.get(second), Some(&10));
}

#[test]
fn from_iter_duplicate_slot() {
let first = Index {
slot: 3,
generation: Generation::from_u32(1).unwrap(),
};
let second = Index {
slot: 3,
generation: Generation::from_u32(9).unwrap(),
};

let arena: Arena<u32> = IntoIterator::into_iter([(first, 10), (second, 20)]).collect();
assert_eq!(arena.len(), 1);
assert_eq!(arena.get(second), Some(&20));
assert_eq!(arena.get(first), None);
}

#[test]
fn extend_existing() {
let mut arena = Arena::new();
let one = arena.insert(1);

let other = Index {
slot: 4,
generation: Generation::from_u32(3).unwrap(),
};
arena.extend([(other, 40)]);

assert_eq!(arena.len(), 2);
assert_eq!(arena.get(one), Some(&1));
assert_eq!(arena.get(other), Some(&40));
}

#[test]
fn map_arena() {
let mut arena = Arena::new();
for i in 0..5 {
arena.insert(i);
}

let string_arena: Arena<String> = arena.iter().map(|(k, v)| (k, v.to_string())).collect();

for ((_, number), (_, string)) in arena.iter().zip(string_arena.iter()) {
assert_eq!(&number.to_string(), string);
assert_eq!(*number, string.parse::<i32>().unwrap());
}
}

#[test]
fn get_mut() {
let mut arena = Arena::new();
Expand Down
Loading