aboutsummaryrefslogtreecommitdiff
path: root/src/mail_uuid.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mail_uuid.rs')
-rw-r--r--src/mail_uuid.rs76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/mail_uuid.rs b/src/mail_uuid.rs
new file mode 100644
index 0000000..d0d582f
--- /dev/null
+++ b/src/mail_uuid.rs
@@ -0,0 +1,76 @@
+use std::sync::atomic::{AtomicU64, Ordering};
+
+use lazy_static::lazy_static;
+use rand::prelude::*;
+use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::time::now_msec;
+
+/// A Mail UUID is composed of two components:
+/// - a process identifier, 128 bits, itself composed of:
+/// - the timestamp of when the process started, 64 bits
+/// - a 64-bit random number
+/// - a sequence number, 64 bits
+#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
+pub struct MailUuid(pub [u8; 24]);
+
+struct UuidGenerator {
+ pid: u128,
+ sn: AtomicU64,
+}
+
+impl UuidGenerator {
+ fn new() -> Self {
+ let time = now_msec() as u128;
+ let rand = thread_rng().gen::<u64>() as u128;
+ Self {
+ pid: (time << 64) | rand,
+ sn: AtomicU64::new(0),
+ }
+ }
+
+ fn gen(&self) -> MailUuid {
+ let sn = self.sn.fetch_add(1, Ordering::Relaxed);
+ let mut res = [0u8; 24];
+ res[0..16].copy_from_slice(&u128::to_be_bytes(self.pid));
+ res[16..24].copy_from_slice(&u64::to_be_bytes(sn));
+ MailUuid(res)
+ }
+}
+
+lazy_static! {
+ static ref GENERATOR: UuidGenerator = UuidGenerator::new();
+}
+
+pub fn gen_uuid() -> MailUuid {
+ GENERATOR.gen()
+}
+
+// -- serde --
+
+impl<'de> Deserialize<'de> for MailUuid {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let v = String::deserialize(d)?;
+ let bytes = hex::decode(v).map_err(|_| D::Error::custom("invalid hex"))?;
+
+ if bytes.len() != 24 {
+ return Err(D::Error::custom("bad length"));
+ }
+
+ let mut tmp = [0u8; 24];
+ tmp[..].copy_from_slice(&bytes);
+ Ok(Self(tmp))
+ }
+}
+
+impl Serialize for MailUuid {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(&hex::encode(self.0))
+ }
+}