commit 8b2d5fe5f01de1c4a0304471a5974b724bf53446 Author: william Date: Mon Sep 4 22:00:00 2023 -0400 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..65b07f3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/uuid.iml b/.idea/uuid.iml new file mode 100644 index 0000000..9b4cf84 --- /dev/null +++ b/.idea/uuid.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a76d5c7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "uuid" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +getrandom = "0.2.10" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9da6b79 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,101 @@ +pub mod v4; +mod rng; + +pub(crate) type Bytes = [u8; 16]; + +pub struct Uuid(Bytes); + +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +pub enum Variant { + NCS = 0u8, + Default = 0x80, + Microsoft = 0xc0, + Future = 0xe0, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +pub enum Version { + Nil = 0, + Time = 1, + DCE = 2, + MD5 = 3, + Random = 4, + SHA1 = 5, + Unknown = u8::MAX +} + +impl Uuid { + pub fn new(version: Version) -> Self { + match version { + Version::Nil => Self::new_nil(), + Version::Random => Self::new_random(), + _ => panic!("Unsupported UUID version: {:?}", version) + } + } + + pub fn new_nil() -> Self { + Uuid(Uuid::nil_bytes()) + } + + #[inline] + pub const fn as_bytes(&self) -> &[u8] { + &self.0 + } + + pub const fn get_variant(&self) -> Variant { + match self.as_bytes()[8] { + x if x & 0x80 == 0 => Variant::NCS, + x if x & 0x40 == 0 => Variant::Default, + x if x & 0x20 == 0 => Variant::Microsoft, + _ => Variant::Future + } + } + + pub const fn get_version_num(&self) -> u8 { + self.as_bytes()[6] >> 4 + } + + pub const fn get_version(&self) -> Version { + match self.get_version_num() { + 0 => Version::Nil, + 1 => Version::Time, + 2 => Version::DCE, + 3 => Version::MD5, + 4 => Version::Random, + 5 => Version::SHA1, + _ => Version::Unknown + } + } + + #[inline] + const fn nil_bytes() -> Bytes { + [0u8; 16] + } + + #[inline] + fn set_variant(bytes: &mut Bytes, variant: Variant) { + let variant = variant as u8; + let byte = bytes[8] & 0xf; + + bytes[8] = variant & byte; + } + + #[inline] + fn set_version(bytes: &mut Bytes, version: Version) { + bytes[6] |= (version as u8) << 4; + } +} + +impl Default for Uuid { + fn default() -> Self { + Uuid::new_nil() + } +} + +// impl AsRef for Uuid { +// fn as_ref(&self) -> &Bytes { +// &self.0 +// } +// } diff --git a/src/rng.rs b/src/rng.rs new file mode 100644 index 0000000..eb0daef --- /dev/null +++ b/src/rng.rs @@ -0,0 +1,10 @@ +use getrandom::getrandom; +use crate::Bytes; + +pub(crate) fn get_random_bytes() -> Bytes { + let mut bytes = [0u8; 16]; + + getrandom(&mut bytes).expect("Failed to get random bytes for UUID"); + + bytes +} diff --git a/src/v4.rs b/src/v4.rs new file mode 100644 index 0000000..dad02ff --- /dev/null +++ b/src/v4.rs @@ -0,0 +1,30 @@ +use crate::rng::get_random_bytes; + +use crate::{Uuid, Variant, Version}; + +impl Uuid { + pub fn new_v4() -> Self { + Uuid::new_random() + } + + pub fn new_random() -> Self { + let mut bytes = get_random_bytes(); + + Uuid::set_variant(&mut bytes, Variant::Default); + Uuid::set_version(&mut bytes, Version::Random); + + Uuid(bytes) + } +} + +#[cfg(test)] +mod tests { + use crate::{Uuid, Variant}; + + #[test] + fn new_v4__variant() { + let uuid = Uuid::new_v4(); + + assert_eq!(uuid.get_variant(), Variant::Default); + } +}