-
Notifications
You must be signed in to change notification settings - Fork 292
Rework container traits #686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0d03378
Progress container with length
antiguru be571f3
Back out push_into change
antiguru ba7abf7
Rename ProgressContainer to WithProgress
antiguru 0cdf9de
Rename Container to IterableContainer and cleanup
antiguru 3f78d6b
Remove WithProgress::is_empty
antiguru ef54810
Remove WithProgress::is_empty
antiguru 236c46b
Restore clear, cleanup
antiguru File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,46 +4,32 @@ | |
|
|
||
| use std::collections::VecDeque; | ||
|
|
||
| /// A container transferring data through dataflow edges | ||
| /// A type representing progress, with an update count. | ||
| /// | ||
| /// A container stores a number of elements and thus is able to describe it length (`len()`) and | ||
| /// whether it is empty (`is_empty()`). It supports removing all elements (`clear`). | ||
| /// It describes its update count (`count()`). | ||
| /// | ||
| /// A container must implement default. The default implementation is not required to allocate | ||
| /// memory for variable-length components. | ||
| /// | ||
| /// We require the container to be cloneable to enable efficient copies when providing references | ||
| /// of containers to operators. Care must be taken that the type's `clone_from` implementation | ||
| /// is efficient (which is not necessarily the case when deriving `Clone`.) | ||
| /// We require [`Default`] for convenience purposes. | ||
| pub trait Container: Default { | ||
| /// The type of elements when reading non-destructively from the container. | ||
| type ItemRef<'a> where Self: 'a; | ||
|
|
||
| /// The type of elements when draining the container. | ||
| type Item<'a> where Self: 'a; | ||
|
|
||
| /// Push `item` into self | ||
| #[inline] | ||
| fn push<T>(&mut self, item: T) where Self: PushInto<T> { | ||
| self.push_into(item) | ||
| } | ||
|
Comment on lines
-27
to
-29
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| /// The number of elements in this container | ||
| /// | ||
| /// This number is used in progress tracking to confirm the receipt of some number | ||
| /// of outstanding records, and it is highly load bearing. The main restriction is | ||
| /// imposed on the `LengthPreservingContainerBuilder` trait, whose implementors | ||
| /// imposed on the [`LengthPreservingContainerBuilder`] trait, whose implementors | ||
| /// must preserve the number of items. | ||
| fn len(&self) -> usize; | ||
|
|
||
| /// Determine if the container contains any elements, corresponding to `len() == 0`. | ||
| fn is_empty(&self) -> bool { | ||
| self.len() == 0 | ||
| } | ||
| fn count(&self) -> usize; | ||
|
|
||
| /// Remove all contents from `self` while retaining allocated memory. | ||
| /// After calling `clear`, `is_empty` must return `true` and `len` 0. | ||
| fn clear(&mut self); | ||
| } | ||
|
|
||
| /// A container that can reveal its contents through iterating by reference and draining. | ||
| pub trait IterContainer: Container { | ||
| /// The type of elements when reading non-destructively from the container. | ||
| type ItemRef<'a> where Self: 'a; | ||
|
|
||
| /// The type of elements when draining the container. | ||
| type Item<'a> where Self: 'a; | ||
|
|
||
| /// Iterator type when reading from the container. | ||
| type Iter<'a>: Iterator<Item=Self::ItemRef<'a>> where Self: 'a; | ||
|
|
@@ -116,8 +102,9 @@ pub trait ContainerBuilder: Default + 'static { | |
| /// Partitions `container` among `builders`, using the function `index` to direct items. | ||
| fn partition<I>(container: &mut Self::Container, builders: &mut [Self], mut index: I) | ||
| where | ||
| Self: for<'a> PushInto<<Self::Container as Container>::Item<'a>>, | ||
| I: for<'a> FnMut(&<Self::Container as Container>::Item<'a>) -> usize, | ||
| Self: for<'a> PushInto<<Self::Container as IterContainer>::Item<'a>>, | ||
| I: for<'a> FnMut(&<Self::Container as IterContainer>::Item<'a>) -> usize, | ||
| Self::Container: IterContainer, | ||
| { | ||
| for datum in container.drain() { | ||
| let index = index(&datum); | ||
|
|
@@ -142,6 +129,33 @@ pub trait ContainerBuilder: Default + 'static { | |
| /// If you have any questions about this trait you are best off not implementing it. | ||
| pub trait LengthPreservingContainerBuilder : ContainerBuilder { } | ||
|
|
||
| /// A container builder that never produces any outputs, and can be used to pass through data in | ||
| /// operators. | ||
| #[derive(Debug, Clone)] | ||
| pub struct PassthroughContainerBuilder<C>(std::marker::PhantomData<C>); | ||
|
|
||
| impl<C> Default for PassthroughContainerBuilder<C> { | ||
| #[inline(always)] | ||
| fn default() -> Self { | ||
| PassthroughContainerBuilder(std::marker::PhantomData) | ||
| } | ||
| } | ||
|
|
||
| impl<C: Container + Clone + 'static> ContainerBuilder for PassthroughContainerBuilder<C> | ||
| { | ||
| type Container = C; | ||
|
|
||
| #[inline(always)] | ||
| fn extract(&mut self) -> Option<&mut Self::Container> { | ||
| None | ||
| } | ||
|
|
||
| #[inline(always)] | ||
| fn finish(&mut self) -> Option<&mut Self::Container> { | ||
| None | ||
| } | ||
| } | ||
|
|
||
| /// A default container builder that uses length and preferred capacity to chunk data. | ||
| /// | ||
| /// Maintains a single empty allocation between [`Self::push_into`] and [`Self::extract`], but not | ||
|
|
@@ -165,7 +179,7 @@ impl<T, C: SizableContainer + PushInto<T>> PushInto<T> for CapacityContainerBuil | |
| self.current.ensure_capacity(&mut self.empty); | ||
|
|
||
| // Push item | ||
| self.current.push(item); | ||
| self.current.push_into(item); | ||
antiguru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Maybe flush | ||
| if self.current.at_capacity() { | ||
|
|
@@ -189,30 +203,24 @@ impl<C: Container + Clone + 'static> ContainerBuilder for CapacityContainerBuild | |
|
|
||
| #[inline] | ||
| fn finish(&mut self) -> Option<&mut C> { | ||
| if !self.current.is_empty() { | ||
| if self.current.count() > 0 { | ||
| self.pending.push_back(std::mem::take(&mut self.current)); | ||
| } | ||
| self.empty = self.pending.pop_front(); | ||
| self.empty.as_mut() | ||
| } | ||
| } | ||
|
|
||
| impl<C: Container + Clone + 'static> LengthPreservingContainerBuilder for CapacityContainerBuilder<C> { } | ||
| impl<C: Container + SizableContainer + Clone + 'static> LengthPreservingContainerBuilder for CapacityContainerBuilder<C> { } | ||
|
|
||
| impl<T> Container for Vec<T> { | ||
| #[inline(always)] fn count(&self) -> usize { Vec::len(self) } | ||
| #[inline(always)] fn clear(&mut self) { Vec::clear(self) } | ||
| } | ||
|
|
||
| impl<T> IterContainer for Vec<T> { | ||
| type ItemRef<'a> = &'a T where T: 'a; | ||
| type Item<'a> = T where T: 'a; | ||
|
|
||
| fn len(&self) -> usize { | ||
| Vec::len(self) | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| Vec::is_empty(self) | ||
| } | ||
|
|
||
| fn clear(&mut self) { Vec::clear(self) } | ||
|
|
||
| type Iter<'a> = std::slice::Iter<'a, T> where Self: 'a; | ||
|
|
||
| fn iter(&self) -> Self::Iter<'_> { | ||
|
|
@@ -268,29 +276,24 @@ mod rc { | |
| use std::ops::Deref; | ||
| use std::rc::Rc; | ||
|
|
||
| use crate::Container; | ||
| use crate::{Container, IterContainer}; | ||
|
|
||
| impl<T: Container> Container for Rc<T> { | ||
| type ItemRef<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Item<'a> = T::ItemRef<'a> where Self: 'a; | ||
|
|
||
| fn len(&self) -> usize { | ||
| std::ops::Deref::deref(self).len() | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| std::ops::Deref::deref(self).is_empty() | ||
| } | ||
|
|
||
| fn clear(&mut self) { | ||
| #[inline(always)] fn count(&self) -> usize { self.as_ref().count() } | ||
| #[inline(always)] fn clear(&mut self) { | ||
| // Try to reuse the allocation if possible | ||
| if let Some(inner) = Rc::get_mut(self) { | ||
| inner.clear(); | ||
| } else { | ||
| *self = Self::default(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| impl<T: IterContainer> IterContainer for Rc<T> { | ||
| type ItemRef<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Item<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Iter<'a> = T::Iter<'a> where Self: 'a; | ||
|
|
||
| fn iter(&self) -> Self::Iter<'_> { | ||
|
|
@@ -309,29 +312,23 @@ mod arc { | |
| use std::ops::Deref; | ||
| use std::sync::Arc; | ||
|
|
||
| use crate::Container; | ||
|
|
||
| impl<T: Container> Container for Arc<T> { | ||
| type ItemRef<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Item<'a> = T::ItemRef<'a> where Self: 'a; | ||
|
|
||
| fn len(&self) -> usize { | ||
| std::ops::Deref::deref(self).len() | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| std::ops::Deref::deref(self).is_empty() | ||
| } | ||
| use crate::{Container, IterContainer}; | ||
|
|
||
| fn clear(&mut self) { | ||
| impl<T: Container> Container for std::sync::Arc<T> { | ||
| #[inline(always)] fn count(&self) -> usize { self.as_ref().count() } | ||
| #[inline(always)] fn clear(&mut self) { | ||
| // Try to reuse the allocation if possible | ||
| if let Some(inner) = Arc::get_mut(self) { | ||
| inner.clear(); | ||
| } else { | ||
| *self = Self::default(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<T: IterContainer> IterContainer for Arc<T> { | ||
| type ItemRef<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Item<'a> = T::ItemRef<'a> where Self: 'a; | ||
| type Iter<'a> = T::Iter<'a> where Self: 'a; | ||
|
|
||
| fn iter(&self) -> Self::Iter<'_> { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A thing I want to discuss, but am not sure I'll find a better place to comment:
My guess is we'd eventually like to close in on a representation that presents its progress tracking information as a
Map<T, usize>, allowing multiple counts for multiple times. This might be in that direction, I think it is, but also I'm trying to think through how this ends up looking and whether there are helpful steps to take as we go.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. We currently have the separation into
Messageand the data it's transferring, and theContainertrait in the past was just the data, mostly for historic reasons. It might make sense to make messages the interface to sending data, which would give more control about the associated timestamps to the users.