Skip to content

Commit c2d313f

Browse files
committed
guarantee v7 timestamp will never overflow
1 parent 56ba68f commit c2d313f

File tree

1 file changed

+37
-20
lines changed

1 file changed

+37
-20
lines changed

src/timestamp.rs

+37-20
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,17 @@ pub mod context {
557557
let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
558558
assert_eq!(1, ts.counter);
559559
}
560+
561+
#[test]
562+
fn context_overflow() {
563+
let seconds = u64::MAX;
564+
let subsec_nanos = u32::MAX;
565+
566+
let context = Context::new(u16::MAX);
567+
568+
// Ensure we don't panic
569+
Timestamp::from_unix(&context, seconds, subsec_nanos);
570+
}
560571
}
561572
}
562573

@@ -812,14 +823,14 @@ pub mod context {
812823

813824
#[derive(Debug)]
814825
struct Adjust {
815-
by_ns: u32,
826+
by_ns: u128,
816827
}
817828

818829
impl Adjust {
819830
#[inline]
820831
fn by_millis(millis: u32) -> Self {
821832
Adjust {
822-
by_ns: millis.saturating_mul(1_000_000),
833+
by_ns: (millis as u128).saturating_mul(1_000_000),
823834
}
824835
}
825836

@@ -830,23 +841,12 @@ pub mod context {
830841
return (seconds, subsec_nanos);
831842
}
832843

833-
let mut shifted_subsec_nanos =
834-
subsec_nanos.checked_add(self.by_ns).unwrap_or(subsec_nanos);
844+
let ts = (seconds as u128)
845+
.saturating_mul(1_000_000_000)
846+
.saturating_add(subsec_nanos as u128)
847+
.saturating_add(self.by_ns as u128);
835848

836-
if shifted_subsec_nanos < 1_000_000_000 {
837-
// The shift hasn't overflowed into the next second
838-
(seconds, shifted_subsec_nanos)
839-
} else {
840-
// The shift has overflowed into the next second
841-
shifted_subsec_nanos -= 1_000_000_000;
842-
843-
if seconds < u64::MAX {
844-
(seconds + 1, shifted_subsec_nanos)
845-
} else {
846-
// The next second would overflow a `u64`
847-
(seconds, subsec_nanos)
848-
}
849-
}
849+
((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32)
850850
}
851851
}
852852

@@ -878,7 +878,9 @@ pub mod context {
878878
#[inline]
879879
fn from_ts(seconds: u64, subsec_nanos: u32) -> Self {
880880
// Reseed when the millisecond advances
881-
let last_seed = (seconds * 1_000) + (subsec_nanos as u64 / 1_000_000);
881+
let last_seed = seconds
882+
.saturating_mul(1_000)
883+
.saturating_add((subsec_nanos / 1_000_000) as u64);
882884

883885
ReseedingTimestamp {
884886
last_seed,
@@ -918,7 +920,7 @@ pub mod context {
918920

919921
// The factor reduces the size of the sub-millisecond precision to
920922
// fit into the specified number of bits
921-
let factor = (999_999u64 / 2u64.pow(bits as u32)) + 1;
923+
let factor = (999_999 / u64::pow(2, bits as u32)) + 1;
922924

923925
Precision {
924926
bits,
@@ -1113,6 +1115,21 @@ pub mod context {
11131115

11141116
assert!(Uuid::new_v7(ts3) > Uuid::new_v7(ts2));
11151117
}
1118+
1119+
#[test]
1120+
fn context_overflow() {
1121+
let seconds = u64::MAX;
1122+
let subsec_nanos = u32::MAX;
1123+
1124+
// Ensure we don't panic
1125+
for context in [
1126+
ContextV7::new(),
1127+
ContextV7::new().with_additional_precision(),
1128+
ContextV7::new().with_adjust_by_millis(u32::MAX),
1129+
] {
1130+
Timestamp::from_unix(&context, seconds, subsec_nanos);
1131+
}
1132+
}
11161133
}
11171134
}
11181135

0 commit comments

Comments
 (0)