From a5d57db2694e73bda6dbfdf691d6b7fab267d554 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Sat, 22 Jun 2024 17:27:04 -0700 Subject: [PATCH] [naga] Break `naga::arena` up into submodules. This commit is almost entirely code motion. The only meaningful changes should be: - changes to imports - changes to visibility - changes to use visible associated constructor functions instead of trying to construct values directly using now-invisible fields - moving the crate-level "Arena" docs into the `arena` module --- naga/src/arena/handle.rs | 126 +++++++ naga/src/arena/handlevec.rs | 105 ++++++ naga/src/arena/mod.rs | 609 ++------------------------------- naga/src/arena/range.rs | 131 +++++++ naga/src/arena/unique_arena.rs | 262 ++++++++++++++ naga/src/lib.rs | 19 - 6 files changed, 652 insertions(+), 600 deletions(-) create mode 100644 naga/src/arena/handle.rs create mode 100644 naga/src/arena/handlevec.rs create mode 100644 naga/src/arena/range.rs create mode 100644 naga/src/arena/unique_arena.rs diff --git a/naga/src/arena/handle.rs b/naga/src/arena/handle.rs new file mode 100644 index 0000000000..d486d6e054 --- /dev/null +++ b/naga/src/arena/handle.rs @@ -0,0 +1,126 @@ +//! Well-typed indices into [`Arena`]s and [`UniqueArena`]s. +//! +//! This module defines [`Handle`] and related types. +//! +//! [`Arena`]: super::Arena +//! [`UniqueArena`]: super::UniqueArena + +use std::{cmp::Ordering, fmt, hash, marker::PhantomData}; + +/// An unique index in the arena array that a handle points to. +/// The "non-max" part ensures that an `Option>` has +/// the same size and representation as `Handle`. +pub type Index = crate::non_max_u32::NonMaxU32; + +#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq)] +#[error("Handle {index} of {kind} is either not present, or inaccessible yet")] +pub struct BadHandle { + pub kind: &'static str, + pub index: usize, +} + +impl BadHandle { + pub fn new(handle: Handle) -> Self { + Self { + kind: std::any::type_name::(), + index: handle.index(), + } + } +} + +/// A strongly typed reference to an arena item. +/// +/// A `Handle` value can be used as an index into an [`Arena`] or [`UniqueArena`]. +/// +/// [`Arena`]: super::Arena +/// [`UniqueArena`]: super::UniqueArena +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +#[cfg_attr( + any(feature = "serialize", feature = "deserialize"), + serde(transparent) +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Handle { + index: Index, + #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))] + marker: PhantomData, +} + +impl Clone for Handle { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Handle {} + +impl PartialEq for Handle { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} + +impl Eq for Handle {} + +impl PartialOrd for Handle { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Handle { + fn cmp(&self, other: &Self) -> Ordering { + self.index.cmp(&other.index) + } +} + +impl fmt::Debug for Handle { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "[{}]", self.index) + } +} + +impl hash::Hash for Handle { + fn hash(&self, hasher: &mut H) { + self.index.hash(hasher) + } +} + +impl Handle { + pub(crate) const fn new(index: Index) -> Self { + Handle { + index, + marker: PhantomData, + } + } + + /// Returns the index of this handle. + pub const fn index(self) -> usize { + self.index.get() as usize + } + + /// Convert a `usize` index into a `Handle`. + pub(super) fn from_usize(index: usize) -> Self { + let handle_index = u32::try_from(index) + .ok() + .and_then(Index::new) + .expect("Failed to insert into arena. Handle overflows"); + Handle::new(handle_index) + } + + /// Convert a `usize` index into a `Handle`, without range checks. + pub(super) const unsafe fn from_usize_unchecked(index: usize) -> Self { + Handle::new(Index::new_unchecked(index as u32)) + } + + /// Write this handle's index to `formatter`, preceded by `prefix`. + pub fn write_prefixed( + &self, + formatter: &mut fmt::Formatter, + prefix: &'static str, + ) -> fmt::Result { + formatter.write_str(prefix)?; + ::fmt(&self.index(), formatter) + } +} diff --git a/naga/src/arena/handlevec.rs b/naga/src/arena/handlevec.rs new file mode 100644 index 0000000000..2ddb65c9a4 --- /dev/null +++ b/naga/src/arena/handlevec.rs @@ -0,0 +1,105 @@ +//! The [`HandleVec`] type and associated definitions. + +use super::handle::Handle; + +use std::marker::PhantomData; +use std::ops; + +/// A [`Vec`] indexed by [`Handle`]s. +/// +/// A `HandleVec` is a [`Vec`] indexed by values of type `Handle`, +/// rather than `usize`. +/// +/// Rather than a `push` method, `HandleVec` has an [`insert`] method, analogous +/// to [`HashMap::insert`], that requires you to provide the handle at which the +/// new value should appear. However, since `HandleVec` only supports insertion +/// at the end, the given handle's index must be equal to the the `HandleVec`'s +/// current length; otherwise, the insertion will panic. +/// +/// [`insert`]: HandleVec::insert +/// [`HashMap::insert`]: std::collections::HashMap::insert +#[derive(Debug)] +pub(crate) struct HandleVec { + inner: Vec, + as_keys: PhantomData, +} + +impl Default for HandleVec { + fn default() -> Self { + Self { + inner: vec![], + as_keys: PhantomData, + } + } +} + +#[allow(dead_code)] +impl HandleVec { + pub(crate) const fn new() -> Self { + Self { + inner: vec![], + as_keys: PhantomData, + } + } + + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { + inner: Vec::with_capacity(capacity), + as_keys: PhantomData, + } + } + + pub(crate) fn len(&self) -> usize { + self.inner.len() + } + + /// Insert a mapping from `handle` to `value`. + /// + /// Unlike a [`HashMap`], a `HandleVec` can only have new entries inserted at + /// the end, like [`Vec::push`]. So the index of `handle` must equal + /// [`self.len()`]. + /// + /// [`HashMap`]: std::collections::HashMap + /// [`self.len()`]: HandleVec::len + pub(crate) fn insert(&mut self, handle: Handle, value: U) { + assert_eq!(handle.index(), self.inner.len()); + self.inner.push(value); + } + + pub(crate) fn get(&self, handle: Handle) -> Option<&U> { + self.inner.get(handle.index()) + } + + pub(crate) fn clear(&mut self) { + self.inner.clear() + } + + pub(crate) fn resize(&mut self, len: usize, fill: U) + where + U: Clone, + { + self.inner.resize(len, fill); + } + + pub(crate) fn iter(&self) -> impl Iterator { + self.inner.iter() + } + + pub(crate) fn iter_mut(&mut self) -> impl Iterator { + self.inner.iter_mut() + } +} + +impl ops::Index> for HandleVec { + type Output = U; + + fn index(&self, handle: Handle) -> &Self::Output { + &self.inner[handle.index()] + } +} + +impl ops::IndexMut> for HandleVec { + fn index_mut(&mut self, handle: Handle) -> &mut Self::Output { + &mut self.inner[handle.index()] + } +} diff --git a/naga/src/arena/mod.rs b/naga/src/arena/mod.rs index 7a6556499d..a1b64d793a 100644 --- a/naga/src/arena/mod.rs +++ b/naga/src/arena/mod.rs @@ -1,241 +1,40 @@ -use std::{cmp::Ordering, fmt, hash, marker::PhantomData, ops}; +/*! The [`Arena`], [`UniqueArena`], and [`Handle`] types. -use crate::non_max_u32::NonMaxU32; +To improve translator performance and reduce memory usage, most structures are +stored in an [`Arena`]. An `Arena` stores a series of `T` values, indexed by +[`Handle`](Handle) values, which are just wrappers around integer indexes. +For example, a `Function`'s expressions are stored in an `Arena`, +and compound expressions refer to their sub-expressions via `Handle` +values. (When examining the serialized form of a `Module`, note that the first +element of an `Arena` has an index of 1, not 0.) -/// An unique index in the arena array that a handle points to. -/// The "non-max" part ensures that an `Option>` has -/// the same size and representation as `Handle`. -type Index = NonMaxU32; +A [`UniqueArena`] is just like an `Arena`, except that it stores only a single +instance of each value. The value type must implement `Eq` and `Hash`. Like an +`Arena`, inserting a value into a `UniqueArena` returns a `Handle` which can be +used to efficiently access the value, without a hash lookup. Inserting a value +multiple times returns the same `Handle`. -use crate::{FastIndexSet, Span}; +If the `span` feature is enabled, both `Arena` and `UniqueArena` can associate a +source code span with each element. -#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq)] -#[error("Handle {index} of {kind} is either not present, or inaccessible yet")] -pub struct BadHandle { - pub kind: &'static str, - pub index: usize, -} - -impl BadHandle { - fn new(handle: Handle) -> Self { - Self { - kind: std::any::type_name::(), - index: handle.index(), - } - } -} - -/// A strongly typed reference to an arena item. -/// -/// A `Handle` value can be used as an index into an [`Arena`] or [`UniqueArena`]. -#[cfg_attr(feature = "serialize", derive(serde::Serialize))] -#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] -#[cfg_attr( - any(feature = "serialize", feature = "deserialize"), - serde(transparent) -)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub struct Handle { - index: Index, - #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))] - marker: PhantomData, -} - -impl Clone for Handle { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for Handle {} - -impl PartialEq for Handle { - fn eq(&self, other: &Self) -> bool { - self.index == other.index - } -} - -impl Eq for Handle {} - -impl PartialOrd for Handle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Handle { - fn cmp(&self, other: &Self) -> Ordering { - self.index.cmp(&other.index) - } -} - -impl fmt::Debug for Handle { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "[{}]", self.index) - } -} - -impl hash::Hash for Handle { - fn hash(&self, hasher: &mut H) { - self.index.hash(hasher) - } -} - -impl Handle { - pub(crate) const fn new(index: Index) -> Self { - Handle { - index, - marker: PhantomData, - } - } - - /// Returns the index of this handle. - pub const fn index(self) -> usize { - self.index.get() as usize - } - - /// Convert a `usize` index into a `Handle`. - fn from_usize(index: usize) -> Self { - let handle_index = u32::try_from(index) - .ok() - .and_then(Index::new) - .expect("Failed to insert into arena. Handle overflows"); - Handle::new(handle_index) - } - - /// Convert a `usize` index into a `Handle`, without range checks. - const unsafe fn from_usize_unchecked(index: usize) -> Self { - Handle::new(Index::new_unchecked(index as u32)) - } - - /// Write this handle's index to `formatter`, preceded by `prefix`. - pub fn write_prefixed( - &self, - formatter: &mut std::fmt::Formatter, - prefix: &'static str, - ) -> std::fmt::Result { - formatter.write_str(prefix)?; - ::fmt(&self.index(), formatter) - } -} - -/// A strongly typed range of handles. -#[cfg_attr(feature = "serialize", derive(serde::Serialize))] -#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] -#[cfg_attr( - any(feature = "serialize", feature = "deserialize"), - serde(transparent) -)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -#[cfg_attr(test, derive(PartialEq))] -pub struct Range { - inner: ops::Range, - #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))] - marker: PhantomData, -} - -impl Range { - pub(crate) const fn erase_type(self) -> Range<()> { - let Self { inner, marker: _ } = self; - Range { - inner, - marker: PhantomData, - } - } -} - -// NOTE: Keep this diagnostic in sync with that of [`BadHandle`]. -#[derive(Clone, Debug, thiserror::Error)] -#[cfg_attr(test, derive(PartialEq))] -#[error("Handle range {range:?} of {kind} is either not present, or inaccessible yet")] -pub struct BadRangeError { - // This error is used for many `Handle` types, but there's no point in making this generic, so - // we just flatten them all to `Handle<()>` here. - kind: &'static str, - range: Range<()>, -} - -impl BadRangeError { - pub fn new(range: Range) -> Self { - Self { - kind: std::any::type_name::(), - range: range.erase_type(), - } - } -} +[`Handle`]: Handle +*/ -impl Clone for Range { - fn clone(&self) -> Self { - Range { - inner: self.inner.clone(), - marker: self.marker, - } - } -} +mod handle; +mod handlevec; +mod range; +mod unique_arena; -impl fmt::Debug for Range { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "[{}..{}]", self.inner.start, self.inner.end - 1) - } -} +pub use handle::{BadHandle, Handle}; +pub(crate) use handlevec::HandleVec; +pub use range::{BadRangeError, Range}; +pub use unique_arena::UniqueArena; -impl Iterator for Range { - type Item = Handle; - fn next(&mut self) -> Option { - if self.inner.start < self.inner.end { - let next = self.inner.start; - self.inner.start += 1; - Some(Handle::new(NonMaxU32::new(next).unwrap())) - } else { - None - } - } -} +use crate::Span; -impl Range { - /// Return a range enclosing handles `first` through `last`, inclusive. - pub fn new_from_bounds(first: Handle, last: Handle) -> Self { - Self { - inner: (first.index() as u32)..(last.index() as u32 + 1), - marker: Default::default(), - } - } +use handle::Index; - /// return the first and last handles included in `self`. - /// - /// If `self` is an empty range, there are no handles included, so - /// return `None`. - pub fn first_and_last(&self) -> Option<(Handle, Handle)> { - if self.inner.start < self.inner.end { - Some(( - // `Range::new_from_bounds` expects a start- and end-inclusive - // range, but `self.inner` is an end-exclusive range. - Handle::new(Index::new(self.inner.start).unwrap()), - Handle::new(Index::new(self.inner.end - 1).unwrap()), - )) - } else { - None - } - } - - /// Return the index range covered by `self`. - pub fn index_range(&self) -> ops::Range { - self.inner.clone() - } - - /// Construct a `Range` that covers the indices in `inner`. - pub fn from_index_range(inner: ops::Range, arena: &Arena) -> Self { - // Since `inner` is a `Range`, we only need to check that - // the start and end are well-ordered, and that the end fits - // within `arena`. - assert!(inner.start <= inner.end); - assert!(inner.end as usize <= arena.len()); - Self { - inner, - marker: Default::default(), - } - } -} +use std::{fmt, ops}; /// An arena holding some kind of component (e.g., type, constant, /// instruction, etc.) that can be referenced. @@ -526,355 +325,3 @@ mod tests { assert!(arena[t1] != arena[t2]); } } - -/// An arena whose elements are guaranteed to be unique. -/// -/// A `UniqueArena` holds a set of unique values of type `T`, each with an -/// associated [`Span`]. Inserting a value returns a `Handle`, which can be -/// used to index the `UniqueArena` and obtain shared access to the `T` element. -/// Access via a `Handle` is an array lookup - no hash lookup is necessary. -/// -/// The element type must implement `Eq` and `Hash`. Insertions of equivalent -/// elements, according to `Eq`, all return the same `Handle`. -/// -/// Once inserted, elements may not be mutated. -/// -/// `UniqueArena` is similar to [`Arena`]: If `Arena` is vector-like, -/// `UniqueArena` is `HashSet`-like. -#[derive(Clone)] -pub struct UniqueArena { - set: FastIndexSet, - - /// Spans for the elements, indexed by handle. - /// - /// The length of this vector is always equal to `set.len()`. `FastIndexSet` - /// promises that its elements "are indexed in a compact range, without - /// holes in the range 0..set.len()", so we can always use the indices - /// returned by insertion as indices into this vector. - span_info: Vec, -} - -impl UniqueArena { - /// Create a new arena with no initial capacity allocated. - pub fn new() -> Self { - UniqueArena { - set: FastIndexSet::default(), - span_info: Vec::new(), - } - } - - /// Return the current number of items stored in this arena. - pub fn len(&self) -> usize { - self.set.len() - } - - /// Return `true` if the arena contains no elements. - pub fn is_empty(&self) -> bool { - self.set.is_empty() - } - - /// Clears the arena, keeping all allocations. - pub fn clear(&mut self) { - self.set.clear(); - self.span_info.clear(); - } - - /// Return the span associated with `handle`. - /// - /// If a value has been inserted multiple times, the span returned is the - /// one provided with the first insertion. - pub fn get_span(&self, handle: Handle) -> Span { - *self - .span_info - .get(handle.index()) - .unwrap_or(&Span::default()) - } - - #[cfg(feature = "compact")] - pub(crate) fn drain_all(&mut self) -> UniqueArenaDrain { - UniqueArenaDrain { - inner_elts: self.set.drain(..), - inner_spans: self.span_info.drain(..), - index: Index::new(0).unwrap(), - } - } -} - -#[cfg(feature = "compact")] -pub(crate) struct UniqueArenaDrain<'a, T> { - inner_elts: indexmap::set::Drain<'a, T>, - inner_spans: std::vec::Drain<'a, Span>, - index: Index, -} - -#[cfg(feature = "compact")] -impl<'a, T> Iterator for UniqueArenaDrain<'a, T> { - type Item = (Handle, T, Span); - - fn next(&mut self) -> Option { - match self.inner_elts.next() { - Some(elt) => { - let handle = Handle::new(self.index); - self.index = self.index.checked_add(1).unwrap(); - let span = self.inner_spans.next().unwrap(); - Some((handle, elt, span)) - } - None => None, - } - } -} - -impl UniqueArena { - /// Returns an iterator over the items stored in this arena, returning both - /// the item's handle and a reference to it. - pub fn iter(&self) -> impl DoubleEndedIterator, &T)> { - self.set.iter().enumerate().map(|(i, v)| { - let index = unsafe { Index::new_unchecked(i as u32) }; - (Handle::new(index), v) - }) - } - - /// Insert a new value into the arena. - /// - /// Return a [`Handle`], which can be used to index this arena to get a - /// shared reference to the element. - /// - /// If this arena already contains an element that is `Eq` to `value`, - /// return a `Handle` to the existing element, and drop `value`. - /// - /// If `value` is inserted into the arena, associate `span` with - /// it. An element's span can be retrieved with the [`get_span`] - /// method. - /// - /// [`Handle`]: Handle - /// [`get_span`]: UniqueArena::get_span - pub fn insert(&mut self, value: T, span: Span) -> Handle { - let (index, added) = self.set.insert_full(value); - - if added { - debug_assert!(index == self.span_info.len()); - self.span_info.push(span); - } - - debug_assert!(self.set.len() == self.span_info.len()); - - Handle::from_usize(index) - } - - /// Replace an old value with a new value. - /// - /// # Panics - /// - /// - if the old value is not in the arena - /// - if the new value already exists in the arena - pub fn replace(&mut self, old: Handle, new: T) { - let (index, added) = self.set.insert_full(new); - assert!(added && index == self.set.len() - 1); - - self.set.swap_remove_index(old.index()).unwrap(); - } - - /// Return this arena's handle for `value`, if present. - /// - /// If this arena already contains an element equal to `value`, - /// return its handle. Otherwise, return `None`. - pub fn get(&self, value: &T) -> Option> { - self.set - .get_index_of(value) - .map(|index| unsafe { Handle::from_usize_unchecked(index) }) - } - - /// Return this arena's value at `handle`, if that is a valid handle. - pub fn get_handle(&self, handle: Handle) -> Result<&T, BadHandle> { - self.set - .get_index(handle.index()) - .ok_or_else(|| BadHandle::new(handle)) - } - - /// Assert that `handle` is valid for this arena. - pub fn check_contains_handle(&self, handle: Handle) -> Result<(), BadHandle> { - if handle.index() < self.set.len() { - Ok(()) - } else { - Err(BadHandle::new(handle)) - } - } -} - -impl Default for UniqueArena { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for UniqueArena { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -impl ops::Index> for UniqueArena { - type Output = T; - fn index(&self, handle: Handle) -> &T { - &self.set[handle.index()] - } -} - -#[cfg(feature = "serialize")] -impl serde::Serialize for UniqueArena -where - T: Eq + hash::Hash + serde::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.set.serialize(serializer) - } -} - -#[cfg(feature = "deserialize")] -impl<'de, T> serde::Deserialize<'de> for UniqueArena -where - T: Eq + hash::Hash + serde::Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let set = FastIndexSet::deserialize(deserializer)?; - let span_info = std::iter::repeat(Span::default()).take(set.len()).collect(); - - Ok(Self { set, span_info }) - } -} - -//Note: largely borrowed from `HashSet` implementation -#[cfg(feature = "arbitrary")] -impl<'a, T> arbitrary::Arbitrary<'a> for UniqueArena -where - T: Eq + hash::Hash + arbitrary::Arbitrary<'a>, -{ - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let mut arena = Self::default(); - for elem in u.arbitrary_iter()? { - arena.set.insert(elem?); - arena.span_info.push(Span::UNDEFINED); - } - Ok(arena) - } - - fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result { - let mut arena = Self::default(); - for elem in u.arbitrary_take_rest_iter()? { - arena.set.insert(elem?); - arena.span_info.push(Span::UNDEFINED); - } - Ok(arena) - } - - #[inline] - fn size_hint(depth: usize) -> (usize, Option) { - let depth_hint = ::size_hint(depth); - arbitrary::size_hint::and(depth_hint, (0, None)) - } -} - -/// A [`Vec`] indexed by [`Handle`]s. -/// -/// A `HandleVec` is a [`Vec`] indexed by values of type `Handle`, -/// rather than `usize`. -/// -/// Rather than a `push` method, `HandleVec` has an [`insert`] method, analogous -/// to [`HashMap::insert`], that requires you to provide the handle at which the -/// new value should appear. However, since `HandleVec` only supports insertion -/// at the end, the given handle's index must be equal to the the `HandleVec`'s -/// current length; otherwise, the insertion will panic. -/// -/// [`insert`]: HandleVec::insert -/// [`HashMap::insert`]: std::collections::HashMap::insert -#[derive(Debug)] -pub(crate) struct HandleVec { - inner: Vec, - as_keys: PhantomData, -} - -impl Default for HandleVec { - fn default() -> Self { - Self { - inner: vec![], - as_keys: PhantomData, - } - } -} - -#[allow(dead_code)] -impl HandleVec { - pub(crate) const fn new() -> Self { - Self { - inner: vec![], - as_keys: PhantomData, - } - } - - pub(crate) fn with_capacity(capacity: usize) -> Self { - Self { - inner: Vec::with_capacity(capacity), - as_keys: PhantomData, - } - } - - pub(crate) fn len(&self) -> usize { - self.inner.len() - } - - /// Insert a mapping from `handle` to `value`. - /// - /// Unlike a [`HashMap`], a `HandleVec` can only have new entries inserted at - /// the end, like [`Vec::push`]. So the index of `handle` must equal - /// [`self.len()`]. - /// - /// [`HashMap`]: std::collections::HashMap - /// [`self.len()`]: HandleVec::len - pub(crate) fn insert(&mut self, handle: Handle, value: U) { - assert_eq!(handle.index(), self.inner.len()); - self.inner.push(value); - } - - pub(crate) fn get(&self, handle: Handle) -> Option<&U> { - self.inner.get(handle.index()) - } - - pub(crate) fn clear(&mut self) { - self.inner.clear() - } - - pub(crate) fn resize(&mut self, len: usize, fill: U) - where - U: Clone, - { - self.inner.resize(len, fill); - } - - pub(crate) fn iter(&self) -> impl Iterator { - self.inner.iter() - } - - pub(crate) fn iter_mut(&mut self) -> impl Iterator { - self.inner.iter_mut() - } -} - -impl ops::Index> for HandleVec { - type Output = U; - - fn index(&self, handle: Handle) -> &Self::Output { - &self.inner[handle.index()] - } -} - -impl ops::IndexMut> for HandleVec { - fn index_mut(&mut self, handle: Handle) -> &mut Self::Output { - &mut self.inner[handle.index()] - } -} diff --git a/naga/src/arena/range.rs b/naga/src/arena/range.rs new file mode 100644 index 0000000000..80b8c101b4 --- /dev/null +++ b/naga/src/arena/range.rs @@ -0,0 +1,131 @@ +//! Well-typed ranges of [`Arena`]s. +//! +//! This module defines the [`Range`] type, representing a contiguous range of +//! entries in an [`Arena`]. +//! +//! [`Arena`]: super::Arena + +use super::{ + handle::{Handle, Index}, + Arena, +}; + +use std::{fmt, marker::PhantomData, ops}; + +/// A strongly typed range of handles. +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +#[cfg_attr( + any(feature = "serialize", feature = "deserialize"), + serde(transparent) +)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(test, derive(PartialEq))] +pub struct Range { + pub(super) inner: ops::Range, + #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))] + marker: PhantomData, +} + +impl Range { + pub(crate) const fn erase_type(self) -> Range<()> { + let Self { inner, marker: _ } = self; + Range { + inner, + marker: PhantomData, + } + } +} + +// NOTE: Keep this diagnostic in sync with that of [`BadHandle`]. +#[derive(Clone, Debug, thiserror::Error)] +#[cfg_attr(test, derive(PartialEq))] +#[error("Handle range {range:?} of {kind} is either not present, or inaccessible yet")] +pub struct BadRangeError { + // This error is used for many `Handle` types, but there's no point in making this generic, so + // we just flatten them all to `Handle<()>` here. + kind: &'static str, + range: Range<()>, +} + +impl BadRangeError { + pub fn new(range: Range) -> Self { + Self { + kind: std::any::type_name::(), + range: range.erase_type(), + } + } +} + +impl Clone for Range { + fn clone(&self) -> Self { + Range { + inner: self.inner.clone(), + marker: self.marker, + } + } +} + +impl fmt::Debug for Range { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "[{}..{}]", self.inner.start, self.inner.end - 1) + } +} + +impl Iterator for Range { + type Item = Handle; + fn next(&mut self) -> Option { + if self.inner.start < self.inner.end { + let next = self.inner.start; + self.inner.start += 1; + Some(Handle::new(Index::new(next).unwrap())) + } else { + None + } + } +} + +impl Range { + /// Return a range enclosing handles `first` through `last`, inclusive. + pub fn new_from_bounds(first: Handle, last: Handle) -> Self { + Self { + inner: (first.index() as u32)..(last.index() as u32 + 1), + marker: Default::default(), + } + } + + /// return the first and last handles included in `self`. + /// + /// If `self` is an empty range, there are no handles included, so + /// return `None`. + pub fn first_and_last(&self) -> Option<(Handle, Handle)> { + if self.inner.start < self.inner.end { + Some(( + // `Range::new_from_bounds` expects a start- and end-inclusive + // range, but `self.inner` is an end-exclusive range. + Handle::new(Index::new(self.inner.start).unwrap()), + Handle::new(Index::new(self.inner.end - 1).unwrap()), + )) + } else { + None + } + } + + /// Return the index range covered by `self`. + pub fn index_range(&self) -> ops::Range { + self.inner.clone() + } + + /// Construct a `Range` that covers the indices in `inner`. + pub fn from_index_range(inner: ops::Range, arena: &Arena) -> Self { + // Since `inner` is a `Range`, we only need to check that + // the start and end are well-ordered, and that the end fits + // within `arena`. + assert!(inner.start <= inner.end); + assert!(inner.end as usize <= arena.len()); + Self { + inner, + marker: Default::default(), + } + } +} diff --git a/naga/src/arena/unique_arena.rs b/naga/src/arena/unique_arena.rs new file mode 100644 index 0000000000..552c1b7d89 --- /dev/null +++ b/naga/src/arena/unique_arena.rs @@ -0,0 +1,262 @@ +//! The [`UniqueArena`] type and supporting definitions. + +use crate::{FastIndexSet, Span}; + +use super::handle::{BadHandle, Handle, Index}; + +use std::{fmt, hash, ops}; + +/// An arena whose elements are guaranteed to be unique. +/// +/// A `UniqueArena` holds a set of unique values of type `T`, each with an +/// associated [`Span`]. Inserting a value returns a `Handle`, which can be +/// used to index the `UniqueArena` and obtain shared access to the `T` element. +/// Access via a `Handle` is an array lookup - no hash lookup is necessary. +/// +/// The element type must implement `Eq` and `Hash`. Insertions of equivalent +/// elements, according to `Eq`, all return the same `Handle`. +/// +/// Once inserted, elements may not be mutated. +/// +/// `UniqueArena` is similar to [`Arena`]: If `Arena` is vector-like, +/// `UniqueArena` is `HashSet`-like. +/// +/// [`Arena`]: super::Arena +#[derive(Clone)] +pub struct UniqueArena { + set: FastIndexSet, + + /// Spans for the elements, indexed by handle. + /// + /// The length of this vector is always equal to `set.len()`. `FastIndexSet` + /// promises that its elements "are indexed in a compact range, without + /// holes in the range 0..set.len()", so we can always use the indices + /// returned by insertion as indices into this vector. + span_info: Vec, +} + +impl UniqueArena { + /// Create a new arena with no initial capacity allocated. + pub fn new() -> Self { + UniqueArena { + set: FastIndexSet::default(), + span_info: Vec::new(), + } + } + + /// Return the current number of items stored in this arena. + pub fn len(&self) -> usize { + self.set.len() + } + + /// Return `true` if the arena contains no elements. + pub fn is_empty(&self) -> bool { + self.set.is_empty() + } + + /// Clears the arena, keeping all allocations. + pub fn clear(&mut self) { + self.set.clear(); + self.span_info.clear(); + } + + /// Return the span associated with `handle`. + /// + /// If a value has been inserted multiple times, the span returned is the + /// one provided with the first insertion. + pub fn get_span(&self, handle: Handle) -> Span { + *self + .span_info + .get(handle.index()) + .unwrap_or(&Span::default()) + } + + #[cfg(feature = "compact")] + pub(crate) fn drain_all(&mut self) -> UniqueArenaDrain { + UniqueArenaDrain { + inner_elts: self.set.drain(..), + inner_spans: self.span_info.drain(..), + index: Index::new(0).unwrap(), + } + } +} + +#[cfg(feature = "compact")] +pub struct UniqueArenaDrain<'a, T> { + inner_elts: indexmap::set::Drain<'a, T>, + inner_spans: std::vec::Drain<'a, Span>, + index: Index, +} + +#[cfg(feature = "compact")] +impl<'a, T> Iterator for UniqueArenaDrain<'a, T> { + type Item = (Handle, T, Span); + + fn next(&mut self) -> Option { + match self.inner_elts.next() { + Some(elt) => { + let handle = Handle::new(self.index); + self.index = self.index.checked_add(1).unwrap(); + let span = self.inner_spans.next().unwrap(); + Some((handle, elt, span)) + } + None => None, + } + } +} + +impl UniqueArena { + /// Returns an iterator over the items stored in this arena, returning both + /// the item's handle and a reference to it. + pub fn iter(&self) -> impl DoubleEndedIterator, &T)> { + self.set.iter().enumerate().map(|(i, v)| { + let index = unsafe { Index::new_unchecked(i as u32) }; + (Handle::new(index), v) + }) + } + + /// Insert a new value into the arena. + /// + /// Return a [`Handle`], which can be used to index this arena to get a + /// shared reference to the element. + /// + /// If this arena already contains an element that is `Eq` to `value`, + /// return a `Handle` to the existing element, and drop `value`. + /// + /// If `value` is inserted into the arena, associate `span` with + /// it. An element's span can be retrieved with the [`get_span`] + /// method. + /// + /// [`Handle`]: Handle + /// [`get_span`]: UniqueArena::get_span + pub fn insert(&mut self, value: T, span: Span) -> Handle { + let (index, added) = self.set.insert_full(value); + + if added { + debug_assert!(index == self.span_info.len()); + self.span_info.push(span); + } + + debug_assert!(self.set.len() == self.span_info.len()); + + Handle::from_usize(index) + } + + /// Replace an old value with a new value. + /// + /// # Panics + /// + /// - if the old value is not in the arena + /// - if the new value already exists in the arena + pub fn replace(&mut self, old: Handle, new: T) { + let (index, added) = self.set.insert_full(new); + assert!(added && index == self.set.len() - 1); + + self.set.swap_remove_index(old.index()).unwrap(); + } + + /// Return this arena's handle for `value`, if present. + /// + /// If this arena already contains an element equal to `value`, + /// return its handle. Otherwise, return `None`. + pub fn get(&self, value: &T) -> Option> { + self.set + .get_index_of(value) + .map(|index| unsafe { Handle::from_usize_unchecked(index) }) + } + + /// Return this arena's value at `handle`, if that is a valid handle. + pub fn get_handle(&self, handle: Handle) -> Result<&T, BadHandle> { + self.set + .get_index(handle.index()) + .ok_or_else(|| BadHandle::new(handle)) + } + + /// Assert that `handle` is valid for this arena. + pub fn check_contains_handle(&self, handle: Handle) -> Result<(), BadHandle> { + if handle.index() < self.set.len() { + Ok(()) + } else { + Err(BadHandle::new(handle)) + } + } +} + +impl Default for UniqueArena { + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for UniqueArena { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl ops::Index> for UniqueArena { + type Output = T; + fn index(&self, handle: Handle) -> &T { + &self.set[handle.index()] + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for UniqueArena +where + T: Eq + hash::Hash + serde::Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.set.serialize(serializer) + } +} + +#[cfg(feature = "deserialize")] +impl<'de, T> serde::Deserialize<'de> for UniqueArena +where + T: Eq + hash::Hash + serde::Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let set = FastIndexSet::deserialize(deserializer)?; + let span_info = std::iter::repeat(Span::default()).take(set.len()).collect(); + + Ok(Self { set, span_info }) + } +} + +//Note: largely borrowed from `HashSet` implementation +#[cfg(feature = "arbitrary")] +impl<'a, T> arbitrary::Arbitrary<'a> for UniqueArena +where + T: Eq + hash::Hash + arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let mut arena = Self::default(); + for elem in u.arbitrary_iter()? { + arena.set.insert(elem?); + arena.span_info.push(Span::UNDEFINED); + } + Ok(arena) + } + + fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result { + let mut arena = Self::default(); + for elem in u.arbitrary_take_rest_iter()? { + arena.set.insert(elem?); + arena.span_info.push(Span::UNDEFINED); + } + Ok(arena) + } + + #[inline] + fn size_hint(depth: usize) -> (usize, Option) { + let depth_hint = ::size_hint(depth); + arbitrary::size_hint::and(depth_hint, (0, None)) + } +} diff --git a/naga/src/lib.rs b/naga/src/lib.rs index 5696f4445e..8ed7527922 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -34,25 +34,6 @@ with optional span info, representing a series of statements executed in order. `EntryPoint`s or `Function` is a `Block`, and `Statement` has a [`Block`][Statement::Block] variant. -## Arenas - -To improve translator performance and reduce memory usage, most structures are -stored in an [`Arena`]. An `Arena` stores a series of `T` values, indexed by -[`Handle`](Handle) values, which are just wrappers around integer indexes. -For example, a `Function`'s expressions are stored in an `Arena`, -and compound expressions refer to their sub-expressions via `Handle` -values. (When examining the serialized form of a `Module`, note that the first -element of an `Arena` has an index of 1, not 0.) - -A [`UniqueArena`] is just like an `Arena`, except that it stores only a single -instance of each value. The value type must implement `Eq` and `Hash`. Like an -`Arena`, inserting a value into a `UniqueArena` returns a `Handle` which can be -used to efficiently access the value, without a hash lookup. Inserting a value -multiple times returns the same `Handle`. - -If the `span` feature is enabled, both `Arena` and `UniqueArena` can associate a -source code span with each element. - ## Function Calls Naga's representation of function calls is unusual. Most languages treat