use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
#[cfg(test)]
use std::thread;
#[derive(Debug)]
pub struct Flaken {
node: u64,
epoch: u64,
bitwidths: (u64, u64, u64),
seq: u64,
start_ts: u64,
start_instant: Instant,
duration: u64,
}
trait AsMillis {
fn as_millis(self) -> u64;
}
impl AsMillis for Duration {
fn as_millis(self) -> u64 {
self.as_secs()*1000 + (self.subsec_nanos() as u64)/1000000
}
}
impl Flaken {
pub fn default() -> Flaken {
let since_unix = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let ts = since_unix.as_millis();
let instant = Instant::now();
Flaken {
node: 0,
seq: 0,
epoch: 1356998400000,
bitwidths: (42, 10, 12),
start_ts: ts,
start_instant: instant,
duration: 0,
}
}
pub fn epoch(mut self, epoch: u64) -> Flaken {
self.epoch = epoch;
self
}
pub fn node(mut self, node: u64) -> Flaken {
self.node = node;
self
}
pub fn bitwidths(mut self, ts_bits: u64, node_bits: u64) -> Flaken {
assert!(ts_bits + node_bits < 64);
self.bitwidths = (ts_bits, node_bits, 64-(ts_bits+node_bits));
self
}
pub fn next(&mut self) -> u64 {
let duration = self.start_instant.elapsed().as_millis();
if duration != self.duration {
self.seq = 0;
}
let ts = self.start_ts + duration;
let id = self.encode(ts, self.node, self.seq);
self.duration = duration;
self.seq += 1;
id
}
pub fn encode(&self, ts: u64, node: u64, seq: u64) -> u64 {
assert!(ts >= self.epoch);
let ts0 = ts - self.epoch;
let (_, node_shift, seq_shift) = self.bitwidths;
let ts_mask = bitmask(node_shift+seq_shift);
let node_mask = bitmask(seq_shift) ^ ts_mask;
let seq_mask = (bitmask(0) ^ ts_mask) ^ node_mask;
((ts0 << (node_shift + seq_shift)) & ts_mask) | ((node << seq_shift) & node_mask) | (seq & seq_mask)
}
pub fn decode(&self, id: u64) -> (u64, u64, u64) {
let (_, node_shift, seq_shift) = self.bitwidths;
let ts_mask = bitmask(node_shift+seq_shift);
let node_mask = bitmask(seq_shift) ^ ts_mask;
let seq_mask = (bitmask(0) ^ ts_mask) ^ node_mask;
let ts = (id & ts_mask) >> (node_shift+seq_shift);
let node = (id & node_mask) >> seq_shift;
let seq = id & seq_mask;
(ts + self.epoch, node, seq)
}
}
fn bitmask(left_shift: u64) -> u64 {
0xFFFFFFFFFFFFFFFF << left_shift
}
#[test]
fn test_bitmask() {
assert_eq!(bitmask(4), 0xFFFFFFFFFFFFFFF0);
assert_eq!(bitmask(7), 0xFFFFFFFFFFFFFF80);
}
#[test]
fn test_encode_decode() {
let flake = Flaken::default();
let vals = (13+flake.start_ts,24,81);
let id = flake.encode(vals.0, vals.1, vals.2);
assert_eq!(flake.decode(id), vals);
}
#[test]
fn test_next() {
let new_epoch = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
let mut flake = Flaken::default().epoch(new_epoch);
let id0 = flake.next();
let id1 = flake.next();
let (ts0, id0, seq0) = flake.decode(id0);
let (ts1, id1, seq1) = flake.decode(id1);
assert!((ts0-new_epoch) < 1);
assert_eq!(id0, 0);
assert_eq!(seq0, 0);
assert!((ts1-new_epoch) < 1);
assert_eq!(id1, 0);
assert_eq!(seq1, 1);
let mut flake1 = flake.node(100);
let id2 = flake1.next();
let (ts2, id2, seq2) = flake1.decode(id2);
assert!((ts2-new_epoch) < 1);
assert_eq!(id2, 100);
assert_eq!(seq2, 2);
thread::sleep(Duration::from_millis(10));
let id3 = flake1.next();
let (ts3, id3, seq3) = flake1.decode(id3);
assert!((ts3-new_epoch) >= 10);
assert_eq!(id3, 100);
assert_eq!(seq3, 0);
}