From 4ef6458e4eb9381f057b1c04483b5095e9df9a3c Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Wed, 21 Oct 2026 01:12:42 +0200 Subject: [PATCH] Fix integer overflow in TempoMap::bar_beat_at_tick for i64::MIN Found while testing crates/time/src/tempo.rs:271 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/time/src/tempo.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/time/src/tempo.rs b/crates/time/src/tempo.rs index 24a8f49a..6aaa50b8 100644 --- a/crates/time/src/tempo.rs +++ b/crates/time/src/tempo.rs @@ -268,9 +268,12 @@ impl TempoMap { let first = self.meter_at_tick(0); if tick < 0 { let tpb = first.ticks_per_bar(); - let bars_back = (-tick + tpb - 1) / tpb; - let bar_tick = -bars_back * tpb; - let within = tick - bar_tick; + let abs = tick.unsigned_abs(); + let tpb_u = tpb.unsigned_abs(); + let bars_back_u = abs / tpb_u + u64::from(!abs.is_multiple_of(tpb_u)); + let bars_back = i64::try_from(bars_back_u).unwrap_or(i64::MAX); + // Offset of `tick` inside its bar, without negating `i64::MIN`. + let within = ((tpb_u - abs % tpb_u) % tpb_u) as i64; let beat_t = first.ticks_per_beat(); return BarBeat { bar: 1 - bars_back, beat: within / beat_t + 1, tick: within % beat_t }; } @@ -341,6 +344,15 @@ mod tests { assert_eq!(m.bar_start_tick(0), -TICKS_PER_QUARTER * 4); } + #[test] + fn bar_beat_at_min_tick_does_not_overflow() { + let m = TempoMap::default(); + let bb = m.bar_beat_at_tick(i64::MIN); + assert!(bb.bar < 1); + assert!(bb.beat >= 1 && bb.tick >= 0); + assert_eq!(m.bar_beat_at_tick(-1).bar, 0); + } + #[test] fn tempo_change_is_integrated() { let mut m = TempoMap::default();