blob: 1fdd1adbc2300c593e3155c9e9586eee86c3fcf4 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Identifier map collection data structure.
//!
//! Defines [`IdMapCollection`], which is a generic map collection that can be
//! keyed on [`IdMapCollectionKey`], which is a two-level key structure.
//!
//! Used to provide collections keyed on [`crate::DeviceId`] that match hot path
//! performance requirements.
use alloc::vec::Vec;
use super::id_map::{Entry, EntryKey};
use super::IdMap;
/// A key that can index items in [`IdMapCollection`].
///
/// An `IdMapCollectionKey` is a key with two levels: `variant` and `id`. The
/// number of `variant`s must be fixed and known at compile time, and is
/// typically mapped to a number of `enum` variants (nested or not).
pub trait IdMapCollectionKey {
/// The number of variants this key supports.
const VARIANT_COUNT: usize;
/// Get the variant index for this key.
///
/// # Panics
///
/// Callers may assume that `get_variant` returns a value in the range `[0,
/// VARIANT_COUNT)`, and may panic if that assumption is violated.
fn get_variant(&self) -> usize;
/// Get the id index for this key.
fn get_id(&self) -> usize;
}
impl<O> EntryKey for O
where
O: IdMapCollectionKey,
{
fn get_key_index(&self) -> usize {
<O as IdMapCollectionKey>::get_id(self)
}
}
/// A generic collection indexed by an [`IdMapCollectionKey`].
///
/// `IdMapCollection` provides the same performance guarantees as [`IdMap`], but
/// provides a two-level keying scheme that matches the pattern used in
/// [`crate::DeviceId`].
pub struct IdMapCollection<K: IdMapCollectionKey, T> {
// TODO(brunodalbo): we define a vector container here because we can't just
// define a fixed array length based on an associated const in
// IdMapCollectionKey. When rust issue #43408 gets resolved we can switch
// this to use the associated const and just have a fixed length array.
data: Vec<IdMap<T>>,
_marker: core::marker::PhantomData<K>,
}
impl<K: IdMapCollectionKey, T> IdMapCollection<K, T> {
/// Creates a new empty `IdMapCollection`.
pub fn new() -> Self {
let mut data = Vec::new();
data.resize_with(K::VARIANT_COUNT, IdMap::default);
Self { data, _marker: core::marker::PhantomData }
}
fn get_map(&self, key: &K) -> &IdMap<T> {
&self.data[key.get_variant()]
}
fn get_map_mut(&mut self, key: &K) -> &mut IdMap<T> {
&mut self.data[key.get_variant()]
}
/// Returns `true` if the `IdMapCollection` holds no items.
pub fn is_empty(&self) -> bool {
self.data.iter().all(|d| d.is_empty())
}
/// Returns a reference to the item indexed by `key`, or `None` if the `key`
/// doesn't exist.
pub fn get(&self, key: &K) -> Option<&T> {
self.get_map(key).get(key.get_id())
}
/// Returns a mutable reference to the item indexed by `key`, or `None` if
/// the `key` doesn't exist.
pub fn get_mut(&mut self, key: &K) -> Option<&mut T> {
self.get_map_mut(key).get_mut(key.get_id())
}
/// Removes item indexed by `key` from the container.
///
/// Returns the removed item if it exists, or `None` otherwise.
pub fn remove(&mut self, key: &K) -> Option<T> {
self.get_map_mut(key).remove(key.get_id())
}
/// Inserts `item` at `key`.
///
/// If the [`IdMapCollection`] already contained an item indexed by `key`,
/// `insert` returns it, or `None` otherwise.
pub fn insert(&mut self, key: &K, item: T) -> Option<T> {
self.get_map_mut(key).insert(key.get_id(), item)
}
/// Creates an iterator over the containing items.
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter().flat_map(|m| m.iter()).map(|(_, v)| v)
}
/// Creates a mutable iterator over the containing items.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.data.iter_mut().flat_map(|m| m.iter_mut()).map(|(_, v)| v)
}
/// Gets the given key's corresponding entry in the map for in-place
/// manipulation.
pub fn entry(&mut self, key: K) -> Entry<'_, K, T> {
self.get_map_mut(&key).entry(key.get_id()).map_key(|_| key)
}
}
impl<K: IdMapCollectionKey, T> Default for IdMapCollection<K, T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum MockVariants {
A,
B,
C,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct MockKey {
id: usize,
var: MockVariants,
}
impl MockKey {
const fn new(id: usize, var: MockVariants) -> Self {
Self { id, var }
}
}
impl IdMapCollectionKey for MockKey {
const VARIANT_COUNT: usize = 3;
fn get_variant(&self) -> usize {
match self.var {
MockVariants::A => 0,
MockVariants::B => 1,
MockVariants::C => 2,
}
}
fn get_id(&self) -> usize {
self.id
}
}
type TestCollection = IdMapCollection<MockKey, i32>;
const KEY_A: MockKey = MockKey::new(0, MockVariants::A);
const KEY_B: MockKey = MockKey::new(2, MockVariants::B);
const KEY_C: MockKey = MockKey::new(4, MockVariants::C);
#[test]
fn test_insert_and_get() {
let mut t = TestCollection::new();
assert!(t.data[0].is_empty());
assert!(t.data[1].is_empty());
assert!(t.data[2].is_empty());
assert!(t.insert(&KEY_A, 1).is_none());
assert!(!t.data[0].is_empty());
assert!(t.insert(&KEY_B, 2).is_none());
assert!(!t.data[1].is_empty());
assert_eq!(*t.get(&KEY_A).unwrap(), 1);
assert!(t.get(&KEY_C).is_none());
*t.get_mut(&KEY_B).unwrap() = 3;
assert_eq!(*t.get(&KEY_B).unwrap(), 3);
}
#[test]
fn test_remove() {
let mut t = TestCollection::new();
assert!(t.insert(&KEY_B, 15).is_none());
assert_eq!(t.remove(&KEY_B).unwrap(), 15);
assert!(t.remove(&KEY_B).is_none());
}
#[test]
fn test_iter() {
let mut t = TestCollection::new();
assert!(t.insert(&KEY_A, 15).is_none());
assert!(t.insert(&KEY_B, -5).is_none());
assert!(t.insert(&KEY_C, -10).is_none());
let mut c = 0;
let mut sum = 0;
for i in t.iter() {
c += 1;
sum += *i;
}
assert_eq!(c, 3);
assert_eq!(sum, 0);
}
#[test]
fn test_is_empty() {
let mut t = TestCollection::new();
assert!(t.is_empty());
assert!(t.insert(&KEY_B, 15).is_none());
assert!(!t.is_empty());
}
#[test]
fn test_iter_mut() {
let mut t = TestCollection::new();
assert!(t.insert(&KEY_A, 15).is_none());
assert!(t.insert(&KEY_B, -5).is_none());
assert!(t.insert(&KEY_C, -10).is_none());
for i in t.iter_mut() {
*i *= 2;
}
assert_eq!(*t.get(&KEY_A).unwrap(), 30);
assert_eq!(*t.get(&KEY_B).unwrap(), -10);
assert_eq!(*t.get(&KEY_C).unwrap(), -20);
}
#[test]
fn test_entry() {
let mut t = TestCollection::new();
assert_eq!(*t.entry(KEY_A).or_insert(2), 2);
assert_eq!(
*t.entry(KEY_A)
.and_modify(|v| {
*v = 10;
})
.or_insert(5),
10
);
assert_eq!(
*t.entry(KEY_B)
.and_modify(|v| {
*v = 10;
})
.or_insert(5),
5
);
assert_eq!(*t.entry(KEY_C).or_insert_with(|| 7), 7);
assert_eq!(*t.entry(KEY_C).key(), KEY_C);
assert_eq!(*t.get(&KEY_A).unwrap(), 10);
assert_eq!(*t.get(&KEY_B).unwrap(), 5);
assert_eq!(*t.get(&KEY_C).unwrap(), 7);
}
}