Clean up the documentation for the entity module.
This commit is contained in:
@@ -1,51 +1,4 @@
|
|||||||
//! Small lists of entity references.
|
//! Small lists of entity references.
|
||||||
//!
|
|
||||||
//! This module defines an `EntityList<T>` type which provides similar functionality to `Vec<T>`,
|
|
||||||
//! but with some important differences in the implementation:
|
|
||||||
//!
|
|
||||||
//! 1. Memory is allocated from a `ListPool<T>` instead of the global heap.
|
|
||||||
//! 2. The footprint of an entity list is 4 bytes, compared with the 24 bytes for `Vec<T>`.
|
|
||||||
//! 3. An entity list doesn't implement `Drop`, leaving it to the pool to manage memory.
|
|
||||||
//!
|
|
||||||
//! The list pool is intended to be used as a LIFO allocator. After building up a larger data
|
|
||||||
//! structure with many list references, the whole thing can be discarded quickly by clearing the
|
|
||||||
//! pool.
|
|
||||||
//!
|
|
||||||
//! # Safety
|
|
||||||
//!
|
|
||||||
//! Entity lists are not as safe to use as `Vec<T>`, but they never jeopardize Rust's memory safety
|
|
||||||
//! guarantees. These are the problems to be aware of:
|
|
||||||
//!
|
|
||||||
//! - If you lose track of an entity list, its memory won't be recycled until the pool is cleared.
|
|
||||||
//! This can cause the pool to grow very large with leaked lists.
|
|
||||||
//! - If entity lists are used after their pool is cleared, they may contain garbage data, and
|
|
||||||
//! modifying them may corrupt other lists in the pool.
|
|
||||||
//! - If an entity list is used with two different pool instances, both pools are likely to become
|
|
||||||
//! corrupted.
|
|
||||||
//!
|
|
||||||
//! # Implementation
|
|
||||||
//!
|
|
||||||
//! The `EntityList` itself is designed to have the smallest possible footprint. This is important
|
|
||||||
//! because it is used inside very compact data structures like `InstructionData`. The list
|
|
||||||
//! contains only a 32-bit index into the pool's memory vector, pointing to the first element of
|
|
||||||
//! the list.
|
|
||||||
//!
|
|
||||||
//! The pool is just a single `Vec<T>` containing all of the allocated lists. Each list is
|
|
||||||
//! represented as three contiguous parts:
|
|
||||||
//!
|
|
||||||
//! 1. The number of elements in the list.
|
|
||||||
//! 2. The list elements.
|
|
||||||
//! 3. Excess capacity elements.
|
|
||||||
//!
|
|
||||||
//! The total size of the three parts is always a power of two, and the excess capacity is always
|
|
||||||
//! as small as possible. This means that shrinking a list may cause the excess capacity to shrink
|
|
||||||
//! if a smaller power-of-two size becomes available.
|
|
||||||
//!
|
|
||||||
//! Both growing and shrinking a list may cause it to be reallocated in the pool vector.
|
|
||||||
//!
|
|
||||||
//! The index stored in an `EntityList` points to part 2, the list elements. The value 0 is
|
|
||||||
//! reserved for the empty list which isn't allocated in the vector.
|
|
||||||
|
|
||||||
use entity::EntityRef;
|
use entity::EntityRef;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -53,8 +6,28 @@ use std::mem;
|
|||||||
|
|
||||||
/// A small list of entity references allocated from a pool.
|
/// A small list of entity references allocated from a pool.
|
||||||
///
|
///
|
||||||
/// All of the list methods that take a pool reference must be given the same pool reference every
|
/// An `EntityList<T>` type provides similar functionality to `Vec<T>`, but with some important
|
||||||
/// time they are called. Otherwise data structures will be corrupted.
|
/// differences in the implementation:
|
||||||
|
///
|
||||||
|
/// 1. Memory is allocated from a `ListPool<T>` instead of the global heap.
|
||||||
|
/// 2. The footprint of an entity list is 4 bytes, compared with the 24 bytes for `Vec<T>`.
|
||||||
|
/// 3. An entity list doesn't implement `Drop`, leaving it to the pool to manage memory.
|
||||||
|
///
|
||||||
|
/// The list pool is intended to be used as a LIFO allocator. After building up a larger data
|
||||||
|
/// structure with many list references, the whole thing can be discarded quickly by clearing the
|
||||||
|
/// pool.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Entity lists are not as safe to use as `Vec<T>`, but they never jeopardize Rust's memory safety
|
||||||
|
/// guarantees. These are the problems to be aware of:
|
||||||
|
///
|
||||||
|
/// - If you lose track of an entity list, its memory won't be recycled until the pool is cleared.
|
||||||
|
/// This can cause the pool to grow very large with leaked lists.
|
||||||
|
/// - If entity lists are used after their pool is cleared, they may contain garbage data, and
|
||||||
|
/// modifying them may corrupt other lists in the pool.
|
||||||
|
/// - If an entity list is used with two different pool instances, both pools are likely to become
|
||||||
|
/// corrupted.
|
||||||
///
|
///
|
||||||
/// Entity lists can be cloned, but that operation should only be used as part of cloning the whole
|
/// Entity lists can be cloned, but that operation should only be used as part of cloning the whole
|
||||||
/// function they belong to. *Cloning an entity list does not allocate new memory for the clone*.
|
/// function they belong to. *Cloning an entity list does not allocate new memory for the clone*.
|
||||||
@@ -63,6 +36,29 @@ use std::mem;
|
|||||||
/// Entity lists can also be hashed and compared for equality, but those operations just panic if,
|
/// Entity lists can also be hashed and compared for equality, but those operations just panic if,
|
||||||
/// they're ever actually called, because it's not possible to compare the contents of the list
|
/// they're ever actually called, because it's not possible to compare the contents of the list
|
||||||
/// without the pool reference.
|
/// without the pool reference.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// The `EntityList` itself is designed to have the smallest possible footprint. This is important
|
||||||
|
/// because it is used inside very compact data structures like `InstructionData`. The list
|
||||||
|
/// contains only a 32-bit index into the pool's memory vector, pointing to the first element of
|
||||||
|
/// the list.
|
||||||
|
///
|
||||||
|
/// The pool is just a single `Vec<T>` containing all of the allocated lists. Each list is
|
||||||
|
/// represented as three contiguous parts:
|
||||||
|
///
|
||||||
|
/// 1. The number of elements in the list.
|
||||||
|
/// 2. The list elements.
|
||||||
|
/// 3. Excess capacity elements.
|
||||||
|
///
|
||||||
|
/// The total size of the three parts is always a power of two, and the excess capacity is always
|
||||||
|
/// as small as possible. This means that shrinking a list may cause the excess capacity to shrink
|
||||||
|
/// if a smaller power-of-two size becomes available.
|
||||||
|
///
|
||||||
|
/// Both growing and shrinking a list may cause it to be reallocated in the pool vector.
|
||||||
|
///
|
||||||
|
/// The index stored in an `EntityList` points to part 2, the list elements. The value 0 is
|
||||||
|
/// reserved for the empty list which isn't allocated in the vector.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EntityList<T: EntityRef> {
|
pub struct EntityList<T: EntityRef> {
|
||||||
index: u32,
|
index: u32,
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
//! Densely numbered entity references as mapping keys.
|
//! Densely numbered entity references as mapping keys.
|
||||||
//!
|
|
||||||
//! The `EntityMap` data structure uses the dense index space to implement a map with a vector.
|
|
||||||
//! Unlike `PrimaryMap`, and `EntityMap` can't be used to allocate entity references. It is used to
|
|
||||||
//! associate secondary information with entities.
|
|
||||||
|
|
||||||
use entity::{EntityRef, Keys};
|
use entity::{EntityRef, Keys};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
/// A mapping `K -> V` for densely indexed entity references.
|
/// A mapping `K -> V` for densely indexed entity references.
|
||||||
|
///
|
||||||
|
/// The `EntityMap` data structure uses the dense index space to implement a map with a vector.
|
||||||
|
/// Unlike `PrimaryMap`, an `EntityMap` can't be used to allocate entity references. It is used to
|
||||||
|
/// associate secondary information with entities.
|
||||||
|
///
|
||||||
|
/// The map does not track if an entry for a key has been inserted or not. Instead it behaves as if
|
||||||
|
/// all keys have a default entry from the beginning.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EntityMap<K, V>
|
pub struct EntityMap<K, V>
|
||||||
where K: EntityRef,
|
where K: EntityRef,
|
||||||
|
|||||||
@@ -1,9 +1,30 @@
|
|||||||
//! Densely numbered entity references as mapping keys.
|
//! Array-based data structures using densely numbered entity references as mapping keys.
|
||||||
//!
|
//!
|
||||||
//! This module defines an `EntityRef` trait that should be implemented by reference types wrapping
|
//! This module defines a number of data structures based on arrays. The arrays are not indexed by
|
||||||
//! a small integer index.
|
//! `usize` as usual, but by *entity references* which are integers wrapped in new-types. This has
|
||||||
|
//! a couple advantages:
|
||||||
//!
|
//!
|
||||||
//! Various data structures based on the entity references are defined in sub-modules.
|
//! - Improved type safety. The various map types accept a specific key type, so there is no
|
||||||
|
//! confusion about the meaning of an array index, as there is with plain arrays.
|
||||||
|
//! - Smaller indexes. The normal `usize` index is often 64 bits which is way too large for most
|
||||||
|
//! purposes. The entity reference types can be smaller, allowing for more compact data
|
||||||
|
//! structures.
|
||||||
|
//!
|
||||||
|
//! The `EntityRef` trait should be implemented by types to be used as indexed. The `entity_impl!`
|
||||||
|
//! macro provides convenient defaults for types wrapping `u32` which is common.
|
||||||
|
//!
|
||||||
|
//! - [`PrimaryMap`](struct.PrimaryMap.html) is used to keep track of a vector of entities,
|
||||||
|
//! assigning a unique entity reference to each.
|
||||||
|
//! - [`EntityMap`](struct.EntityMap.html) is used to associate secondary information to an entity.
|
||||||
|
//! The map is implemented as a simple vector, so it does not keep track of which entities have
|
||||||
|
//! been inserted. Instead, any unknown entities map to the default value.
|
||||||
|
//! - [`SparseMap`](struct.SparseMap.html) is used to associate secondary information to a small
|
||||||
|
//! number of entities. It tracks accurately which entities have been inserted. This is a
|
||||||
|
//! specialized data structure which can use a lot of memory, so read the documentation before
|
||||||
|
//! using it.
|
||||||
|
//! - [`EntityList`](struct.EntityList.html) is a compact representation of lists of entity
|
||||||
|
//! references allocated from an associated memory pool. It has a much smaller footprint than
|
||||||
|
//! `Vec`.
|
||||||
|
|
||||||
mod keys;
|
mod keys;
|
||||||
mod list;
|
mod list;
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
//! Densely numbered entity references as mapping keys.
|
//! Densely numbered entity references as mapping keys.
|
||||||
//!
|
|
||||||
//! The `PrimaryMap` data structure uses the dense index space to implement a map with a vector.
|
|
||||||
//!
|
|
||||||
//! A primary map contains the main definition of an entity, and it can be used to allocate new
|
|
||||||
//! entity references with the `push` method.
|
|
||||||
//!
|
|
||||||
//! There should only be a single `PrimaryMap` instance for a given `EntityRef` type, otherwise
|
|
||||||
//! conflicting references will be created.
|
|
||||||
|
|
||||||
use entity::{EntityRef, Keys};
|
use entity::{EntityRef, Keys};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
/// A mapping `K -> V` for densely indexed entity references.
|
/// A primary mapping `K -> V` allocating dense entity references.
|
||||||
|
///
|
||||||
|
/// The `PrimaryMap` data structure uses the dense index space to implement a map with a vector.
|
||||||
|
///
|
||||||
|
/// A primary map contains the main definition of an entity, and it can be used to allocate new
|
||||||
|
/// entity references with the `push` method.
|
||||||
|
///
|
||||||
|
/// There should only be a single `PrimaryMap` instance for a given `EntityRef` type, otherwise
|
||||||
|
/// conflicting references will be created. Using unknown keys for indexing will cause a panic.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PrimaryMap<K, V>
|
pub struct PrimaryMap<K, V>
|
||||||
where K: EntityRef
|
where K: EntityRef
|
||||||
|
|||||||
@@ -6,34 +6,6 @@
|
|||||||
//!
|
//!
|
||||||
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
|
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
|
||||||
//! ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
|
//! ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
|
||||||
//!
|
|
||||||
//! A `SparseMap<K, V>` map provides:
|
|
||||||
//!
|
|
||||||
//! - Memory usage equivalent to `EntityMap<K, u32>` + `Vec<V>`, so much smaller than
|
|
||||||
//! `EntityMap<K, V>` for sparse mappings of larger `V` types.
|
|
||||||
//! - Constant time lookup, slightly slower than `EntityMap`.
|
|
||||||
//! - A very fast, constant time `clear()` operation.
|
|
||||||
//! - Fast insert and erase operations.
|
|
||||||
//! - Stable iteration that is as fast as a `Vec<V>`.
|
|
||||||
//!
|
|
||||||
//! # Compared to `EntityMap`
|
|
||||||
//!
|
|
||||||
//! When should we use a `SparseMap` instead of a secondary `EntityMap`? First of all, `SparseMap`
|
|
||||||
//! does not provide the functionality of a primary `EntityMap` which can allocate and assign
|
|
||||||
//! entity references to objects as they are pushed onto the map. It is only the secondary
|
|
||||||
//! entity maps that can be replaced with a `SparseMap`.
|
|
||||||
//!
|
|
||||||
//! - A secondary entity map requires its values to implement `Default`, and it is a bit loose
|
|
||||||
//! about creating new mappings to the default value. It doesn't distinguish clearly between an
|
|
||||||
//! unmapped key and one that maps to the default value. `SparseMap` does not require `Default`
|
|
||||||
//! values, and it tracks accurately if a key has been mapped or not.
|
|
||||||
//! - Iterating over the contents of an `EntityMap` is linear in the size of the *key space*, while
|
|
||||||
//! iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an
|
|
||||||
//! advantage precisely when the mapping is sparse.
|
|
||||||
//! - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the
|
|
||||||
//! size of the key space. (Or, rather the required `resize()` call following the `clear()` is).
|
|
||||||
//! - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must
|
|
||||||
//! contain their own key.
|
|
||||||
|
|
||||||
use entity::{EntityRef, EntityMap};
|
use entity::{EntityRef, EntityMap};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@@ -51,6 +23,33 @@ pub trait SparseMapValue<K> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A sparse mapping of entity references.
|
/// A sparse mapping of entity references.
|
||||||
|
///
|
||||||
|
/// A `SparseMap<K, V>` map provides:
|
||||||
|
///
|
||||||
|
/// - Memory usage equivalent to `EntityMap<K, u32>` + `Vec<V>`, so much smaller than
|
||||||
|
/// `EntityMap<K, V>` for sparse mappings of larger `V` types.
|
||||||
|
/// - Constant time lookup, slightly slower than `EntityMap`.
|
||||||
|
/// - A very fast, constant time `clear()` operation.
|
||||||
|
/// - Fast insert and erase operations.
|
||||||
|
/// - Stable iteration that is as fast as a `Vec<V>`.
|
||||||
|
///
|
||||||
|
/// # Compared to `EntityMap`
|
||||||
|
///
|
||||||
|
/// When should we use a `SparseMap` instead of a secondary `EntityMap`? First of all, `SparseMap`
|
||||||
|
/// does not provide the functionality of a `PrimaryMap` which can allocate and assign entity
|
||||||
|
/// references to objects as they are pushed onto the map. It is only the secondary entity maps
|
||||||
|
/// that can be replaced with a `SparseMap`.
|
||||||
|
///
|
||||||
|
/// - A secondary entity map assigns a default mapping to all keys. It doesn't distinguish between
|
||||||
|
/// an unmapped key and one that maps to the default value. `SparseMap` does not require
|
||||||
|
/// `Default` values, and it tracks accurately if a key has been mapped or not.
|
||||||
|
/// - Iterating over the contents of an `EntityMap` is linear in the size of the *key space*, while
|
||||||
|
/// iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an
|
||||||
|
/// advantage precisely when the mapping is sparse.
|
||||||
|
/// - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the
|
||||||
|
/// size of the key space. (Or, rather the required `resize()` call following the `clear()` is).
|
||||||
|
/// - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must
|
||||||
|
/// contain their own key.
|
||||||
pub struct SparseMap<K, V>
|
pub struct SparseMap<K, V>
|
||||||
where K: EntityRef,
|
where K: EntityRef,
|
||||||
V: SparseMapValue<K>
|
V: SparseMapValue<K>
|
||||||
|
|||||||
Reference in New Issue
Block a user