macro_rules! for_each_layout {
(all, $callback:ident $(, $extra:tt)*) => { ... };
(fixed, $callback:ident $(, $extra:tt)*) => { ... };
(ordered, $callback:ident $(, $extra:tt)*) => { ... };
(integer, $callback:ident $(, $extra:tt)*) => { ... };
(signed, $callback:ident $(, $extra:tt)*) => { ... };
(unsigned, $callback:ident $(, $extra:tt)*) => { ... };
(exact, $callback:ident $(, $extra:tt)*) => { ... };
(narrow, $callback:ident $(, $extra:tt)*) => { ... };
(float, $callback:ident $(, $extra:tt)*) => { ... };
}Expand description
Calls $callback with one group of physical layouts.
Each entry is (variant, element type, zero), where the variant is the
Data variant, the element type is what one value of it is, and the zero is the
value that fills a slot whose row is null. A caller that does not need all three ignores the
ones it does not need.
The callback is a macro the caller has already defined, almost always a macro_rules inside the
function that needs it, so that its body can refer to the function’s own locals. It is passed a
comma separated list of parenthesised triples and should match
$(($variant:ident, $native:ty, $zero:expr)),+ $(,)?.
Anything after the callback name is passed through ahead of the list, one token tree each, for
the callers that generate a loop per layout inside another loop per layout and need to hand the
inner one what the outer one bound. A caller taking one of those matches it first, as in
($values:expr, $(($variant:ident, $native:ty, $zero:expr)),+ $(,)?).
use rudb_vector::{Data, for_each_layout};
fn widest(data: &Data) -> Option<i128> {
macro_rules! biggest {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match data {
$(Data::$variant(values) => {
values.iter().copied().map(i128::from).max().or(Some($zero))
})+
_ => None,
}
};
}
for_each_layout!(narrow, biggest)
}
assert_eq!(widest(&Data::Int32(vec![3, 9, 4].into())), Some(9));
assert_eq!(widest(&Data::Float64(vec![3.0].into())), None);§The groups
all, every layout that holds values.Data::Emptyis not in it, because it holds none and has nothing for a loop to read.fixed, every layout whose run is aBufferof aCopyelement. These are the ones where a null slot can be filled with a zero and a gather is a copy of fixed width slots rather than a copy of bytes.ordered, the layouts whose SQL order is the derived order of the element type. A float is not in it, becauseNaNorders where SQL says rather than where the hardware says, and a string is not in it, because its order is over the bytes a view points at.integer, the ten integer widths.signedandunsigned, the five of each thatintegeris made of. Several kernels want one half and not the other, negation being the clearest, since negating an unsigned value is an overflow at every row but zero and a loop for it would be a loop that exists to fail.exact, the integers whose every value fits in ani128, so a kernel can widen the lot into one accumulator type.UInt128is the one that does not.narrow, the integers narrower than 128 bits. Two things follow from that and both of them are used. The total of a whole vector of them still fits in ani128, which is what lets the only overflow check in a sum be the one at the vector boundary rather than one per row, and a run ofi128narrows into any of them, which is the shape the cast path works in.float, the two IEEE widths.