in bistro/cron/StandardCrontabItem.cpp [337:386]
void StandardCrontabItem::findMatchingPositionsImpl(
MatchComputeState *s, int64_t carry_steps
) const {
s->increaseDepth();
const auto& sel = selectors_[(int)s->pos_];
int64_t val = s->getVal(s->pos_);
if (val < sel.getMin() || val > sel.getMax()) {
throw logic_error(format(
"{} not in [{}, {}]", val, sel.getMin(), sel.getMax()
).str());
}
int64_t new_val;
bool carry_next;
tie(new_val, carry_next) = sel.findFirstMatch(val + carry_steps);
if (
!carry_next && s->pos_ == Position::DAY_OF_MONTH &&
new_val > s->getEndOfMonthDay()
) {
carry_next = true; // findFirstMatch may return 30 in February
}
if (carry_next) {
if (s->pos_ == Position::YEAR) {
// We exceeded the final year in matched by this crontab item,
// so we won't be able to find a match.
s->hasResult_ = false;
return;
}
s->pos_ = prevPos(s->pos_);
return findMatchingPositions(s, true);
}
// We know we didn't wrap around (carry_next is false here), so the value
// can't go down (and goes up if we had an input carry).
if (new_val < val || (carry_steps && new_val == val)) {
throw logic_error(
format("{} + {} went to {}", val, carry_steps, new_val).str()
);
}
s->setVal(s->pos_, new_val);
s->pos_ = nextPos(s->pos_);
if (s->pos_ == Position::MAX) {
return; // All done!
}
// If this position advanced, we should reset all later ones, like 199 => 200
if (new_val != val) {
for (auto p = s->pos_; p < Position::MAX; p = nextPos(p)) {
s->setVal(p, selectors_[(int)p].getMin());
}
}
return findMatchingPositions(s, false);
}