Skip to content

Commit

Permalink
Merge pull request #19 from negezor/maintain
Browse files Browse the repository at this point in the history
Maintain crate
  • Loading branch information
bahlo authored Jun 22, 2023
2 parents e40e8bb + 2b5e6a2 commit 241edab
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 39 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ description = "A distributed unique ID generator inspired by Twitter's Snowflake
repository = "https://github.com/bahlo/sonyflake-rs"
readme = "README.md"
keywords = ["sonyflake", "snowflake", "random", "id", "generator"]
edition = "2018"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
chrono = "0.4"
thiserror = "1.0"
pnet = "0.28"
pnet = "0.33"

[dev-dependencies]
bencher = "0.1"
Expand Down
4 changes: 2 additions & 2 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ fn bench_new(b: &mut Bencher) {
}

fn bench_next_id(b: &mut Bencher) {
let mut sf = Sonyflake::new().expect("Could not create Sonyflake");
let sf = Sonyflake::new().expect("Could not create Sonyflake");
b.iter(|| sf.next_id());
}

fn bench_decompose(b: &mut Bencher) {
let mut sf = Sonyflake::new().expect("Could not create Sonyflake");
let sf = Sonyflake::new().expect("Could not create Sonyflake");
let next_id = sf.next_id().expect("Could not get next id");

b.iter(|| decompose(next_id));
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<'a> Builder<'a> {

to_sonyflake_time(start_time)
} else {
to_sonyflake_time(Utc.ymd(2014, 9, 1).and_hms(0, 0, 0))
to_sonyflake_time(Utc.with_ymd_and_hms(2014, 9, 1, 0, 0, 0).unwrap())
};

let machine_id = if let Some(machine_id) = self.machine_id {
Expand Down
52 changes: 31 additions & 21 deletions src/sonyflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::builder::Builder;
use crate::error::*;
use chrono::prelude::*;
use std::{
collections::HashMap,
sync::{Arc, Mutex},
thread,
time::Duration,
Expand All @@ -15,6 +14,8 @@ pub(crate) const BIT_LEN_SEQUENCE: u64 = 8;
/// bit length of machine id
pub(crate) const BIT_LEN_MACHINE_ID: u64 = 63 - BIT_LEN_TIME - BIT_LEN_SEQUENCE;

const GENERATE_MASK_SEQUENCE: u16 = (1 << BIT_LEN_SEQUENCE) - 1;

#[derive(Debug)]
pub(crate) struct Internals {
pub(crate) elapsed_time: i64,
Expand Down Expand Up @@ -52,9 +53,7 @@ impl Sonyflake {

/// Generate the next unique id.
/// After the Sonyflake time overflows, next_id returns an error.
pub fn next_id(&mut self) -> Result<u64, Error> {
let mask_sequence = (1 << BIT_LEN_SEQUENCE) - 1;

pub fn next_id(&self) -> Result<u64, Error> {
let mut internals = self.0.internals.lock().map_err(|_| Error::MutexPoisoned)?;

let current = current_elapsed_time(self.0.start_time);
Expand All @@ -63,7 +62,7 @@ impl Sonyflake {
internals.sequence = 0;
} else {
// self.elapsed_time >= current
internals.sequence = (internals.sequence + 1) & mask_sequence;
internals.sequence = (internals.sequence + 1) & GENERATE_MASK_SEQUENCE;
if internals.sequence == 0 {
internals.elapsed_time += 1;
let overtime = internals.elapsed_time - current;
Expand Down Expand Up @@ -105,21 +104,32 @@ fn sleep_time(overtime: i64) -> Duration {
- Duration::from_nanos((Utc::now().timestamp_nanos() % SONYFLAKE_TIME_UNIT) as u64)
}

pub struct DecomposedSonyflake {
pub id: u64,
pub msb: u64,
pub time: u64,
pub sequence: u64,
pub machine_id: u64,
}

impl DecomposedSonyflake {
/// Returns the timestamp in nanoseconds without epoch.
pub fn nanos_time(&self) -> i64 {
(self.time as i64) * SONYFLAKE_TIME_UNIT
}
}

const DECOMPOSE_MASK_SEQUENCE: u64 = ((1 << BIT_LEN_SEQUENCE) - 1) << BIT_LEN_MACHINE_ID;

const MASK_MACHINE_ID: u64 = (1 << BIT_LEN_MACHINE_ID) - 1;

/// Break a Sonyflake ID up into its parts.
pub fn decompose(id: u64) -> HashMap<String, u64> {
let mut map = HashMap::new();

let mask_sequence = ((1 << BIT_LEN_SEQUENCE) - 1) << BIT_LEN_MACHINE_ID;
let mask_machine_id = (1 << BIT_LEN_MACHINE_ID) - 1;

map.insert("id".into(), id);
map.insert("msb".into(), id >> 63);
map.insert("time".into(), id >> (BIT_LEN_SEQUENCE + BIT_LEN_MACHINE_ID));
map.insert(
"sequence".into(),
(id & mask_sequence) >> BIT_LEN_MACHINE_ID,
);
map.insert("machine-id".into(), id & mask_machine_id);

map
pub fn decompose(id: u64) -> DecomposedSonyflake {
DecomposedSonyflake {
id,
msb: id >> 63,
time: id >> (BIT_LEN_SEQUENCE + BIT_LEN_MACHINE_ID),
sequence: (id & DECOMPOSE_MASK_SEQUENCE) >> BIT_LEN_MACHINE_ID,
machine_id: id & MASK_MACHINE_ID,
}
}
26 changes: 13 additions & 13 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,32 @@ use thiserror::Error;

#[test]
fn test_next_id() -> Result<(), BoxDynError> {
let mut sf = Sonyflake::new()?;
let sf = Sonyflake::new()?;
assert!(sf.next_id().is_ok());
Ok(())
}

#[test]
fn test_once() -> Result<(), BoxDynError> {
let now = Utc::now();
let mut sf = Sonyflake::builder().start_time(now).finalize()?;
let sf = Sonyflake::builder().start_time(now).finalize()?;

let sleep_time = 50;
thread::sleep(Duration::from_millis(10 * sleep_time));

let id = sf.next_id()?;
let parts = decompose(id);

let actual_msb = *parts.get("msb").expect("No msb part");
let actual_msb = parts.msb;
assert_eq!(0, actual_msb, "Unexpected msb");

let actual_time = *parts.get("time").expect("No time part");
let actual_time = parts.time;
if actual_time < sleep_time || actual_time > sleep_time + 1 {
panic!("Unexpected time {}", actual_time)
}

let machine_id = lower_16_bit_private_ip()? as u64;
let actual_machine_id = *parts.get("machine-id").expect("No machine id part");
let actual_machine_id = parts.machine_id;
assert_eq!(machine_id, actual_machine_id, "Unexpected machine id");

Ok(())
Expand All @@ -53,7 +53,7 @@ fn test_once() -> Result<(), BoxDynError> {
fn test_run_for_10s() -> Result<(), BoxDynError> {
let now = Utc::now();
let start_time = to_sonyflake_time(now);
let mut sf = Sonyflake::builder().start_time(now).finalize()?;
let sf = Sonyflake::builder().start_time(now).finalize()?;

let mut last_id: u64 = 0;
let mut max_sequence: u64 = 0;
Expand All @@ -73,23 +73,23 @@ fn test_run_for_10s() -> Result<(), BoxDynError> {

current = to_sonyflake_time(Utc::now());

let actual_msb = *parts.get("msb").unwrap();
let actual_msb = parts.msb;
if actual_msb != 0 {
panic!("unexpected msb: {}", actual_msb);
}

let actual_time = *parts.get("time").unwrap() as i64;
let actual_time = parts.time as i64;
let overtime = start_time + actual_time - current;
if overtime > 0 {
panic!("unexpected overtime: {}", overtime)
}

let actual_sequence = *parts.get("sequence").unwrap();
let actual_sequence = parts.sequence;
if max_sequence < actual_sequence {
max_sequence = actual_sequence;
}

let actual_machine_id = *parts.get("machine-id").unwrap();
let actual_machine_id = parts.machine_id;
if actual_machine_id != machine_id {
panic!("unexpected machine id: {}", actual_machine_id)
}
Expand All @@ -112,7 +112,7 @@ fn test_threads() -> Result<(), BoxDynError> {

let mut children = Vec::new();
for _ in 0..10 {
let mut thread_sf = sf.clone();
let thread_sf = sf.clone();
let thread_tx = tx.clone();
children.push(thread::spawn(move || {
for _ in 0..1000 {
Expand All @@ -137,7 +137,7 @@ fn test_threads() -> Result<(), BoxDynError> {

#[test]
fn test_generate_10_ids() -> Result<(), BoxDynError> {
let mut sf = Sonyflake::builder().machine_id(&|| Ok(42)).finalize()?;
let sf = Sonyflake::builder().machine_id(&|| Ok(42)).finalize()?;
let mut ids = vec![];
for _ in 0..10 {
let id = sf.next_id()?;
Expand Down Expand Up @@ -189,7 +189,7 @@ fn test_error_send_sync() {

#[test]
fn test_over_time_limit() -> Result<(), BoxDynError> {
let mut sf = Sonyflake::new()?;
let sf = Sonyflake::new()?;
let mut internals = sf.0.internals.lock().unwrap();
internals.elapsed_time = 1 << BIT_LEN_TIME;
drop(internals);
Expand Down

0 comments on commit 241edab

Please sign in to comment.