tamer: ir::asg::section: Improve iteration

`SectionsIter` was introduced to remove that responsibility from xmle
writer, since that's currently being reimplemented using XIR.

The existing iterator has been renamed SectionIter{ator=>} for a more
idiomatic name for iterator structs, and now has a static type rather than
relying on dynamic dispatch.  The author of that code wasn't sure how to
handle it otherwise.  (Which is understandable, since we were both still
getting acquainted with Rust.)  There's no notable change in performance in
my benchmarking.

This abstraction is a bit awkward, in that it's named for object file
sections, but they aren't.  Further, it's coupled with the ASG via
`SortableAsg` and perhaps should be generalized into a sorting routine that
takes a function for sorting, so that `Sections` can be moved into xmle's
packages.
main
Mike Gerwitz 2021-09-01 09:14:51 -04:00
parent b80064f59e
commit 1fa9614698
3 changed files with 111 additions and 57 deletions

View File

@ -205,7 +205,7 @@ pub use object::{
FragmentText, IdentObject, IdentObjectData, IdentObjectState, Source,
TransitionError, TransitionResult, UnresolvedError,
};
pub use section::{Section, SectionIterator, Sections};
pub use section::{Section, SectionIter, Sections};
/// Default concrete ASG implementation.
pub type DefaultAsg<O, Ix> = base::BaseAsg<O, Ix>;

View File

