simplyfy Snowflake

This commit is contained in:
Vincent Junge 2023-06-20 02:30:46 +02:00
parent aae478543d
commit f9619dde8d
4 changed files with 77 additions and 132 deletions

View File

@ -29,9 +29,6 @@ openssl = "0.10.52"
base64 = "0.21.2" base64 = "0.21.2"
hostname = "0.3.1" hostname = "0.3.1"
bitflags = { version = "2.2.1", features = ["serde"] } bitflags = { version = "2.2.1", features = ["serde"] }
atomic = "0.5.3"
bigdecimal = "0.3.1"
num-bigint = "0.4.3"
lazy_static = "1.4.0" lazy_static = "1.4.0"
poem = { version = "1.3.55", optional = true } poem = { version = "1.3.55", optional = true }
sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true } sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true }

View File

@ -89,15 +89,15 @@ fn generate_pairs(obj: &Value, key: &str) -> Vec<ConfigEntity> {
fn pairs_to_config(pairs: Vec<ConfigEntity>) -> ConfigValue { fn pairs_to_config(pairs: Vec<ConfigEntity>) -> ConfigValue {
let mut value = Value::Object(Map::new()); let mut value = Value::Object(Map::new());
for p in pairs { for pair in pairs {
let keys: Vec<&str> = p.key.split('_').collect(); let keys: Vec<&str> = pair.key.split('_').collect();
let mut path = vec![]; let mut path = vec![];
for (i, &key) in keys.iter().enumerate() { for (i, &key) in keys.iter().enumerate() {
path.push(key); path.push(key);
if i == keys.len() - 1 { if i == keys.len() - 1 {
insert_into(&mut value, &path, p.value.clone().unwrap_or(Value::Null)); insert_into(&mut value, &path, pair.value.clone().unwrap_or(Value::Null));
} else if keys[i + 1].parse::<usize>().is_ok() { } else if keys[i + 1].parse::<usize>().is_ok() {
if !path_exists(&value, &path) { if !path_exists(&value, &path) {
insert_into(&mut value, &path, Value::Array(Vec::new())); insert_into(&mut value, &path, Value::Array(Vec::new()));
@ -182,6 +182,7 @@ mod test {
let pairs = generate_pairs(&v, ""); let pairs = generate_pairs(&v, "");
let cfg = pairs_to_config(pairs); let cfg = pairs_to_config(pairs);
assert_eq!(cfg, c) assert_eq!(cfg, c)
} }
} }

View File

@ -1,6 +1,6 @@
pub use regexes::*; pub use regexes::*;
pub use rights::Rights; pub use rights::Rights;
pub use snowflake::{DeconstructedSnowflake, Snowflake}; pub use snowflake::Snowflake;
pub mod jwt; pub mod jwt;
mod regexes; mod regexes;

View File

@ -1,22 +1,41 @@
use std::fmt::Display; use std::{
fmt::Display,
sync::atomic::{AtomicUsize, Ordering},
};
use atomic::Atomic; use chrono::{DateTime, TimeZone, Utc};
use bigdecimal::{Num, ToPrimitive, Zero};
use num_bigint::{BigInt, ToBigInt};
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
use sqlx::Type; use sqlx::Type;
/// 2015-01-01
const EPOCH: i64 = 1420070400000; const EPOCH: i64 = 1420070400000;
static WORKER_ID: u128 = 0;
static PROCESS_ID: u128 = 1; /// Unique identifier including a timestamp.
lazy_static::lazy_static! { /// See https://discord.com/developers/docs/reference#snowflakes
static ref INCREMENT: Atomic<u128> = Atomic::default(); #[derive(Debug, Copy, Clone, PartialEq, Eq)]
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(Type))] #[cfg_attr(feature = "sqlx", derive(Type))]
#[cfg_attr(feature = "sqlx", sqlx(transparent))] #[cfg_attr(feature = "sqlx", sqlx(transparent))]
pub struct Snowflake(String); pub struct Snowflake(u64);
impl Snowflake {
pub fn generate() -> Self {
const WORKER_ID: u64 = 0;
const PROCESS_ID: u64 = 1;
static INCREMENT: AtomicUsize = AtomicUsize::new(0);
let time = (Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22;
let worker = WORKER_ID << 17;
let process = PROCESS_ID << 12;
let increment = INCREMENT.fetch_add(1, Ordering::Relaxed) as u64 % 32;
Self(time as u64 | worker | process | increment)
}
pub fn timestamp(self) -> DateTime<Utc> {
Utc.timestamp_millis_opt((self.0 >> 22) as i64 + EPOCH)
.unwrap()
}
}
impl Default for Snowflake { impl Default for Snowflake {
fn default() -> Self { fn default() -> Self {
@ -30,131 +49,59 @@ impl Display for Snowflake {
} }
} }
impl Snowflake { impl serde::Serialize for Snowflake {
pub fn to_binary(&self) -> String { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
let self_len = self.0.len(); where
let high = self.0[..self_len - 10].parse::<u64>().unwrap_or(0); S: serde::Serializer,
let low = self.0[self_len - 10..].parse::<u64>().unwrap(); {
let mut low = low; serializer.serialize_str(&self.0.to_string())
let mut high = high;
let mut bin = Vec::with_capacity(64);
while low > 0 || high > 0 {
bin.push((low & 1) as u8);
low >>= 1;
if high > 0 {
low += 5_000_000_000 * (high % 2);
high >>= 1;
} }
} }
bin.iter() impl<'de> serde::Deserialize<'de> for Snowflake {
.rev() fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
.map(|b| char::from_digit(*b as u32, 10).unwrap()) where
.collect() D: serde::Deserializer<'de>,
{
struct SnowflakeVisitor;
impl<'de> serde::de::Visitor<'de> for SnowflakeVisitor {
type Value = Snowflake;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("snowflake string")
} }
pub fn from_binary(num: &str) -> String { fn visit_str<E>(self, value: &str) -> Result<Snowflake, E>
let mut num = BigInt::from_str_radix(num, 2).unwrap(); where
let mut dec = Vec::with_capacity(18); E: serde::de::Error,
{
let ten = 10.to_bigint().unwrap(); match value.parse() {
let _two = 2.to_bigint().unwrap(); Ok(value) => Ok(Snowflake(value)),
let _thirty_two = 32.to_bigint().unwrap(); Err(_) => Err(serde::de::Error::custom("")),
while num.bits() > 50 {
let high: BigInt = &num >> 32;
let low: BigInt = (high.clone() % &ten) << 32 | &num & BigInt::from((1u64 << 32) - 1);
let next: BigInt = low.clone() % &ten;
dec.push(next.to_u8().unwrap());
num = (high / &ten) << 32 | (low / &ten);
}
while !num.is_zero() {
dec.push((num.clone() % &ten).to_u8().unwrap());
num /= &ten;
}
dec.iter()
.rev()
.map(|d| char::from_digit(*d as u32, 10).unwrap())
.collect()
}
pub fn generate_worker_process() -> u128 {
let time = (chrono::Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22;
let worker = WORKER_ID << 17;
let process = PROCESS_ID << 12;
let increment = INCREMENT.load(atomic::Ordering::Relaxed);
INCREMENT.store(increment + 1, atomic::Ordering::Relaxed);
time as u128 | worker | process | increment
}
pub fn generate() -> Self {
Self(Self::generate_worker_process().to_string())
}
pub fn deconstruct(&self) -> DeconstructedSnowflake {
let binary = format!("{:0>64}", self.to_binary());
let ts = i64::from_str_radix(&binary[0..42], 2).unwrap() + EPOCH;
let wid = u64::from_str_radix(&binary[42..47], 2).unwrap();
let pid = u64::from_str_radix(&binary[47..52], 2).unwrap();
let increment = BigInt::from_str_radix(&binary[52..64], 2).unwrap();
DeconstructedSnowflake {
timestamp: ts,
worker_id: wid,
process_id: pid,
increment,
binary,
} }
} }
} }
deserializer.deserialize_str(SnowflakeVisitor)
#[derive(Debug, Clone, PartialEq, Eq)] }
pub struct DeconstructedSnowflake {
pub timestamp: i64,
pub worker_id: u64,
pub process_id: u64,
pub increment: BigInt,
pub binary: String,
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use chrono::{DateTime, Utc};
use super::Snowflake; use super::Snowflake;
#[test] #[test]
fn test_new_snowflake() { fn generate() {
let snow = Snowflake::generate(); let snow_1 = Snowflake::generate();
println!("{snow}"); let snow_2 = Snowflake::generate();
assert!(snow_1.0 < snow_2.0)
} }
#[test] #[test]
fn snowflake_to_binary() { fn timestamp() {
let snowflake = super::Snowflake("1104339392517902336".to_string()); let snow: Snowflake = serde_json::from_str("\"175928847299117063\"").unwrap();
let timestamp = "2016-04-30 11:18:25.796Z".parse::<DateTime<Utc>>().unwrap();
let bin = snowflake.to_binary(); assert_eq!(snow.timestamp(), timestamp);
println!("{bin}");
}
#[test]
fn binary_to_snowflake() {
let snowflake = super::Snowflake::from_binary(
"111101010011011001101101001110010010100000000001000000000000",
);
println!("{snowflake}");
}
#[test]
fn test_deconstruct() {
let new = super::Snowflake::generate();
println!("{:?}", new.deconstruct());
} }
} }