diff --git a/src/lib.rs b/src/lib.rs index bca0560..fbd7bbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,190 +422,6 @@ mod tests { assert!(slice.iter().copied().eq(c.index(idx))); } - struct Person { - name: String, - age: u16, - hobbies: Vec, - } - - impl Containerized for Person { - type Region = PersonRegion; - } - - #[derive(Default)] - struct PersonRegion { - name_container: ::Region, - age_container: ::Region, - hobbies: as Containerized>::Region, - } - - #[derive(Debug, Clone, Copy)] - struct PersonRef<'a> { - name: <::Region as Region>::ReadItem<'a>, - age: <::Region as Region>::ReadItem<'a>, - hobbies: < as Containerized>::Region as Region>::ReadItem<'a>, - } - - impl<'a> IntoOwned<'a> for PersonRef<'a> { - type Owned = Person; - - fn into_owned(self) -> Self::Owned { - Person { - name: self.name.into_owned(), - age: self.age, - hobbies: self.hobbies.into_owned(), - } - } - - fn clone_onto(self, other: &mut Self::Owned) { - self.name.clone_onto(&mut other.name); - other.age = self.age; - self.hobbies.clone_onto(&mut other.hobbies); - } - - fn borrow_as(owned: &'a Self::Owned) -> Self { - Self { - name: IntoOwned::borrow_as(&owned.name), - age: owned.age, - hobbies: IntoOwned::borrow_as(&owned.hobbies), - } - } - } - - impl Region for PersonRegion { - type Owned = Person; - type ReadItem<'a> = PersonRef<'a> where Self: 'a; - type Index = ( - <::Region as Region>::Index, - <::Region as Region>::Index, - < as Containerized>::Region as Region>::Index, - ); - - fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self - where - Self: 'a, - { - Self { - name_container: ::Region::merge_regions( - regions.clone().map(|r| &r.name_container), - ), - age_container: ::Region::merge_regions( - regions.clone().map(|r| &r.age_container), - ), - hobbies: as Containerized>::Region::merge_regions( - regions.map(|r| &r.hobbies), - ), - } - } - - fn index(&self, (name, age, hobbies): Self::Index) -> Self::ReadItem<'_> { - PersonRef { - name: self.name_container.index(name), - age: self.age_container.index(age), - hobbies: self.hobbies.index(hobbies), - } - } - - fn reserve_regions<'a, I>(&mut self, regions: I) - where - Self: 'a, - I: Iterator + Clone, - { - self.name_container - .reserve_regions(regions.clone().map(|r| &r.name_container)); - self.age_container - .reserve_regions(regions.clone().map(|r| &r.age_container)); - self.hobbies - .reserve_regions(regions.clone().map(|r| &r.hobbies)); - } - - fn clear(&mut self) { - self.name_container.clear(); - self.age_container.clear(); - self.hobbies.clear(); - } - - fn heap_size(&self, mut callback: F) { - self.name_container.heap_size(&mut callback); - self.age_container.heap_size(&mut callback); - self.hobbies.heap_size(callback); - } - - fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> - where - Self: 'a, - { - PersonRef { - name: ::Region::reborrow(item.name), - age: ::Region::reborrow(item.age), - hobbies: as Containerized>::Region::reborrow(item.hobbies), - } - } - } - - impl Push<&Person> for PersonRegion { - fn push(&mut self, item: &Person) -> ::Index { - let name = self.name_container.push(&item.name); - let age = self.age_container.push(item.age); - let hobbies = self.hobbies.push(&item.hobbies); - (name, age, hobbies) - } - } - - impl<'a> ReserveItems<&'a Person> for PersonRegion { - fn reserve_items(&mut self, items: I) - where - I: Iterator + Clone, - { - self.name_container - .reserve_items(items.clone().map(|i| &i.name)); - self.age_container - .reserve_items(items.clone().map(|i| &i.age)); - self.hobbies.reserve_items(items.map(|i| &i.hobbies)); - } - } - - impl Push> for PersonRegion { - fn push(&mut self, item: PersonRef<'_>) -> ::Index { - let name = self.name_container.push(item.name); - let age = self.age_container.push(item.age); - let hobbies = self.hobbies.push(item.hobbies); - (name, age, hobbies) - } - } - - #[test] - fn test_person() { - let hobbies = ["Computers", "Guitar"]; - let p = Person { - name: "Moritz".to_string(), - age: 123, - hobbies: hobbies.iter().map(ToString::to_string).collect(), - }; - - let mut c = FlatStack::default_impl::(); - c.copy(&p); - let person_ref = c.get(0); - assert_eq!("Moritz", person_ref.name); - assert_eq!(123, person_ref.age); - assert_eq!(2, person_ref.hobbies.len()); - for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { - assert_eq!(copied_hobby, hobby); - } - - let mut cc = FlatStack::default_impl::(); - - cc.copy(c.get(0)); - - let person_ref = cc.get(0); - assert_eq!("Moritz", person_ref.name); - assert_eq!(123, person_ref.age); - assert_eq!(2, person_ref.hobbies.len()); - for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { - assert_eq!(copied_hobby, hobby); - } - } - #[test] fn test_result() { let r: Result<_, u16> = Ok("abc"); @@ -809,154 +625,4 @@ mod tests { // let _ = item == IntoOwned::borrow_as(owned); let _ = R::reborrow(item) == R::reborrow(IntoOwned::borrow_as(owned)); } - - mod cow { - //! What follows is an example of a Cow-like type that can be used to switch between a GAT - //! and an owned type at runtime. - - use crate::{FlatStack, IntoOwned, Push, Region, StringRegion}; - use std::convert::Infallible; - use std::fmt::{Debug, Formatter}; - - #[allow(dead_code)] - enum GatCow<'a, B, T> { - Borrowed(B), - Owned(T), - Never(&'a Infallible), - } - - impl<'a, B, T> GatCow<'a, B, T> - where - B: IntoOwned<'a, Owned = T> + Copy, - { - pub fn to_mut(&mut self) -> &mut T { - match self { - Self::Borrowed(borrowed) => { - *self = Self::Owned(borrowed.into_owned()); - match *self { - Self::Borrowed(..) => unreachable!(), - Self::Owned(ref mut owned) => owned, - Self::Never(_) => unreachable!(), - } - } - Self::Owned(ref mut owned) => owned, - Self::Never(_) => unreachable!(), - } - } - } - - impl<'a, B, T> IntoOwned<'a> for GatCow<'a, B, T> - where - B: IntoOwned<'a, Owned = T> + Copy, - { - type Owned = T; - - fn into_owned(self) -> T { - match self { - GatCow::Borrowed(b) => b.into_owned(), - GatCow::Owned(o) => o, - Self::Never(_) => unreachable!(), - } - } - - fn clone_onto(self, other: &mut T) { - match self { - GatCow::Borrowed(b) => b.clone_onto(other), - GatCow::Owned(o) => *other = o, - Self::Never(_) => unreachable!(), - } - } - - fn borrow_as(owned: &'a T) -> Self { - GatCow::Borrowed(IntoOwned::borrow_as(owned)) - } - } - - impl Debug for GatCow<'_, B, T> - where - B: Debug, - T: Debug, - { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GatCow::Borrowed(b) => b.fmt(f), - GatCow::Owned(o) => o.fmt(f), - Self::Never(_) => unreachable!(), - } - } - } - - #[derive(Default, Debug, Clone)] - struct CowRegion(R); - - impl Region for CowRegion - where - R: Region, - for<'a> R::ReadItem<'a>: Copy, - { - type Owned = ::Owned; - type ReadItem<'a> = GatCow<'a, R::ReadItem<'a>, R::Owned> where Self: 'a; - type Index = R::Index; - - fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self - where - Self: 'a, - { - Self(R::merge_regions(regions.map(|r| &r.0))) - } - - fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { - GatCow::Borrowed(self.0.index(index)) - } - - fn reserve_regions<'a, I>(&mut self, regions: I) - where - Self: 'a, - I: Iterator + Clone, - { - self.0.reserve_regions(regions.map(|r| &r.0)) - } - - fn clear(&mut self) { - self.0.clear() - } - - fn heap_size(&self, callback: F) { - self.0.heap_size(callback) - } - - fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> - where - Self: 'a, - { - match item { - GatCow::Borrowed(b) => GatCow::Borrowed(R::reborrow(b)), - GatCow::Owned(o) => GatCow::Owned(o), - GatCow::Never(_) => unreachable!(), - } - } - } - - impl Push for CowRegion - where - R: Region + Push, - for<'a> R::ReadItem<'a>: Copy, - { - fn push(&mut self, item: D) -> Self::Index { - self.0.push(item) - } - } - - #[test] - fn test_gat_cow() { - let mut c = >>::default(); - c.copy("abc"); - - assert_eq!("abc", c.get(0).into_owned()); - let mut item = c.get(0); - item.to_mut().push_str("def"); - assert_eq!("abcdef", item.into_owned()); - assert_eq!("abc", c.get(0).into_owned()); - } - } } diff --git a/tests/cow.rs b/tests/cow.rs new file mode 100644 index 0000000..b0118aa --- /dev/null +++ b/tests/cow.rs @@ -0,0 +1,147 @@ +//! What follows is an example of a Cow-like type that can be used to switch between a GAT +//! and an owned type at runtime. + +use flatcontainer::{FlatStack, IntoOwned, Push, Region, StringRegion}; +use std::convert::Infallible; +use std::fmt::{Debug, Formatter}; + +#[allow(dead_code)] +enum GatCow<'a, B, T> { + Borrowed(B), + Owned(T), + Never(&'a Infallible), +} + +impl<'a, B, T> GatCow<'a, B, T> +where + B: IntoOwned<'a, Owned = T> + Copy, +{ + pub fn to_mut(&mut self) -> &mut T { + match self { + Self::Borrowed(borrowed) => { + *self = Self::Owned(borrowed.into_owned()); + match *self { + Self::Borrowed(..) => unreachable!(), + Self::Owned(ref mut owned) => owned, + Self::Never(_) => unreachable!(), + } + } + Self::Owned(ref mut owned) => owned, + Self::Never(_) => unreachable!(), + } + } +} + +impl<'a, B, T> IntoOwned<'a> for GatCow<'a, B, T> +where + B: IntoOwned<'a, Owned = T> + Copy, +{ + type Owned = T; + + fn into_owned(self) -> T { + match self { + GatCow::Borrowed(b) => b.into_owned(), + GatCow::Owned(o) => o, + Self::Never(_) => unreachable!(), + } + } + + fn clone_onto(self, other: &mut T) { + match self { + GatCow::Borrowed(b) => b.clone_onto(other), + GatCow::Owned(o) => *other = o, + Self::Never(_) => unreachable!(), + } + } + + fn borrow_as(owned: &'a T) -> Self { + GatCow::Borrowed(IntoOwned::borrow_as(owned)) + } +} + +impl Debug for GatCow<'_, B, T> +where + B: Debug, + T: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + GatCow::Borrowed(b) => b.fmt(f), + GatCow::Owned(o) => o.fmt(f), + Self::Never(_) => unreachable!(), + } + } +} + +#[derive(Default, Debug, Clone)] +struct CowRegion(R); + +impl Region for CowRegion +where + R: Region, + for<'a> R::ReadItem<'a>: Copy, +{ + type Owned = ::Owned; + type ReadItem<'a> = GatCow<'a, R::ReadItem<'a>, R::Owned> where Self: 'a; + type Index = R::Index; + + fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self + where + Self: 'a, + { + Self(R::merge_regions(regions.map(|r| &r.0))) + } + + fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { + GatCow::Borrowed(self.0.index(index)) + } + + fn reserve_regions<'a, I>(&mut self, regions: I) + where + Self: 'a, + I: Iterator + Clone, + { + self.0.reserve_regions(regions.map(|r| &r.0)) + } + + fn clear(&mut self) { + self.0.clear() + } + + fn heap_size(&self, callback: F) { + self.0.heap_size(callback) + } + + fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> + where + Self: 'a, + { + match item { + GatCow::Borrowed(b) => GatCow::Borrowed(R::reborrow(b)), + GatCow::Owned(o) => GatCow::Owned(o), + GatCow::Never(_) => unreachable!(), + } + } +} + +impl Push for CowRegion +where + R: Region + Push, + for<'a> R::ReadItem<'a>: Copy, +{ + fn push(&mut self, item: D) -> Self::Index { + self.0.push(item) + } +} + +#[test] +fn test_gat_cow() { + let mut c = >>::default(); + c.copy("abc"); + + assert_eq!("abc", c.get(0).into_owned()); + let mut item = c.get(0); + item.to_mut().push_str("def"); + assert_eq!("abcdef", item.into_owned()); + assert_eq!("abc", c.get(0).into_owned()); +} diff --git a/tests/person.rs b/tests/person.rs new file mode 100644 index 0000000..514217e --- /dev/null +++ b/tests/person.rs @@ -0,0 +1,187 @@ +//! Test a slightly more struct with nested regions, representing people. + +use flatcontainer::{Containerized, FlatStack, IntoOwned, Push, Region, ReserveItems}; + +struct Person { + name: String, + age: u16, + hobbies: Vec, +} + +impl Containerized for Person { + type Region = PersonRegion; +} + +#[derive(Default)] +struct PersonRegion { + name_container: ::Region, + age_container: ::Region, + hobbies: as Containerized>::Region, +} + +#[derive(Debug, Clone, Copy)] +struct PersonRef<'a> { + name: <::Region as Region>::ReadItem<'a>, + age: <::Region as Region>::ReadItem<'a>, + hobbies: < as Containerized>::Region as Region>::ReadItem<'a>, +} + +impl<'a> IntoOwned<'a> for PersonRef<'a> { + type Owned = Person; + + fn into_owned(self) -> Self::Owned { + Person { + name: self.name.into_owned(), + age: self.age, + hobbies: self.hobbies.into_owned(), + } + } + + fn clone_onto(self, other: &mut Self::Owned) { + self.name.clone_onto(&mut other.name); + other.age = self.age; + self.hobbies.clone_onto(&mut other.hobbies); + } + + fn borrow_as(owned: &'a Self::Owned) -> Self { + Self { + name: IntoOwned::borrow_as(&owned.name), + age: owned.age, + hobbies: IntoOwned::borrow_as(&owned.hobbies), + } + } +} + +impl Region for PersonRegion { + type Owned = Person; + type ReadItem<'a> = PersonRef<'a> where Self: 'a; + type Index = ( + <::Region as Region>::Index, + <::Region as Region>::Index, + < as Containerized>::Region as Region>::Index, + ); + + fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self + where + Self: 'a, + { + Self { + name_container: ::Region::merge_regions( + regions.clone().map(|r| &r.name_container), + ), + age_container: ::Region::merge_regions( + regions.clone().map(|r| &r.age_container), + ), + hobbies: as Containerized>::Region::merge_regions( + regions.map(|r| &r.hobbies), + ), + } + } + + fn index(&self, (name, age, hobbies): Self::Index) -> Self::ReadItem<'_> { + PersonRef { + name: self.name_container.index(name), + age: self.age_container.index(age), + hobbies: self.hobbies.index(hobbies), + } + } + + fn reserve_regions<'a, I>(&mut self, regions: I) + where + Self: 'a, + I: Iterator + Clone, + { + self.name_container + .reserve_regions(regions.clone().map(|r| &r.name_container)); + self.age_container + .reserve_regions(regions.clone().map(|r| &r.age_container)); + self.hobbies + .reserve_regions(regions.clone().map(|r| &r.hobbies)); + } + + fn clear(&mut self) { + self.name_container.clear(); + self.age_container.clear(); + self.hobbies.clear(); + } + + fn heap_size(&self, mut callback: F) { + self.name_container.heap_size(&mut callback); + self.age_container.heap_size(&mut callback); + self.hobbies.heap_size(callback); + } + + fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> + where + Self: 'a, + { + PersonRef { + name: ::Region::reborrow(item.name), + age: ::Region::reborrow(item.age), + hobbies: as Containerized>::Region::reborrow(item.hobbies), + } + } +} + +impl Push<&Person> for PersonRegion { + fn push(&mut self, item: &Person) -> ::Index { + let name = self.name_container.push(&item.name); + let age = self.age_container.push(item.age); + let hobbies = self.hobbies.push(&item.hobbies); + (name, age, hobbies) + } +} + +impl<'a> ReserveItems<&'a Person> for PersonRegion { + fn reserve_items(&mut self, items: I) + where + I: Iterator + Clone, + { + self.name_container + .reserve_items(items.clone().map(|i| &i.name)); + self.age_container + .reserve_items(items.clone().map(|i| &i.age)); + self.hobbies.reserve_items(items.map(|i| &i.hobbies)); + } +} + +impl Push> for PersonRegion { + fn push(&mut self, item: PersonRef<'_>) -> ::Index { + let name = self.name_container.push(item.name); + let age = self.age_container.push(item.age); + let hobbies = self.hobbies.push(item.hobbies); + (name, age, hobbies) + } +} + +#[test] +fn test_person() { + let hobbies = ["Computers", "Guitar"]; + let p = Person { + name: "Moritz".to_string(), + age: 123, + hobbies: hobbies.iter().map(ToString::to_string).collect(), + }; + + let mut c = FlatStack::default_impl::(); + c.copy(&p); + let person_ref = c.get(0); + assert_eq!("Moritz", person_ref.name); + assert_eq!(123, person_ref.age); + assert_eq!(2, person_ref.hobbies.len()); + for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { + assert_eq!(copied_hobby, hobby); + } + + let mut cc = FlatStack::default_impl::(); + + cc.copy(c.get(0)); + + let person_ref = cc.get(0); + assert_eq!("Moritz", person_ref.name); + assert_eq!(123, person_ref.age); + assert_eq!(2, person_ref.hobbies.len()); + for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { + assert_eq!(copied_hobby, hobby); + } +} diff --git a/tests/recursive.rs b/tests/recursive.rs new file mode 100644 index 0000000..76f783f --- /dev/null +++ b/tests/recursive.rs @@ -0,0 +1,152 @@ +//! Demonstration of how to encode recursive data structures. + +use flatcontainer::impls::deduplicate::ConsecutiveOffsetPairs; +use flatcontainer::impls::offsets::OffsetContainer; +use flatcontainer::{IntoOwned, Push, Region, StringRegion}; + +#[derive(Clone)] +struct List(T, Option>>); + +struct ListRef<'a, C: Region>( + Result<(&'a ListRegion, ::Index, Option), &'a List>, +); + +impl<'a, C: Region> ListRef<'a, C> +where + C::Owned: Clone, +{ + fn inner(&self) -> C::ReadItem<'_> { + match &self.0 { + Ok((region, index, _continuation)) => region.inner.index(*index), + Err(list) => IntoOwned::borrow_as(&list.0), + } + } + + fn next(&self) -> Option { + match &self.0 { + Ok((region, _index, continuation)) => continuation.map(|index| region.index(index)), + Err(list) => list + .1 + .as_ref() + .map(|next| Self(Err(IntoOwned::borrow_as(next.as_ref())))), + } + } +} + +impl<'a, C: Region> IntoOwned<'a> for ListRef<'a, C> +where + C::Owned: Clone, +{ + type Owned = List; + + fn into_owned(self) -> Self::Owned { + List( + self.inner().into_owned(), + self.next().map(|next| Box::new(next.into_owned())), + ) + } + + fn clone_onto(self, other: &mut Self::Owned) { + *other = self.into_owned(); + } + + fn borrow_as(owned: &'a Self::Owned) -> Self { + Self(Err(owned)) + } +} + +#[derive(Debug)] +struct ListRegion { + indexes: Vec<(C::Index, Option)>, + inner: C, +} + +impl Default for ListRegion { + fn default() -> Self { + Self { + indexes: Vec::default(), + inner: C::default(), + } + } +} + +impl Region for ListRegion +where + C::Owned: Clone, +{ + type Owned = List; + type ReadItem<'a> = ListRef<'a, C> where C: 'a; + type Index = usize; + + fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { + let (inner_index, continuation) = self.indexes[index]; + ListRef(Ok((self, inner_index, continuation))) + } + + fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self + where + Self: 'a, + { + Self { + indexes: Vec::with_capacity(regions.clone().map(|r| r.indexes.len()).sum()), + inner: C::merge_regions(regions.map(|r| &r.inner)), + } + } + + fn reserve_regions<'a, I>(&mut self, regions: I) + where + Self: 'a, + I: Iterator + Clone, + { + self.indexes + .reserve(regions.clone().map(|r| r.indexes.len()).sum()); + self.inner.reserve_regions(regions.map(|r| &r.inner)); + } + + fn clear(&mut self) { + self.indexes.clear(); + self.inner.clear(); + } + + fn heap_size(&self, mut callback: F) { + self.indexes.heap_size(&mut callback); + self.inner.heap_size(callback); + } + + fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> + where + Self: 'a, + { + item + } +} + +impl Push<&List> for ListRegion +where + for<'a> C: Region + Push<&'a T>, + C::Owned: Clone, +{ + fn push(&mut self, item: &List) -> as Region>::Index { + let inner_index = self.inner.push(&item.0); + let continuation = item.1.as_deref().map(|next| self.push(next)); + self.indexes.push((inner_index, continuation)); + self.indexes.len() - 1 + } +} + +#[test] +fn recursive() { + let mut region = >>::default(); + let r = List("abc", Some(Box::new(List("def", None)))); + let index = region.push(&r); + + let rref = region.index(index); + assert_eq!(rref.inner(), "abc"); + let next = rref.next(); + assert!(next.is_some()); + let next = next.unwrap(); + assert_eq!(next.inner(), "def"); + assert!(next.next().is_none()); + + println!("{region:?}"); +}