Skip to content

Commit

Permalink
Merge pull request #5 from antiguru/updates
Browse files Browse the repository at this point in the history
  • Loading branch information
antiguru authored Feb 8, 2024
2 parents 781433c + 0233b2b commit 949b886
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 9 deletions.
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<&str, u16>>();
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

Expand Down
11 changes: 9 additions & 2 deletions src/impls/mirror.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A region that copies its inputs.
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;

#[cfg(feature = "serde")]
Expand All @@ -23,16 +24,22 @@ 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<T>(PhantomData<*const T>);
pub struct MirrorRegion<T>(PhantomData<T>);

impl<T> Default for MirrorRegion<T> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<T> Debug for MirrorRegion<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "MirrorRegion<{}>", std::any::type_name::<T>())
}
}

impl<T: Index + CopyOnto<Self>> Region for MirrorRegion<T> {
type ReadItem<'a> = T where T: 'a;
type Index = T;
Expand Down
11 changes: 10 additions & 1 deletion src/impls/slice.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A region that stores slices.
use std::fmt::{Debug, Formatter};
use std::ops::Deref;

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -95,7 +96,6 @@ impl<C: Region> Default for SliceRegion<C> {
}

/// 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> {
Expand All @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions src/impls/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ impl<'a> Containerized for &'a str {
type Region = StringRegion;
}

impl CopyOnto<StringRegion> for String {
#[inline]
fn copy_onto(self, target: &mut StringRegion) -> <StringRegion as Region>::Index {
self.as_str().copy_onto(target)
}
}

impl CopyOnto<StringRegion> for &String {
#[inline]
fn copy_onto(self, target: &mut StringRegion) -> <StringRegion as Region>::Index {
Expand Down
25 changes: 23 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#![deny(missing_docs)]

use std::fmt::{Debug, Formatter};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -119,7 +121,7 @@ pub trait ReserveItems<R: Region> {
}

/// A container for indices into a region.
#[derive(Debug, Clone)]
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
Expand All @@ -144,6 +146,15 @@ impl<R: Region> Default for FlatStack<R> {
}
}

impl<R: Region> Debug for FlatStack<R>
where
for<'a> R::ReadItem<'a>: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}

impl<R: Region> FlatStack<R> {
/// Default implementation based on the preference of type `T`.
#[inline]
Expand Down Expand Up @@ -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::<Result<&str, u16>>();
c.copy(&r);
assert_eq!(r, c.get(0));
}

#[test]
fn test_slice_string_onto() {
let mut c = StringRegion::default();
Expand Down Expand Up @@ -349,7 +368,7 @@ mod tests {

impl<'a> CopyOnto<PersonRegion> for &'a Person {
fn copy_onto(self, target: &mut PersonRegion) -> <PersonRegion as Region>::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)
Expand Down Expand Up @@ -421,6 +440,8 @@ mod tests {
fn test_copy<T, C: Region + Clone>(t: T)
where
T: CopyOnto<C>,
// 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);
Expand Down

0 comments on commit 949b886

Please sign in to comment.