fix(A.3 #25): guard sample_index_for_seconds against NaN and infinity
Review feedback (MINOR): the original <= 0.0 clamp caught negatives and zero but not non-finite inputs. Rust's saturating float-to-int cast turns f64::INFINITY into u64::MAX, which would park the capture buffer origin beyond any reachable sample index and trim the whole buffer forever if a future end_secs source ever produces infinity (clock glitch, overflow upstream, corrupted timestamp in a pass). Adds is_finite() check. NaN, +infinity, -infinity, and zero all return 0, which downstream trim_buffer_to_commit_point treats as no-op. Test covers all three non-finite cases.
This commit is contained in:
@@ -14,8 +14,14 @@
|
|||||||
/// Absolute sample index at the end of the given session-relative
|
/// Absolute sample index at the end of the given session-relative
|
||||||
/// seconds mark, rounded to the nearest sample. `end_secs` typically
|
/// seconds mark, rounded to the nearest sample. `end_secs` typically
|
||||||
/// comes from `LocalAgreement::last_committed_end_secs()`.
|
/// comes from `LocalAgreement::last_committed_end_secs()`.
|
||||||
|
///
|
||||||
|
/// Guards against non-finite inputs: NaN and ±infinity both return 0
|
||||||
|
/// ("nothing committed yet"). Without this, Rust's saturating
|
||||||
|
/// float-to-int cast turns `f64::INFINITY` into `u64::MAX`, which
|
||||||
|
/// would park the capture buffer origin at an index beyond any
|
||||||
|
/// reachable sample and trim the entire buffer forever.
|
||||||
pub fn sample_index_for_seconds(end_secs: f64, sample_rate: u32) -> u64 {
|
pub fn sample_index_for_seconds(end_secs: f64, sample_rate: u32) -> u64 {
|
||||||
if end_secs <= 0.0 {
|
if !end_secs.is_finite() || end_secs <= 0.0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
(end_secs * sample_rate as f64).round() as u64
|
(end_secs * sample_rate as f64).round() as u64
|
||||||
@@ -65,6 +71,17 @@ mod tests {
|
|||||||
assert_eq!(sample_index_for_seconds(-1.0, 16_000), 0);
|
assert_eq!(sample_index_for_seconds(-1.0, 16_000), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_index_for_seconds_rejects_nan_and_infinity() {
|
||||||
|
// Defensive against non-finite inputs: without the is_finite()
|
||||||
|
// check, Rust's saturating float-to-int cast makes +infinity
|
||||||
|
// become u64::MAX, which would park the buffer origin beyond
|
||||||
|
// reach and trim the whole buffer forever.
|
||||||
|
assert_eq!(sample_index_for_seconds(f64::NAN, 16_000), 0);
|
||||||
|
assert_eq!(sample_index_for_seconds(f64::INFINITY, 16_000), 0);
|
||||||
|
assert_eq!(sample_index_for_seconds(f64::NEG_INFINITY, 16_000), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sample_index_for_seconds_rounds_nearest() {
|
fn sample_index_for_seconds_rounds_nearest() {
|
||||||
// 0.5 s at 16 kHz = 8000 samples exactly.
|
// 0.5 s at 16 kHz = 8000 samples exactly.
|
||||||
|
|||||||
Reference in New Issue
Block a user