diff --git a/xso/ChangeLog b/xso/ChangeLog index eceefdff300b66b8d71e6b5f1630b455c3b31111..bdfdd96befeacd262680b52a73446e8f3d097a65 100644 --- a/xso/ChangeLog +++ b/xso/ChangeLog @@ -59,6 +59,8 @@ Version NEXT: elements are valid candidates for parsing (!573). - `Xso`, a Box-like struct which can hold a dynamically-typed XSO, alongside related traits and (declarative) macros (!573). + - `XsoVec`, a container which can hold dynamically-typed + XSOs (!573). * Changes - Generated AsXml iterator and FromXml builder types are now doc(hidden), to not clutter hand-written documentation with auto diff --git a/xso/src/dynxso.rs b/xso/src/dynxso.rs index 5bb6f89ccd8afcf420e949e2f72b72e1d220427b..04390043bb11374690dfec397103ce4b8705d9f8 100644 --- a/xso/src/dynxso.rs +++ b/xso/src/dynxso.rs @@ -61,12 +61,20 @@ //! assert_eq!(Bar, *x.downcast::().unwrap()); //! ``` -use alloc::{boxed::Box, vec::Vec}; +use alloc::{ + boxed::Box, + collections::{ + btree_map::{self, Entry}, + BTreeMap, + }, + vec::{self, Vec}, +}; use core::{ any::{Any, TypeId}, fmt, marker::PhantomData, ops::{Deref, DerefMut}, + slice, }; use std::sync::Mutex; @@ -721,6 +729,21 @@ impl Xso { } } + fn force_downcast(self) -> Box + where + T: MayContain, + { + match self.downcast::() { + Ok(v) => v, + Err(v) => panic!( + "force_downcast called on mismatching types: requested {:?} ({}) != actual {:?}", + TypeId::of::(), + core::any::type_name::(), + v.inner_type_id() + ), + } + } + /// Downcast `&self` to `&U`. /// /// ``` @@ -776,7 +799,7 @@ impl Xso { } fn inner_type_id(&self) -> TypeId { - (&self.inner as &dyn Any).type_id() + DynXso::type_id(&*self.inner) } /// Register a new type to be constructible. @@ -884,3 +907,698 @@ impl AsXml for Xso { self.inner.as_xml_dyn_iter() } } + +/// Error type for retrieving a single item from `XsoVec`. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum TakeOneError { + /// More than one item was found. + MultipleEntries, +} + +impl fmt::Display for TakeOneError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::MultipleEntries => f.write_str("multiple entries found"), + } + } +} + +/// # Container for dynamically-typed XSOs optimized for type-keyed access +/// +/// This container holds dynamically typed XSOs (see +/// [`Xso`][`Xso`]). It allows efficient access to its contents +/// based on the actual type. +/// +/// Like `Xso` itself, `XsoVec` requires that +/// `MayContain` is implemented by `dyn Trait` for all items which are added +/// to the container. This is automatically the case for all `T: Trait` +/// if [`derive_dyn_traits`][`crate::derive_dyn_traits`] has been used on +/// `Trait`. +/// +/// Note that `XsoVec` has a non-obvious iteration order, which is described +/// in [`XsoVec::iter()`][`Self::iter`]. +pub struct XsoVec { + inner: BTreeMap>>, +} + +impl fmt::Debug for XsoVec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "XsoVec[{} types, {} items]", + self.inner.len(), + self.len() + ) + } +} + +impl Default for XsoVec { + fn default() -> Self { + Self { + inner: BTreeMap::default(), + } + } +} + +impl XsoVec { + /// Construct a new, empty `XsoVec`. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// let mut vec = XsoVec::::new(); + /// ``` + pub const fn new() -> Self { + Self { + inner: BTreeMap::new(), + } + } + + /// Return a reference to the first item of type `U`. + /// + /// If the container does not hold any item of type `U`, return `None`. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// assert_eq!(vec.get_first::(), Some(&Foo(2))); + /// assert_eq!(vec.get_first::(), Some(&Bar(1))); + /// assert_eq!(vec.get_first::(), None); + /// + /// ``` + pub fn get_first(&self) -> Option<&U> + where + T: MayContain, + { + self.iter_typed::().next() + } + + /// Return a mutable reference to the first item of type `U`. + /// + /// If the container does not hold any item of type `U`, return `None`. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Foo(1)); + /// vec.get_first_mut::().unwrap().0 = 2; + /// assert_eq!(vec.get_first::(), Some(&Foo(2))); + /// ``` + pub fn get_first_mut(&mut self) -> Option<&mut U> + where + T: MayContain, + { + self.iter_typed_mut::().next() + } + + /// Take and return exactly one item of type `U`. + /// + /// If no item of type `U` is present in the container, return Ok(None). + /// If more than one item of type `U` is present in the container, + /// return an error. + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// assert_eq!(vec.take_one::(), Err(TakeOneError::MultipleEntries)); + /// assert_eq!(*vec.take_one::().unwrap().unwrap(), Bar(1)); + /// assert_eq!(vec.take_one::(), Ok(None)); + /// assert_eq!(vec.take_one::(), Ok(None)); + /// ``` + pub fn take_one(&mut self) -> Result>, TakeOneError> + where + T: MayContain, + { + let source = match self.inner.get_mut(&TypeId::of::()) { + Some(v) => v, + None => return Ok(None), + }; + if source.len() > 1 { + return Err(TakeOneError::MultipleEntries); + } + Ok(source.pop().map(Xso::force_downcast)) + } + + /// Take and return the first item of type `U`. + /// + /// If no item of type `U` is present in the container, return None. + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// assert_eq!(*vec.take_first::().unwrap(), Foo(2)); + /// assert_eq!(*vec.take_first::().unwrap(), Foo(1)); + /// assert_eq!(*vec.take_first::().unwrap(), Bar(1)); + /// assert_eq!(vec.take_first::(), None); + /// assert_eq!(vec.take_first::(), None); + /// ``` + pub fn take_first(&mut self) -> Option> + where + T: MayContain, + { + let source = self.inner.get_mut(&TypeId::of::())?; + if source.len() == 0 { + return None; + } + Some(source.remove(0).force_downcast()) + } + + /// Take and return the last item of type `U`. + /// + /// If no item of type `U` is present in the container, return None. + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// assert_eq!(*vec.take_last::().unwrap(), Foo(1)); + /// assert_eq!(*vec.take_last::().unwrap(), Foo(2)); + /// assert_eq!(*vec.take_last::().unwrap(), Bar(1)); + /// assert_eq!(vec.take_last::(), None); + /// assert_eq!(vec.take_last::(), None); + /// ``` + pub fn take_last(&mut self) -> Option> + where + T: MayContain, + { + let source = self.inner.get_mut(&TypeId::of::())?; + source.pop().map(Xso::force_downcast) + } + + /// Iterate all items of type `U` as references. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// + /// let foos: Vec<_> = vec.iter_typed::().collect(); + /// assert_eq!(&foos[..], &[&Foo(2), &Foo(1)]); + /// ``` + pub fn iter_typed(&self) -> impl Iterator + where + T: MayContain, + { + let iter = match self.inner.get(&TypeId::of::()) { + Some(v) => v.deref().iter(), + None => (&[]).iter(), + }; + // UNWRAP: We group the values by TypeId, so the downcast should never + // fail, but I am too chicken to use the unchecked variants :). + iter.map(|x| x.downcast_ref::().unwrap()) + } + + /// Iterate all items of type `U` as mutable references. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// + /// let foos: Vec<_> = vec.iter_typed_mut::().collect(); + /// assert_eq!(&foos[..], &[&mut Foo(2), &mut Foo(1)]); + /// ``` + pub fn iter_typed_mut(&mut self) -> impl Iterator + where + T: MayContain, + { + let iter = match self.inner.get_mut(&TypeId::of::()) { + Some(v) => v.deref_mut().iter_mut(), + None => (&mut []).iter_mut(), + }; + // UNWRAP: We group the values by TypeId, so the downcast should never + // fail, but I am too chicken to use the unchecked variants :). + iter.map(|x| x.downcast_mut::().unwrap()) + } + + /// Drain all items of type `U` out of the container. + /// + /// If the result is dropped before the end of the iterator has been + /// reached, the remaining items are still dropped out of the container. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Baz(u32); + /// impl Trait for Baz {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// vec.push(Foo(1)); + /// + /// let foos: Vec<_> = vec.drain_typed::().map(|x| *x).collect(); + /// // converts Box to T ↑ + /// assert_eq!(&foos[..], &[Foo(2), Foo(1)]); + /// ``` + pub fn drain_typed(&mut self) -> impl Iterator> + where + T: MayContain, + { + let iter = match self.inner.remove(&TypeId::of::()) { + Some(v) => v.into_iter(), + None => Vec::new().into_iter(), + }; + // UNWRAP: We group the values by TypeId, so the downcast should never + // fail, but I am too chicken to use the unchecked variants :). + iter.map(|x| match x.downcast::() { + Ok(v) => v, + Err(_) => { + unreachable!("TypeId disagrees with Xso<_>::downcast, or internal state corruption") + } + }) + } + + fn ensure_vec_mut_for(&mut self, type_id: TypeId) -> &mut Vec> { + match self.inner.entry(type_id) { + Entry::Vacant(v) => v.insert(Vec::new()), + Entry::Occupied(o) => o.into_mut(), + } + } + + /// Push a new item of type `U` to the end of the section of `U` inside + /// the container. + /// + /// Please note the information about iteration order of the `XsoVec` + /// at [`XsoVec::iter`][`Self::iter`]. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Foo(1)); + /// ``` + pub fn push(&mut self, value: U) + where + T: MayContain, + { + self.ensure_vec_mut_for(TypeId::of::()) + .push(Xso::wrap(value)); + } + + /// Push a new dynamically typed item to the end of the section of values + /// with the same type inside the container. + /// + /// Please note the information about iteration order of the `XsoVec` + /// at [`XsoVec::iter`][`Self::iter`]. + /// + /// ``` + /// # use xso::dynxso::Xso; + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u8); + /// impl Trait for Bar {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Foo(1)); + /// vec.push_dyn(Xso::wrap(Foo(2))); + /// vec.push_dyn(Xso::wrap(Bar(1))); + /// vec.push(Bar(2)); + /// + /// let foos: Vec<_> = vec.iter_typed::().collect(); + /// assert_eq!(&foos[..], &[&Foo(1), &Foo(2)]); + /// + /// let bars: Vec<_> = vec.iter_typed::().collect(); + /// assert_eq!(&bars[..], &[&Bar(1), &Bar(2)]); + /// ``` + pub fn push_dyn(&mut self, value: Xso) { + self.ensure_vec_mut_for(value.inner_type_id()).push(value); + } +} + +impl XsoVec { + /// Clear all contents, without deallocating memory. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Foo(1)); + /// vec.push(Foo(2)); + /// vec.clear(); + /// assert_eq!(vec.len(), 0); + /// ``` + pub fn clear(&mut self) { + self.inner.values_mut().for_each(|x| x.clear()); + } + + /// Return true if there are no items in the container. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// let mut vec = XsoVec::::new(); + /// assert!(vec.is_empty()); + /// vec.push(Foo(1)); + /// assert!(!vec.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.values().all(|x| x.is_empty()) + } + + /// Reduce memory use of the container to the minimum required to hold + /// the current data. + /// + /// This may be expensive if lots of data needs to be shuffled. + pub fn shrink_to_fit(&mut self) { + self.inner.retain(|_, x| { + if x.is_empty() { + return false; + } + x.shrink_to_fit(); + true + }); + } + + /// Return the total amount of items in the container. + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// let mut vec = XsoVec::::new(); + /// assert_eq!(vec.len(), 0); + /// vec.push(Foo(1)); + /// assert_eq!(vec.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.values().map(|x| x.len()).sum() + } + + /// Iterate the items inside the container. + /// + /// This iterator (unlike the iterator returned by + /// [`iter_typed()`][`Self::iter_typed`]) yields references to **untyped** + /// [`Xso`][`Xso`]. + /// + /// # Iteration order + /// + /// Items which have the same concrete type are grouped and their ordering + /// with respect to one another is preserved. However, the ordering of + /// items with *different* concrete types is unspecified. + /// + /// # Example + /// + /// ``` + #[doc = include_str!("xso_vec_test_prelude.rs")] + /// #[derive(PartialEq, Debug)] + /// struct Foo(u8); + /// impl Trait for Foo {} + /// + /// #[derive(PartialEq, Debug)] + /// struct Bar(u16); + /// impl Trait for Bar {} + /// + /// let mut vec = XsoVec::::new(); + /// vec.push(Foo(1)); + /// vec.push(Bar(1)); + /// vec.push(Foo(2)); + /// + /// for item in vec.iter() { + /// println!("{:?}", item); + /// } + /// ``` + pub fn iter(&self) -> XsoVecIter<'_, T> { + XsoVecIter { + remaining: self.len(), + outer: self.inner.values(), + inner: None, + } + } + + /// Iterate the items inside the container, mutably. + /// + /// This iterator (unlike the iterator returned by + /// [`iter_typed_mut()`][`Self::iter_typed_mut`]) yields mutable + /// references to **untyped** [`Xso`][`Xso`]. + /// + /// Please note the information about iteration order of the `XsoVec` + /// at [`XsoVec::iter`][`Self::iter`]. + pub fn iter_mut(&mut self) -> XsoVecIterMut<'_, T> { + XsoVecIterMut { + remaining: self.len(), + outer: self.inner.values_mut(), + inner: None, + } + } +} + +impl IntoIterator for XsoVec { + type Item = Xso; + type IntoIter = XsoVecIntoIter; + + fn into_iter(self) -> Self::IntoIter { + XsoVecIntoIter { + remaining: self.len(), + outer: self.inner.into_values(), + inner: None, + } + } +} + +impl<'x, T: ?Sized> IntoIterator for &'x XsoVec { + type Item = &'x Xso; + type IntoIter = XsoVecIter<'x, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'x, T: ?Sized> IntoIterator for &'x mut XsoVec { + type Item = &'x mut Xso; + type IntoIter = XsoVecIterMut<'x, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl Extend> for XsoVec { + fn extend>>(&mut self, iter: I) { + for item in iter { + self.ensure_vec_mut_for(item.inner_type_id()).push(item); + } + } +} + +/// Helper types for [`XsoVec`]. +pub mod xso_vec { + use super::*; + + /// Iterator over the contents of an [`XsoVec`]. + pub struct XsoVecIter<'x, T: ?Sized> { + pub(super) outer: btree_map::Values<'x, TypeId, Vec>>, + pub(super) inner: Option>>, + pub(super) remaining: usize, + } + + impl<'x, T: ?Sized> Iterator for XsoVecIter<'x, T> { + type Item = &'x Xso; + + fn next(&mut self) -> Option { + loop { + if let Some(inner) = self.inner.as_mut() { + if let Some(item) = inner.next() { + self.remaining = self.remaining.saturating_sub(1); + return Some(item); + } + // Inner is exhausted, so equivalent to None, fall through. + } + // The `?` in there is our exit condition. + self.inner = Some(self.outer.next()?.deref().iter()) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } + } + + /// Mutable iterator over the contents of an [`XsoVec`]. + pub struct XsoVecIterMut<'x, T: ?Sized> { + pub(super) outer: btree_map::ValuesMut<'x, TypeId, Vec>>, + pub(super) inner: Option>>, + pub(super) remaining: usize, + } + + impl<'x, T: ?Sized> Iterator for XsoVecIterMut<'x, T> { + type Item = &'x mut Xso; + + fn next(&mut self) -> Option { + loop { + if let Some(inner) = self.inner.as_mut() { + if let Some(item) = inner.next() { + self.remaining = self.remaining.saturating_sub(1); + return Some(item); + } + // Inner is exhausted, so equivalent to None, fall through. + } + // The `?` in there is our exit condition. + self.inner = Some(self.outer.next()?.deref_mut().iter_mut()) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } + } + + /// Iterator over the owned contents of an [`XsoVec`]. + pub struct XsoVecIntoIter { + pub(super) outer: btree_map::IntoValues>>, + pub(super) inner: Option>>, + pub(super) remaining: usize, + } + + impl Iterator for XsoVecIntoIter { + type Item = Xso; + + fn next(&mut self) -> Option { + loop { + if let Some(inner) = self.inner.as_mut() { + if let Some(item) = inner.next() { + self.remaining = self.remaining.saturating_sub(1); + return Some(item); + } + // Inner is exhausted, so equivalent to None, fall through. + } + // The `?` in there is our exit condition. + self.inner = Some(self.outer.next()?.into_iter()) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } + } +} + +use xso_vec::*; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn xso_inner_type_id_is_correct() { + trait Trait: Any {} + crate::derive_dyn_traits!(Trait use () = ()); + struct Foo; + impl Trait for Foo {} + + let ty_id = TypeId::of::(); + let x: Xso = Xso::wrap(Foo); + assert_eq!(x.inner_type_id(), ty_id); + } +} diff --git a/xso/src/xso_vec_test_prelude.rs b/xso/src/xso_vec_test_prelude.rs new file mode 100644 index 0000000000000000000000000000000000000000..18405423babce6b193feb9062a2adc400c172644 --- /dev/null +++ b/xso/src/xso_vec_test_prelude.rs @@ -0,0 +1,7 @@ +# use core::any::Any; +# use core::fmt::Debug; +# use xso::{derive_dyn_traits, dynxso::{XsoVec, TakeOneError}}; +# +# trait Trait: Any + Debug {} +# +# derive_dyn_traits!(Trait);