@ -17,6 +17,16 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Ordered sections of ASG object references.
//!
//! These sections are the result of an ordering operation from
//! [`SortableAsg::sort`].
//!
//! [`SortableAsg::sort`]: super::SortableAsg::sort
use std::iter::Chain;
use std::slice::Iter;
/// A section of an [object file](crate::obj).
///
/// Most sections will only need a `body`, but some innlude `head` and `tail`
@ -64,62 +74,39 @@ impl<'a, T> Section<'a, T> {
self.tail.push(obj)
}
/// Merge the parts of a `Section` into one [`SectionIterator`]
///
/// The `Section` internals need to be iterated as a group so we needed to
/// create a custom iterator, [`SectionIterator`] to do this for us. This
/// method allows us to access the iterator.
///
/// ```
/// use tamer::ir::asg::{IdentObject, Section};
/// use tamer::sym::{DefaultPkgInterner, Interner};
///
/// let interner = DefaultPkgInterner::new();
/// let mut section = Section::new();
/// let obj = IdentObject::Missing(interner.intern("ident"));
/// let expect = vec![&obj, &obj, &obj];
///
/// section.push_head(&obj);
/// section.push_body(&obj);
/// section.push_tail(&obj);
/// let section_iter = section.iter();
///
/// for object in section_iter {
/// assert_eq!(&obj, object);
/// }
/// ```
pub fn iter(&self) -> SectionIterator<T> {
SectionIterator {
inner: Box::new(
self.head
.iter()
.chain(self.body.iter())
.chain(self.tail.iter())
.copied(),
),
}
/// Construct a new iterator visiting each head, body, and tail object
/// in order.
pub fn iter(&self) -> SectionIter<T> {
SectionIter(
self.head
.iter()
.chain(self.body.iter())
.chain(self.tail.iter()),
)
}
}
/// Wrapper for an Iterator
/// Iterator over the head, body, and tail of a [`Section`].
///
/// This allows us to iterate over all parts of a [`Section`].
pub struct SectionIterator<'a, T> {
inner: Box<dyn Iterator<Item = &'a T> + 'a>,
}
/// This iterator should be created with [`Section::iter`].
///
/// This hides the complex iterator type from callers.
pub struct SectionIter<'a, T>(
Chain<Chain<Iter<'a, &'a T>, Iter<'a, &'a T>>, Iter<'a, &'a T>>,
);
impl<'a, T> Iterator for SectionIterator<'a, T> {
impl<'a, T> Iterator for SectionIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
self.0.next().map(|x| *x)
}
}
/// Sections that need to be written to a buffer
/// ASG objects organized into logical sections.
///
/// All the properties are public [`Section`] objects and will be accessed
/// directly by the the objects interacting with them.
/// These sections may not necessarily correspond directly to sections of an
/// [object file](crate::obj).
#[derive(Debug, Default, PartialEq)]
pub struct Sections<'a, T> {
pub map: Section<'a, T>,
@ -148,6 +135,56 @@ impl<'a, T> Sections<'a, T> {
rater: Section::new(),
}
}
/// Construct an iterator over each of the individual sections in
/// arbitrary order.
///
/// Each individual section is ordered as stated in [`Section::iter`],
/// but you should not rely on the order that the sections themselves
/// appear in;
/// they may change or be combined in the future.
/// At the time of writing,
/// they are chained in the same order in which they are defined
/// on the [`Sections`] struct.
pub fn iter_all(&self) -> SectionsIter<T> {
SectionsIter(
self.map
.iter()
.chain(self.retmap.iter())
.chain(self.meta.iter())
.chain(self.worksheet.iter())
.chain(self.params.iter())
.chain(self.types.iter())
.chain(self.funcs.iter())
.chain(self.consts.iter())
.chain(self.rater.iter()),
)
}
}
// Compose the chained iterator type for [`SectionsIter`].
// This could be further abstracted away,
// but it's likely that `Sections` will be simplified in the future.
type SIter<'a, T> = SectionIter<'a, T>;
type CSIter1<'a, T, L> = Chain<L, SIter<'a, T>>;
type CSIter2<'a, T, L> = CSIter1<'a, T, CSIter1<'a, T, L>>;
type CSIter4<'a, T, L> = CSIter2<'a, T, CSIter2<'a, T, L>>;
type CSIter8<'a, T, L> = CSIter4<'a, T, CSIter4<'a, T, L>>;
type SIter9<'a, T> = CSIter8<'a, T, SIter<'a, T>>;
/// Iterator over each of the sections.
///
/// This iterator should be created with [`Sections::iter_all`].
///
/// This hides the complex iterator type from callers.
pub struct SectionsIter<'a, T>(SIter9<'a, T>);
impl<'a, T> Iterator for SectionsIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[cfg(test)]
@ -262,4 +299,30 @@ mod test {
assert_eq!(expect, collection);
}
#[test]
fn sections_iter_all() {
let mut sections = Sections::new();
let objs = (0..=10)
.map(|i| IdentObject::<u16>::Missing(i.to_string().into()))
.collect::<Vec<_>>();
sections.map.head.push(&objs[0]);
sections.map.body.push(&objs[1]);
sections.map.tail.push(&objs[2]);
sections.retmap.body.push(&objs[3]);
sections.meta.body.push(&objs[4]);
sections.worksheet.body.push(&objs[5]);
sections.params.body.push(&objs[6]);
sections.types.body.push(&objs[7]);
sections.funcs.body.push(&objs[8]);
sections.consts.body.push(&objs[9]);
sections.rater.body.push(&objs[10]);
assert_eq!(
sections.iter_all().collect::<Vec<_>>(),
objs.iter().collect::<Vec<_>>()
);
}
}

View File

@ -20,7 +20,7 @@
use super::writer::{Result, WriterError};
use crate::global;
use crate::ir::asg::{
IdentKind, IdentObject, IdentObjectData, SectionIterator, Sections,
IdentKind, IdentObject, IdentObjectData, SectionIter, Sections,
};
use crate::sym::{GlobalSymbolResolve, ProgSymbolId, SymbolId};
use fxhash::FxHashSet;
@ -202,16 +202,7 @@ impl<W: Write> XmleWriter<W> {
sections: &Sections<T>,
relroot: &str,
) -> Result<&mut XmleWriter<W>> {
let all = sections
.meta
.iter()
.chain(sections.map.iter())
.chain(sections.retmap.iter())
.chain(sections.worksheet.iter())
.chain(sections.params.iter())
.chain(sections.types.iter())
.chain(sections.funcs.iter())
.chain(sections.rater.iter());
let all = sections.iter_all();
for obj in all {
let ident = obj
@ -352,7 +343,7 @@ impl<W: Write> XmleWriter<W> {
/// `writer`'s 'write_event`.
fn write_section<T: IdentObjectData<Ix>>(
&mut self,
idents: SectionIterator<T>,
idents: SectionIter<T>,
) -> Result<&mut XmleWriter<W>> {
for obj in idents {
let ident = obj