tamer: fmt (ListDisplayWrapper::fmt_nth): List display without a slice
This exposes the internal rendering of `ListDisplayWrapper::fmt` such that we can output a list without actually creating a list. This is used in an upcoming change for =ele_parse!= so that Sum NTs can render the union of all the QNames that their constituent NTs match on, recursively, as a single list, without having to create an ephemeral collection only for display. If Rust supports const functions for arrays/Vecs in the future, we could generate this at compile-time, if we were okay with the (small) cost, but this solution seems just fine. But output may be even _more_ performant since they'd all be adjacent in memory. This is used in these secenarios: 1. Diagnostic messages; 2. Error messages (overlaps with #1); and 3. `Display::fmt` of the `ParseState`s themselves. The reason that we want this to be reasonably performant is because #3 results in a _lot_ of output---easily GiB of output depending on what is being traced. Adding heap allocations to this would make it even slower, since a description is generated for each individual trace. Anyway, this is a fairly simple solution, albeit a little bit less clear, and only came after I had tried a number of other different approaches related to recursively constructing QName lists at compile time; they weren't worth the effort when this was so easy to do. DEV-7145main
parent
6b29479fd6
commit
fd3184c795
|
@ -221,32 +221,61 @@ pub trait ListDisplayWrapper {
|
|||
/// [`ListDisplayWrapper::wrap`] may be used to produce a
|
||||
/// [`Display`]-able object instead.
|
||||
fn fmt<T: Display>(list: &[T], f: &mut Formatter) -> Result {
|
||||
let maxi = list.len().saturating_sub(1);
|
||||
let lasti = list.len().saturating_sub(1);
|
||||
|
||||
// This can be further abstracted away using the above primitives,
|
||||
// if ever we have a use.
|
||||
for next in list.into_iter().enumerate() {
|
||||
match next {
|
||||
(0, x) if maxi == 0 => {
|
||||
Self::Single::fmt(x, f)?;
|
||||
}
|
||||
(i, x) => Self::fmt_nth(lasti, i, x, f)?,
|
||||
};
|
||||
}
|
||||
|
||||
(0, x) => {
|
||||
Self::First::fmt(x, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
(i, x) if maxi == i => {
|
||||
if i == 1 {
|
||||
Self::LastOfPair::fmt(x, f)?;
|
||||
} else {
|
||||
Self::LastOfMany::fmt(x, f)?;
|
||||
}
|
||||
}
|
||||
/// Format an item as if it were the `i`th value of a list of length
|
||||
/// `lasti+1`.
|
||||
///
|
||||
/// This allows for generating list-like output without the expense of
|
||||
/// actually producing a list.
|
||||
/// This may be useful when values are stored in different memory
|
||||
/// location,
|
||||
/// so that the displaying of those values is a problem of invoking
|
||||
/// this method on them in the right order,
|
||||
/// rather than collecting them just for the sake of display.
|
||||
/// If Rust supports `const` array/Vec functions in the future,
|
||||
/// this may not be necessary anymore,
|
||||
/// unless we also don't want the space cost of such a
|
||||
/// precomputation
|
||||
/// (but it may come with performance benefits from locality).
|
||||
#[inline]
|
||||
fn fmt_nth<T: Display>(
|
||||
lasti: usize,
|
||||
i: usize,
|
||||
item: &T,
|
||||
f: &mut Formatter,
|
||||
) -> Result {
|
||||
match (i, item) {
|
||||
(0, x) if lasti == 0 => {
|
||||
Self::Single::fmt(x, f)?;
|
||||
}
|
||||
|
||||
(_, x) => {
|
||||
Self::Middle::fmt(x, f)?;
|
||||
(0, x) => {
|
||||
Self::First::fmt(x, f)?;
|
||||
}
|
||||
|
||||
(i, x) if lasti == i => {
|
||||
if i == 1 {
|
||||
Self::LastOfPair::fmt(x, f)?;
|
||||
} else {
|
||||
Self::LastOfMany::fmt(x, f)?;
|
||||
}
|
||||
}
|
||||
|
||||
(_, x) => {
|
||||
Self::Middle::fmt(x, f)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -547,4 +576,42 @@ mod test {
|
|||
"test fmt",
|
||||
);
|
||||
}
|
||||
|
||||
// `fmt_nth` is used by the above tests,
|
||||
// but that's an implementation detail;
|
||||
// we expose it as a public API so it ought to be tested too.
|
||||
#[test]
|
||||
fn fmt_nth() {
|
||||
type Sut = QualConjList<"thing", "things", "or", Raw>;
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(0, 0, &"foo", f)).to_string(),
|
||||
"thing foo",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(1, 0, &"foo", f)).to_string(),
|
||||
"things foo",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(1, 1, &"foo", f)).to_string(),
|
||||
" or foo",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(2, 0, &"foo", f)).to_string(),
|
||||
"things foo",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(2, 1, &"foo", f)).to_string(),
|
||||
", foo",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DisplayFn(|f| Sut::fmt_nth(2, 2, &"foo", f)).to_string(),
|
||||
", or foo",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue