diff --git a/README.md b/README.md index 4b870e8..0313e69 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,49 @@ flatcontainer = "0.1" ## Example ```rust -use flatcontainer::{ResultContainer, CopyOnto}; +use flatcontainer::{FlatStack, CopyOnto}; fn main() { let r: Result<_, u16> = Ok("abc"); - let mut c = ResultContainer::default(); - let idx = r.copy_onto(&mut c); + let mut c = FlatStack::default_impl::>(); + let idx = c.copy(&r); assert_eq!(r, c.index(idx)); } ``` ## Details -TODO +`flatcontainer` is a library that provides abstractions and implementations for +flattening collections of nested data structures into dense allocations. At its +core, a `Region` trait describes how to represent data in memory, with the option +to extract a lifetimed representation of the original data, which can be different +from what the caller had initially. + +`flatcontainer` decouples the write-half of a container from its storage and read +interface to permit a multitude of types to be presented to the same region. For +example, a region containing string-like objects and only promising access to +`&str` can accept anything that looks like a string, i.e., `String`, `&str` and +references to said types. + +Regions permit data access through opaque indexes, which the caller is responsible +for memorizing. An index grants access to the region-specific data representation, +and although it might be inspected, it should not be minted or modified in any +way. As far as a region is concerned, an index is an opaque type with no particular +meaning attached to it, unless specified differently by the region. + +Regions roughly follow two patterns: Either they fan out to other regions, or they +behave as terminal nodes and explicitly contain storage. A `Result` region +dispatches to an ok and error region, and uses its index type to distinguish where +to look up a value. A region for slices has storage to remember slices of indexes +where the index can be used to look up the datum's representation in an inner +region. + +`flatcontainer` provides `FlatStack`, an exemplary implementation of how to +implement a collection of items that supports pushing additional elements, +and retrieval by offset of previously pushed elements. It can be used in many +places that simply want to use a flat data representation, but it leaves potential +for optimization behind. Specifically, indexes, although opaque, follow a +simple structure where a more efficient storage can save memory instead of blindly +writing down all values. #### License diff --git a/src/impls/mirror.rs b/src/impls/mirror.rs index ecd16ab..32b0c9a 100644 --- a/src/impls/mirror.rs +++ b/src/impls/mirror.rs @@ -1,5 +1,6 @@ //! A region that copies its inputs. +use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; #[cfg(feature = "serde")] @@ -23,9 +24,9 @@ use crate::{Containerized, CopyOnto, Index, Region, ReserveItems}; /// let output: u8 = r.index(42); /// assert_eq!(output, 42); /// ``` -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MirrorRegion(PhantomData<*const T>); +pub struct MirrorRegion(PhantomData); impl Default for MirrorRegion { fn default() -> Self { @@ -33,6 +34,12 @@ impl Default for MirrorRegion { } } +impl Debug for MirrorRegion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "MirrorRegion<{}>", std::any::type_name::()) + } +} + impl> Region for MirrorRegion { type ReadItem<'a> = T where T: 'a; type Index = T; diff --git a/src/impls/slice.rs b/src/impls/slice.rs index cbf3e8e..3da7af1 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,5 +1,6 @@ //! A region that stores slices. +use std::fmt::{Debug, Formatter}; use std::ops::Deref; #[cfg(feature = "serde")] @@ -95,7 +96,6 @@ impl Default for SliceRegion { } /// A helper to read data out of a slice region. -#[derive(Debug)] pub struct ReadSlice<'a, C: Region>(pub &'a C, pub &'a [C::Index]); impl<'a, C: Region> ReadSlice<'a, C> { @@ -121,6 +121,15 @@ impl<'a, C: Region> ReadSlice<'a, C> { } } +impl<'a, C: Region> Debug for ReadSlice<'a, C> +where + C::ReadItem<'a>: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl<'a, C: Region> Clone for ReadSlice<'a, C> { #[inline] fn clone(&self) -> Self { diff --git a/src/impls/string.rs b/src/impls/string.rs index 8b61982..9069e66 100644 --- a/src/impls/string.rs +++ b/src/impls/string.rs @@ -61,6 +61,13 @@ impl<'a> Containerized for &'a str { type Region = StringRegion; } +impl CopyOnto for String { + #[inline] + fn copy_onto(self, target: &mut StringRegion) -> ::Index { + self.as_str().copy_onto(target) + } +} + impl CopyOnto for &String { #[inline] fn copy_onto(self, target: &mut StringRegion) -> ::Index { diff --git a/src/lib.rs b/src/lib.rs index e8db03f..5c5ee07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ #![deny(missing_docs)] +use std::fmt::{Debug, Formatter}; + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -119,7 +121,7 @@ pub trait ReserveItems { } /// A container for indices into a region. -#[derive(Debug, Clone)] +#[derive(Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde", @@ -144,6 +146,15 @@ impl Default for FlatStack { } } +impl Debug for FlatStack +where + for<'a> R::ReadItem<'a>: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl FlatStack { /// Default implementation based on the preference of type `T`. #[inline] @@ -253,6 +264,14 @@ impl<'a, R: Region> ExactSizeIterator for Iter<'a, R> {} mod tests { use super::*; + #[test] + fn test_readme() { + let r: Result<_, u16> = Ok("abc"); + let mut c = FlatStack::default_impl::>(); + c.copy(&r); + assert_eq!(r, c.get(0)); + } + #[test] fn test_slice_string_onto() { let mut c = StringRegion::default(); @@ -349,7 +368,7 @@ mod tests { impl<'a> CopyOnto for &'a Person { fn copy_onto(self, target: &mut PersonRegion) -> ::Index { - let name = self.name.copy_onto(&mut target.name_container); + let name = (&self.name).copy_onto(&mut target.name_container); let age = self.age.copy_onto(&mut target.age_container); let hobbies = (&self.hobbies).copy_onto(&mut target.hobbies); (name, age, hobbies) @@ -421,6 +440,8 @@ mod tests { fn test_copy(t: T) where T: CopyOnto, + // Make sure that types are debug, even if we don't use this in the test. + for<'a> C::ReadItem<'a>: Debug, { let mut c = FlatStack::default(); c.copy(t);