From e1e15cf3abc00a91b813d06a10825cc2f8d185d1 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:53:51 +0200 Subject: [PATCH 1/2] Compute timecode frame counts in i128 and clamp (#248) Timecode::to_frames multiplied caller-supplied hours in i64 without a check, so "9223372036854775807:00:00:00" panicked under overflow checks and wrapped without them. The count is now computed in i128 and clamped to i64, and samples_at_frame clamps instead of returning 0 on overflow, so huge labels saturate toward the right end of the timeline. --- crates/time/src/timecode.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/time/src/timecode.rs b/crates/time/src/timecode.rs index c0db20f3..6b3b0f3c 100644 --- a/crates/time/src/timecode.rs +++ b/crates/time/src/timecode.rs @@ -109,7 +109,7 @@ impl FrameRate { return 0; } let v = (i128::from(f) * i128::from(sr.hz()) * i128::from(d) + i128::from(n) / 2) / i128::from(n); - i64::try_from(v).unwrap_or(0) + i64::try_from(v).unwrap_or(if v < 0 { i64::MIN } else { i64::MAX }) } /// Samples per frame (fractional). @@ -153,13 +153,14 @@ impl Timecode { /// Absolute frame count of this label. pub fn to_frames(&self, rate: FrameRate) -> i64 { let base = rate.nominal().max(1); - let total_min = self.hours * 60 + self.minutes; - let mut f = (total_min * 60 + self.seconds) * base + self.frames; + let total_min = i128::from(self.hours) * 60 + i128::from(self.minutes); + let mut f = (total_min * 60 + i128::from(self.seconds)) * i128::from(base) + i128::from(self.frames); if rate.is_drop() { let drop = if base >= 60 { 4 } else { 2 }; f -= drop * (total_min - total_min / 10); } - if self.negative { -f } else { f } + let f = if self.negative { -f } else { f }; + i64::try_from(f).unwrap_or(if f < 0 { i64::MIN } else { i64::MAX }) } pub fn from_samples(s: Samples, rate: FrameRate, sr: SampleRate) -> Timecode { @@ -233,6 +234,19 @@ mod tests { } } + #[test] + fn huge_timecode_saturates_instead_of_overflowing() { + let sr = SampleRate::HZ_48000; + for r in [FrameRate::Fps30, FrameRate::Fps29_97Drop] { + let tc = Timecode::parse("9223372036854775807:00:00:00").unwrap(); + assert_eq!(tc.to_samples(r, sr), i64::MAX); + let tc = Timecode::parse("-9223372036854775807:00:00:00").unwrap(); + assert_eq!(tc.to_samples(r, sr), i64::MIN); + } + let tc = Timecode::parse("01:02:03:04").unwrap(); + assert_eq!(tc.to_samples(FrameRate::Fps30, sr), 178_710_400); + } + #[test] fn parse_variants() { let tc = Timecode::parse("01:02:03:04").unwrap(); From ec41f804ff13cd73252af2217a576c4fb4998023 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:26:37 +0200 Subject: [PATCH 2/2] Docs: update the progress docs for this change --- ROADMAP.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ROADMAP.md b/ROADMAP.md index 25ae055f..dd79f695 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -122,6 +122,7 @@ mutated and truncated movies. Field reports from the first public week are track ## Progress log +- **2026-10-12:** Timecode labels with huge fields no longer overflow: frame counts are computed in 128 bits and clamp, so `setup.current_timecode` and `session.format_time` saturate instead of failing (#248) - **2026-10-10 (UI docking):** Tracks and Groups, Clips, MIDI Editor, Memory Locations, and Undo History use movable panels. Edit and Mix retain separate arrangements; Clips and MIDI Editor belong to Edit. Transport, plug-in, and specialist utility windows retain their existing hosts. Panel moves and reset are available as UI commands, and saved UI preferences retain arrangements. Common toolbar and track record-enable buttons share interaction, accessible labels and keyboard focus through `craft-ui`, retaining blue tool selection and red record-arm states. No readiness percentage change. - **2026-10-10** — Stage re-normalized to **pre-alpha** by the core-workflow gate (interface recording, MIDI recording and session interchange fail). Full re-measure against Pro Tools Ultimate 2026.4.1 and the standard progress-docs set (this page, target-app parity, gaps, roadmap, UI/format/hardware/plugin/ @@ -143,6 +144,7 @@ mutated and truncated movies. Field reports from the first public week are track | Date | Change | Summary | |---|---|---| +| 2026-10-12 | trivial | Progress log: timecode overflow (#248) | | 2026-10-10 | minor | Readiness-by-audience table with hours: full 950–1,450 h, mainstream 380–600 h, essentials 110–200 h | | 2026-10-10 | minor | Added mainstream practitioner ~36 % and essentials user ~57 % to the headline numbers | | 2026-10-10 | minor | Stage alpha → pre-alpha: the core-workflow gate fails on interface recording, MIDI recording and session interchange; alpha distance added |