simplyfy Snowflake
This commit is contained in:
parent
9942a4b100
commit
29fcdfe641
|
@ -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 }
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue