Skip to content

Commit 66edada

Browse files
committed
add files
0 parents  commit 66edada

File tree

5 files changed

+296
-0
lines changed

5 files changed

+296
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock
4+
.idea/
5+
*.iml

Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "ulid-rs"
3+
version = "0.1.0"
4+
authors = ["Junichi Kato <[email protected]>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
rand = { version = "0.7.3" }
11+
chrono = { version = "0.4"}
12+
serde = { version = "1.0", optional = true }

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# ulid-rs
2+
3+
## Alternative crates
4+
5+
- https://github.com/dylanhart/ulid-rs (53 stars)
6+
- https://github.com/huxi/rusty_ulid (10 stars)

rust-toolchain

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nightly-2020-12-25

src/lib.rs

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
use std::cmp::Ordering;
2+
use std::convert::TryFrom;
3+
use std::str::FromStr;
4+
5+
use chrono::{DateTime, Duration, Local, TimeZone, Utc};
6+
use rand::rngs::ThreadRng;
7+
use rand::RngCore;
8+
9+
use crate::ULIDError::RandomGenError;
10+
11+
const ULID_BYTES_LENGTH: i32 = 16;
12+
const TIMESTAMP_OVERFLOW_MASK: u64 = 0xffff000000000000;
13+
const MASK_BITS: u32 = 5;
14+
const MASK: u64 = 0x1f;
15+
const ENCODING_CHARS: [char; 32] = [
16+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
17+
'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z',
18+
];
19+
const DECODING_CHARS: [i32; 123] = [
20+
// 0
21+
-1, -1, -1, -1, -1, -1, -1, -1, // 8
22+
-1, -1, -1, -1, -1, -1, -1, -1, // 16
23+
-1, -1, -1, -1, -1, -1, -1, -1, // 24
24+
-1, -1, -1, -1, -1, -1, -1, -1, // 32
25+
-1, -1, -1, -1, -1, -1, -1, -1, // 40
26+
-1, -1, -1, -1, -1, -1, -1, -1, // 48
27+
0, 1, 2, 3, 4, 5, 6, 7, // 56
28+
8, 9, -1, -1, -1, -1, -1, -1, // 64
29+
-1, 10, 11, 12, 13, 14, 15, 16, // 72
30+
17, 1, 18, 19, 1, 20, 21, 0, // 80
31+
22, 23, 24, 25, 26, -1, 27, 28, // 88
32+
29, 30, 31, -1, -1, -1, -1, -1, // 96
33+
-1, 10, 11, 12, 13, 14, 15, 16, // 104
34+
17, 1, 18, 19, 1, 20, 21, 0, // 112
35+
22, 23, 24, 25, 26, -1, 27, 28, // 120
36+
29, 30, 31,
37+
];
38+
39+
fn internal_write_crockford(value: u64, count: u32) -> String {
40+
(0..count)
41+
.into_iter()
42+
.fold("".to_string(), |mut result, i| {
43+
let index = (value >> ((count - i - 1) * MASK_BITS)) & MASK;
44+
result.push(ENCODING_CHARS[index as usize]);
45+
result
46+
})
47+
}
48+
49+
fn interal_parse_crockford(input: &str) -> u64 {
50+
let length = input.len();
51+
if length > 12 {
52+
panic!("input length must not exceed 12 but was {}!", length)
53+
}
54+
let result = input
55+
.chars()
56+
.enumerate()
57+
.into_iter()
58+
.fold(0u64, |result, (i, current)| {
59+
let value = if (current as usize) < DECODING_CHARS.len() {
60+
DECODING_CHARS[current as usize]
61+
} else {
62+
-1
63+
};
64+
if value < 0 {
65+
panic!("Illegal character '{}'!", current)
66+
}
67+
let factor = (length as u32) - 1u32 - (i as u32);
68+
let value = (value as u64) << (factor * MASK_BITS);
69+
result | value
70+
});
71+
result
72+
}
73+
74+
#[derive(Debug, Clone, Copy, PartialEq)]
75+
pub struct ULID {
76+
most_significant_bits: u64,
77+
least_significant_bits: u64,
78+
}
79+
80+
impl PartialOrd for ULID {
81+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
82+
if self.most_significant_bits < other.most_significant_bits {
83+
Some(Ordering::Less)
84+
} else if self.most_significant_bits > other.most_significant_bits {
85+
Some(Ordering::Greater)
86+
} else if self.least_significant_bits < other.least_significant_bits {
87+
Some(Ordering::Less)
88+
} else if self.least_significant_bits > other.least_significant_bits {
89+
Some(Ordering::Greater)
90+
} else {
91+
Some(Ordering::Equal)
92+
}
93+
}
94+
}
95+
96+
impl ToString for ULID {
97+
fn to_string(&self) -> String {
98+
let mut result = internal_write_crockford(self.to_epoch_milli_as_long(), 10);
99+
result.push_str(&internal_write_crockford(
100+
(self.most_significant_bits & 0xffff) << 24 | self.least_significant_bits >> 40,
101+
8,
102+
));
103+
result.push_str(&internal_write_crockford(self.least_significant_bits, 8));
104+
result
105+
}
106+
}
107+
108+
impl ULID {
109+
pub fn new(most_significant_bits: u64, least_significant_bits: u64) -> Self {
110+
Self {
111+
most_significant_bits,
112+
least_significant_bits,
113+
}
114+
}
115+
116+
pub fn to_epoch_milli_as_long(&self) -> u64 {
117+
self.most_significant_bits >> 16
118+
}
119+
120+
pub fn to_epoch_milli_as_duration(&self) -> Duration {
121+
Duration::milliseconds(self.to_epoch_milli_as_long() as i64)
122+
}
123+
124+
pub fn to_date_time(&self) -> DateTime<Utc> {
125+
Utc.timestamp_millis(self.to_epoch_milli_as_long() as i64)
126+
}
127+
128+
pub fn to_bytes(&self) -> ByteArray {
129+
let mut result: ByteArray = Vec::with_capacity(16);
130+
result.resize(16, 0);
131+
for i in 0..8 {
132+
result[i] = ((self.most_significant_bits >> ((7 - i) * 8)) & 0xff) as u8;
133+
}
134+
for i in 8..16 {
135+
result[i] = ((self.least_significant_bits >> ((15 - i) * 8)) & 0xff) as u8;
136+
}
137+
result
138+
}
139+
}
140+
141+
pub struct ULIDGenerator {
142+
rng: ThreadRng,
143+
}
144+
145+
#[derive(Debug, Clone)]
146+
pub enum ULIDError {
147+
RandomGenError { msg: String },
148+
InvalidByteArrayError,
149+
TimestampOverflowError,
150+
}
151+
152+
type ByteArray = Vec<u8>;
153+
154+
impl FromStr for ULID {
155+
type Err = ULIDError;
156+
157+
fn from_str(ulid_str: &str) -> Result<Self, Self::Err> {
158+
let len = ulid_str.len();
159+
let ts = interal_parse_crockford(&ulid_str[0..10]);
160+
if (ts & TIMESTAMP_OVERFLOW_MASK) != 0 {
161+
return Err(ULIDError::TimestampOverflowError);
162+
}
163+
let part1 = interal_parse_crockford(&ulid_str[10..18]);
164+
let part2 = interal_parse_crockford(&ulid_str[18..len]);
165+
166+
let most_significant_bits = (ts << 16) | (part1 >> 24);
167+
let least_significant_bits = part2 | (part1 << 40);
168+
Ok(ULID::new(most_significant_bits, least_significant_bits))
169+
}
170+
}
171+
172+
impl TryFrom<ByteArray> for ULID {
173+
type Error = ULIDError;
174+
175+
fn try_from(value: ByteArray) -> Result<Self, Self::Error> {
176+
if value.len() != ULID_BYTES_LENGTH as usize {
177+
Err(ULIDError::InvalidByteArrayError)
178+
} else {
179+
let mut most_significant_bits = 0u64;
180+
for i in 0..8 {
181+
most_significant_bits = (most_significant_bits << 8) | (value[i] & 0xff) as u64;
182+
}
183+
let mut least_significant_bits = 0u64;
184+
for i in 8..16 {
185+
least_significant_bits = (least_significant_bits << 8) | (value[i] & 0xff) as u64;
186+
}
187+
Ok(ULID::new(most_significant_bits, least_significant_bits))
188+
}
189+
}
190+
}
191+
192+
impl ULIDGenerator {
193+
pub fn new() -> Self {
194+
Self {
195+
rng: rand::thread_rng(),
196+
}
197+
}
198+
199+
pub fn generate(&mut self) -> ULID {
200+
let ts = Self::unix_time_stamp() as u64;
201+
Self::check_timestamp(ts);
202+
let (r1, r2) = Self::generate_random(|usize| self.random(usize).unwrap());
203+
let most_significant_bits = (ts << 16) | (r1 >> 24);
204+
let least_significant_bits = (r1 << 40) | r2;
205+
ULID::new(most_significant_bits, least_significant_bits)
206+
}
207+
208+
fn check_timestamp(timestamp: u64) {
209+
if (timestamp & TIMESTAMP_OVERFLOW_MASK) != 0 {
210+
panic!("ULID does not support timestamps after +10889-08-02T05:31:50.655Z!")
211+
}
212+
}
213+
214+
fn random(&mut self, size: usize) -> Result<ByteArray, ULIDError> {
215+
let mut b: Vec<u8> = Vec::with_capacity(size);
216+
b.resize(size, 0u8);
217+
let result = self.rng.try_fill_bytes(&mut b[..]);
218+
match result {
219+
Ok(_) => Ok(b),
220+
Err(e) => Err(RandomGenError { msg: e.to_string() }),
221+
}
222+
}
223+
224+
fn unix_time_stamp() -> i64 {
225+
Utc::now().timestamp_millis()
226+
}
227+
228+
#[inline]
229+
fn generate_random<F>(mut random_gen: F) -> (u64, u64)
230+
where
231+
F: FnMut(usize) -> ByteArray,
232+
{
233+
let bytes = random_gen(10);
234+
235+
let mut random1 = ((bytes[0x0] & 0xff) as u64) << 32;
236+
random1 |= ((bytes[0x1] & 0xff) as u64) << 24;
237+
random1 |= ((bytes[0x2] & 0xff) as u64) << 16;
238+
random1 |= ((bytes[0x3] & 0xff) as u64) << 8;
239+
random1 |= (bytes[0x4] & 0xff) as u64;
240+
241+
let mut random2 = ((bytes[0x5] & 0xff) as u64) << 32;
242+
random2 |= ((bytes[0x6] & 0xff) as u64) << 24;
243+
random2 |= ((bytes[0x7] & 0xff) as u64) << 16;
244+
random2 |= ((bytes[0x8] & 0xff) as u64) << 8;
245+
random2 |= (bytes[0x9] & 0xff) as u64;
246+
247+
(random1, random2)
248+
}
249+
}
250+
251+
#[cfg(test)]
252+
mod tests {
253+
use std::convert::TryFrom;
254+
255+
use crate::{ULIDGenerator, ULID};
256+
257+
#[test]
258+
fn it_works() {
259+
let mut ulid_generator = ULIDGenerator::new();
260+
let ulid: ULID = ulid_generator.generate();
261+
println!("{:?}", ulid);
262+
println!("{:?}", ulid.to_string());
263+
264+
let s = ulid.to_string().parse::<ULID>();
265+
println!("{:?}", s);
266+
267+
let b = ulid.to_bytes();
268+
println!("{:?}", b);
269+
let t = ULID::try_from(b).unwrap();
270+
println!("{:?}", t);
271+
}
272+
}

0 commit comments

Comments
 (0)