mirror of
https://github.com/uuid-rs/uuid.git
synced 2025-10-02 07:20:40 +00:00
work on higher precision V7 UUIDs
This commit is contained in:
parent
bf5b0b84d2
commit
45683af675
@ -668,7 +668,7 @@ pub mod context {
|
|||||||
mod v7_support {
|
mod v7_support {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use core::{cell::Cell, panic::RefUnwindSafe};
|
use core::{cell::Cell, panic::RefUnwindSafe, convert::TryInto};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
static CONTEXT_V7: SharedContextV7 =
|
static CONTEXT_V7: SharedContextV7 =
|
||||||
@ -707,6 +707,8 @@ pub mod context {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ContextV7 {
|
pub struct ContextV7 {
|
||||||
last_reseed: Cell<LastReseed>,
|
last_reseed: Cell<LastReseed>,
|
||||||
|
shift: Shift,
|
||||||
|
sub_milli_precision_bits: u8,
|
||||||
counter: Cell<u64>,
|
counter: Cell<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,11 +720,61 @@ pub mod context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LastReseed {
|
impl LastReseed {
|
||||||
fn from_millis(millis: u64) -> Self {
|
fn from_ts(ts_seconds: u64, ts_subsec_nanos: u32) -> Self {
|
||||||
|
let millis = (ts_seconds * 1_000).saturating_add(ts_subsec_nanos as u64 / 1_000_000);
|
||||||
|
|
||||||
LastReseed {
|
LastReseed {
|
||||||
millis,
|
millis,
|
||||||
ts_seconds: millis / 1_000,
|
ts_seconds,
|
||||||
ts_subsec_nanos: (millis % 1_000) as u32 * 1_000_000,
|
ts_subsec_nanos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_reseed(&self, ts_seconds: u64, ts_subsec_nanos: u32) -> bool {
|
||||||
|
let millis = (ts_seconds * 1_000).saturating_add(ts_subsec_nanos as u64 / 1_000_000);
|
||||||
|
|
||||||
|
self.millis != millis
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increment(&self) -> Self {
|
||||||
|
let (ts_seconds, ts_subsec_nanos) = Shift { by_ns: 1_000_000 }.apply(self.ts_seconds, self.ts_subsec_nanos);
|
||||||
|
|
||||||
|
LastReseed {
|
||||||
|
millis: self.millis + 1,
|
||||||
|
ts_seconds,
|
||||||
|
ts_subsec_nanos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Shift {
|
||||||
|
by_ns: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shift {
|
||||||
|
#[inline]
|
||||||
|
fn apply(&self, seconds: u64, subsec_nanos: u32) -> (u64, u32) {
|
||||||
|
if self.by_ns == 0 {
|
||||||
|
// No shift applied
|
||||||
|
return (seconds, subsec_nanos);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut shifted_subsec_nanos = subsec_nanos.checked_add(self.by_ns).unwrap_or(subsec_nanos);
|
||||||
|
|
||||||
|
if shifted_subsec_nanos < 1_000_000_000 {
|
||||||
|
// The shift hasn't overflowed into the next second
|
||||||
|
(seconds, shifted_subsec_nanos)
|
||||||
|
} else {
|
||||||
|
// The shift has overflowed into the next second
|
||||||
|
shifted_subsec_nanos -= 1_000_000_000;
|
||||||
|
|
||||||
|
if seconds < u64::MAX {
|
||||||
|
(seconds + 1, shifted_subsec_nanos)
|
||||||
|
} else {
|
||||||
|
// The next second would overflow a `u64`
|
||||||
|
(seconds, subsec_nanos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -740,8 +792,18 @@ pub mod context {
|
|||||||
ts_subsec_nanos: 0,
|
ts_subsec_nanos: 0,
|
||||||
}),
|
}),
|
||||||
counter: Cell::new(0),
|
counter: Cell::new(0),
|
||||||
|
shift: Shift {
|
||||||
|
by_ns: 0,
|
||||||
|
},
|
||||||
|
sub_milli_precision_bits: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specify an amount to shift timestamps by to obfuscate their actual generation time.
|
||||||
|
pub fn with_shift_millis(mut self, shift: u32) -> Self {
|
||||||
|
self.shift.by_ns = shift * 1_000_000;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClockSequence for ContextV7 {
|
impl ClockSequence for ContextV7 {
|
||||||
@ -756,13 +818,13 @@ pub mod context {
|
|||||||
seconds: u64,
|
seconds: u64,
|
||||||
subsec_nanos: u32,
|
subsec_nanos: u32,
|
||||||
) -> (Self::Output, u64, u32) {
|
) -> (Self::Output, u64, u32) {
|
||||||
let millis = (seconds * 1_000).saturating_add(subsec_nanos as u64 / 1_000_000);
|
let (seconds, subsec_nanos) = self.shift.apply(seconds, subsec_nanos);
|
||||||
|
|
||||||
let last_reseed = self.last_reseed.get();
|
let last_reseed = self.last_reseed.get();
|
||||||
|
|
||||||
// If the observed system time has shifted forwards then regenerate the counter
|
// If the observed system time has shifted forwards then regenerate the counter
|
||||||
if millis > last_reseed.millis {
|
if last_reseed.should_reseed(seconds, subsec_nanos) {
|
||||||
let last_reseed = LastReseed::from_millis(millis);
|
let last_reseed = LastReseed::from_ts(seconds, subsec_nanos);
|
||||||
self.last_reseed.set(last_reseed);
|
self.last_reseed.set(last_reseed);
|
||||||
|
|
||||||
let counter = crate::rng::u64() & RESEED_MASK;
|
let counter = crate::rng::u64() & RESEED_MASK;
|
||||||
@ -792,7 +854,7 @@ pub mod context {
|
|||||||
// this incremented value, all timestamps will use it to maintain monotonicity
|
// this incremented value, all timestamps will use it to maintain monotonicity
|
||||||
else {
|
else {
|
||||||
// Increment the timestamp by 1 milli
|
// Increment the timestamp by 1 milli
|
||||||
let last_reseed = LastReseed::from_millis(last_reseed.millis + 1);
|
let last_reseed = last_reseed.increment();
|
||||||
self.last_reseed.set(last_reseed);
|
self.last_reseed.set(last_reseed);
|
||||||
|
|
||||||
// Reseed the counter
|
// Reseed the counter
|
||||||
@ -885,6 +947,7 @@ pub mod context {
|
|||||||
// This context will wrap
|
// This context will wrap
|
||||||
let context = ContextV7 {
|
let context = ContextV7 {
|
||||||
last_reseed: Cell::new(LastReseed::from_millis(millis)),
|
last_reseed: Cell::new(LastReseed::from_millis(millis)),
|
||||||
|
shift_ns: 0,
|
||||||
counter: Cell::new(u64::MAX >> 22),
|
counter: Cell::new(u64::MAX >> 22),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -900,6 +963,24 @@ pub mod context {
|
|||||||
assert!(ts.counter < (u64::MAX >> 22) as u128);
|
assert!(ts.counter < (u64::MAX >> 22) as u128);
|
||||||
assert_ne!(0, ts.counter);
|
assert_ne!(0, ts.counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn context_shift() {
|
||||||
|
let seconds = 1_496_854_535;
|
||||||
|
let subsec_nanos = 812_946_000;
|
||||||
|
|
||||||
|
let context = ContextV7::new().with_shift_millis(1);
|
||||||
|
|
||||||
|
let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
|
||||||
|
|
||||||
|
assert_eq!((1_496_854_535, 813_000_000), ts.to_unix());
|
||||||
|
|
||||||
|
let context = ContextV7::new().with_shift_millis(-1);
|
||||||
|
|
||||||
|
let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
|
||||||
|
|
||||||
|
assert_eq!((1_496_854_535, 811_000_000), ts.to_unix());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user