#![cfg_attr(not(any(test, feature = "std")), no_std)]
use bitvec::prelude::*;
use core::{convert::TryInto, fmt, fmt::Write as _, str::FromStr};
use md5::{Digest, Md5};
use rand::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use sha1::{digest::generic_array::sequence::Shorten, Sha1};
const UUID_STR_LENGTH: usize = 36;
const UUID_URN_LENGTH: usize = 45;
const UUID_URN: &str = "urn:uuid:";
pub const NAMESPACE_DNS: Uuid = Uuid::from_bytes([
107, 167, 184, 16, 157, 173, 17, 209, 128, 180, 0, 192, 79, 212, 48, 200,
]);
pub const NAMESPACE_URL: Uuid = Uuid::from_bytes([
107, 167, 184, 17, 157, 173, 17, 209, 128, 180, 0, 192, 79, 212, 48, 200,
]);
pub const NAMESPACE_OID: Uuid = Uuid::from_bytes([
107, 167, 184, 18, 157, 173, 17, 209, 128, 180, 0, 192, 79, 212, 48, 200,
]);
pub const NAMESPACE_X500: Uuid = Uuid::from_bytes([
107, 167, 184, 20, 157, 173, 17, 209, 128, 180, 0, 192, 79, 212, 48, 200,
]);
pub type Bytes = [u8; 16];
struct BytesWrapper<'a> {
bytes: &'a mut [u8],
offset: usize,
}
impl<'a> BytesWrapper<'a> {
fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes, offset: 0 }
}
fn into_inner(self) -> &'a mut [u8] {
self.bytes
}
}
impl<'a> fmt::Write for BytesWrapper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if (self.bytes.len() - self.offset) < s.len() {
return Err(fmt::Error);
}
self.bytes[self.offset..][..s.len()].copy_from_slice(s.as_bytes());
self.offset += s.len();
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Rng(rand::rngs::StdRng);
impl Rng {
#[cfg(feature = "getrandom")]
pub fn new() -> Self {
Self(StdRng::from_rng(rand::rngs::OsRng).unwrap())
}
pub fn from_seed(seed: [u8; 32]) -> Self {
Self(StdRng::from_seed(seed))
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
}
#[cfg(feature = "getrandom")]
impl Default for Rng {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Variant {
Ncs,
Rfc4122,
Microsoft,
Reserved,
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Version {
Time,
Dce,
Md5,
Random,
Sha1,
Nil,
Invalid,
}
#[derive(Debug)]
pub struct ParseUuidError;
impl fmt::Display for ParseUuidError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ParseUuidError")
}
}
#[cfg(any(test, feature = "std"))]
impl std::error::Error for ParseUuidError {}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
#[repr(transparent)]
pub struct Uuid(Bytes);
impl Uuid {
fn set_version(&mut self, ver: Version) {
let bits = self.0[6].view_bits_mut::<Msb0>();
let bits = &mut bits[..4];
match ver {
Version::Time => bits.store_be(1u8),
Version::Dce => bits.store_be(2u8),
Version::Md5 => bits.store_be(3u8),
Version::Random => bits.store_be(4u8),
Version::Sha1 => bits.store_be(5u8),
Version::Nil => unreachable!("Can't set UUID to nil version"),
Version::Invalid => unreachable!("Can't set UUID to invalid version"),
}
}
fn set_variant(&mut self, ver: Variant) {
let bits = self.0[8].view_bits_mut::<Msb0>();
let bits = &mut bits[..3];
match ver {
Variant::Ncs => bits.set(0, true),
Variant::Rfc4122 => {
bits.set(0, true);
bits.set(1, false);
}
Variant::Microsoft => {
bits.set(0, true);
bits.set(1, true);
bits.set(2, false);
}
Variant::Reserved => bits.set_all(true),
}
}
#[inline]
fn swap_endian(mut self) -> Self {
self.0[0..4].reverse();
self.0[4..6].reverse();
self.0[6..8].reverse();
self
}
}
impl Uuid {
#[inline]
pub const fn nil() -> Self {
Uuid([0; 16])
}
#[inline]
pub const fn from_bytes(bytes: Bytes) -> Self {
Self(bytes)
}
#[inline]
pub const fn to_bytes(self) -> Bytes {
self.0
}
#[inline]
pub fn from_bytes_me(bytes: Bytes) -> Self {
Self(bytes).swap_endian()
}
#[inline]
pub fn to_bytes_me(self) -> Bytes {
self.swap_endian().to_bytes()
}
#[inline]
pub fn is_nil(self) -> bool {
self.0 == Self::nil().0
}
#[inline]
pub fn variant(self) -> Variant {
let bits = &self.0[8].view_bits::<Msb0>()[..3];
match (bits[0], bits[1], bits[2]) {
(true, true, true) => Variant::Reserved,
(true, true, false) => Variant::Microsoft,
(true, false, ..) => Variant::Rfc4122,
(false, ..) => Variant::Ncs,
}
}
#[inline]
pub fn version(self) -> Version {
let bits = &self.0[6].view_bits::<Msb0>()[..4];
match (bits[0], bits[1], bits[2], bits[3]) {
(false, false, false, false) => Version::Nil,
(false, false, false, true) => Version::Time,
(false, false, true, false) => Version::Dce,
(false, false, true, true) => Version::Md5,
(false, true, false, false) => Version::Random,
(false, true, false, true) => Version::Sha1,
_ => Version::Invalid,
}
}
pub fn to_str(self, buf: &mut [u8]) -> &mut str {
assert!(buf.len() == 36, "Uuid::to_str requires 36 bytes");
let bytes = self.to_bytes();
let time_low = u32::from_be_bytes(bytes[..4].try_into().unwrap());
let time_mid = u16::from_be_bytes(bytes[4..6].try_into().unwrap());
let time_hi_and_version = u16::from_be_bytes(bytes[6..8].try_into().unwrap());
let clock_seq_hi_and_reserved = u8::from_be_bytes(bytes[8..9].try_into().unwrap());
let clock_seq_low = u8::from_be_bytes(bytes[9..10].try_into().unwrap());
let mut node = [0; 8];
node[2..].copy_from_slice(&bytes[10..16]);
let node = u64::from_be_bytes(node);
let mut buf = BytesWrapper::new(buf);
write!(
buf,
"{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:012x}",
time_low, time_mid, time_hi_and_version, clock_seq_hi_and_reserved, clock_seq_low, node
)
.expect("BUG: Couldn't write UUID");
core::str::from_utf8_mut(buf.into_inner()).expect("BUG: Invalid UTF8")
}
pub fn to_urn(self, buf: &mut [u8]) -> &mut str {
assert!(buf.len() == 45, "Uuid::to_urn requires 45 bytes");
buf[..UUID_URN.len()].copy_from_slice(UUID_URN.as_bytes());
self.to_str(&mut buf[UUID_URN.len()..]);
core::str::from_utf8_mut(buf).expect("BUG: Invalid UTF8")
}
pub fn to_str_upper(self, buf: &mut [u8]) -> &mut str {
let s = self.to_str(buf);
s.make_ascii_uppercase();
s
}
pub fn to_urn_upper(self, buf: &mut [u8]) -> &mut str {
let s = self.to_urn(buf);
s[UUID_URN.len()..].make_ascii_uppercase();
s
}
}
impl Uuid {
#[inline]
pub fn parse(s: &str) -> Result<Self, ParseUuidError> {
Uuid::from_str(s)
}
#[cfg(feature = "getrandom")]
pub fn new_v4() -> Self {
let mut uuid = Uuid::nil();
Rng::new().fill_bytes(&mut uuid.0);
uuid.set_variant(Variant::Rfc4122);
uuid.set_version(Version::Random);
uuid
}
pub fn new_v4_rng(rng: &mut Rng) -> Self {
let mut uuid = Uuid::nil();
rng.fill_bytes(&mut uuid.0);
uuid.set_variant(Variant::Rfc4122);
uuid.set_version(Version::Random);
uuid
}
pub fn new_v3(namespace: Uuid, name: &[u8]) -> Self {
let mut hasher = Md5::new();
hasher.update(namespace.to_bytes());
hasher.update(name);
let mut uuid = Uuid::from_bytes(hasher.finalize().into());
uuid.set_version(Version::Md5);
uuid.set_variant(Variant::Rfc4122);
uuid
}
pub fn new_v5(namespace: Uuid, name: &[u8]) -> Self {
let mut hasher = Sha1::new();
hasher.update(namespace.to_bytes());
hasher.update(name);
let mut uuid = Uuid::from_bytes(
hasher
.finalize()
.pop_back()
.0
.pop_back()
.0
.pop_back()
.0
.pop_back()
.0
.into(),
);
uuid.set_version(Version::Sha1);
uuid.set_variant(Variant::Rfc4122);
uuid
}
}
impl FromStr for Uuid {
type Err = ParseUuidError;
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
if s.len() == UUID_URN_LENGTH {
s = &s[UUID_URN.len()..];
}
if s.len() != UUID_STR_LENGTH {
return Err(ParseUuidError);
}
let mut raw = [0; 16];
let mut buf: &mut [u8] = &mut raw;
for data in s.split('-') {
match data.len() {
8 => {
buf[..4].copy_from_slice(
&u32::from_str_radix(data, 16)
.or(Err(ParseUuidError))?
.to_be_bytes(),
);
buf = &mut buf[4..];
}
4 => {
buf[..2].copy_from_slice(
&u16::from_str_radix(data, 16)
.or(Err(ParseUuidError))?
.to_be_bytes(),
);
buf = &mut buf[2..];
}
12 => {
buf[..6].copy_from_slice(
&u64::from_str_radix(data, 16)
.or(Err(ParseUuidError))?
.to_be_bytes()[2..],
);
}
_ => return Err(ParseUuidError),
}
}
Ok(Uuid::from_bytes(raw))
}
}
impl fmt::Display for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", self)
}
}
impl fmt::Debug for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Uuid({:X})", self)
}
}
impl fmt::LowerHex for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}", UUID_URN)?;
}
let mut buf = [0; 36];
let s = self.to_str(&mut buf);
write!(f, "{}", s)
}
}
impl fmt::UpperHex for Uuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}", UUID_URN)?;
}
let mut buf = [0; 36];
write!(f, "{}", self.to_str_upper(&mut buf))
}
}
impl AsRef<[u8]> for Uuid {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8; 16]> for Uuid {
#[inline]
fn as_ref(&self) -> &[u8; 16] {
&self.0
}
}
impl From<[u8; 16]> for Uuid {
#[inline]
fn from(b: [u8; 16]) -> Self {
Uuid::from_bytes(b)
}
}
#[cfg(test)]
mod tests {
use super::*;
const UUID_NIL: &str = "00000000-0000-0000-0000-000000000000";
const UUID_V4: &str = "662aa7c7-7598-4d56-8bcc-a72c30f998a2";
const UUID_V4_URN: &str = "urn:uuid:662aa7c7-7598-4d56-8bcc-a72c30f998a2";
const UUID_V4_URN_UPPER: &str = "urn:uuid:662AA7C7-7598-4D56-8BCC-A72C30F998A2";
const RAW: [u8; 16] = [
102, 42, 167, 199, 117, 152, 77, 86, 139, 204, 167, 44, 48, 249, 152, 162,
];
fn name(fun: fn(Uuid, &[u8]) -> Uuid, ver: Version) {
let namespace = Uuid::new_v4();
let namespace2 = Uuid::new_v4();
let uuid1 = fun(namespace, b"test");
std::thread::sleep(std::time::Duration::from_millis(500));
let uuid2 = fun(namespace, b"test");
assert_eq!(
uuid1, uuid2,
"V3 UUID's from different times with the same name/namespace must be equal"
);
let uuid = fun(namespace, b"Cat");
assert_ne!(
uuid, uuid2,
"UUID's with two different names in the same namespace must NOT be equal"
);
let uuid = fun(namespace2, b"test");
assert_ne!(
uuid, uuid2,
"UUID's with the same names in a different namespace must NOT be equal"
);
assert_eq!(uuid.version(), ver);
assert_eq!(uuid.variant(), Variant::Rfc4122);
}
#[test]
#[allow(deprecated)]
fn md5() {
name(
|namespace, name| Uuid::new_v3(namespace, name),
Version::Md5,
)
}
#[test]
fn sha1() {
name(
|namespace, name| Uuid::new_v5(namespace, name),
Version::Sha1,
)
}
#[test]
fn parse_string() {
let uuid = Uuid::from_str(UUID_V4).unwrap();
assert_eq!(RAW, uuid.to_bytes(), "Parsed UUID bytes don't match");
let uuid = Uuid::from_str(UUID_V4_URN).unwrap();
assert_eq!(RAW, uuid.to_bytes(), "Parsed UUID bytes don't match");
}
#[test]
fn string() {
let uuid = Uuid::from_bytes(RAW);
let mut buf = [0; 45];
assert_eq!(
&uuid.to_str(&mut buf[..36])[..],
UUID_V4,
"UUID strings didn't match"
);
assert_eq!(
uuid.to_urn(&mut buf),
UUID_V4_URN,
"UUID URN strings didn't match"
);
assert_eq!(
uuid.to_urn_upper(&mut buf),
UUID_V4_URN_UPPER,
"UUID URN upper strings didn't match"
);
assert_eq!(
format!("{:#x}", uuid),
UUID_V4_URN,
"UUID URN Display didn't match"
);
assert_eq!(format!("{:x}", uuid), UUID_V4, "UUID Display didn't match");
assert_eq!(
format!("{}", uuid),
UUID_V4.to_ascii_uppercase(),
"UUID Display didn't match"
);
assert_eq!(
format!("{}", Uuid::nil()),
UUID_NIL,
"Nil UUID Display didn't work!"
);
}
#[test]
fn endian() {
let uuid_be = Uuid::from_bytes(RAW);
assert_eq!(uuid_be.version(), Version::Random);
assert_eq!(uuid_be.variant(), Variant::Rfc4122);
let uuid_le = Uuid::from_bytes_me(uuid_be.to_bytes_me());
assert_eq!(uuid_le.version(), Version::Random);
assert_eq!(uuid_le.variant(), Variant::Rfc4122);
}
#[test]
fn info() {
let uuid = Uuid::from_bytes(RAW);
assert_eq!(uuid.version(), Version::Random);
assert_eq!(uuid.variant(), Variant::Rfc4122);
#[cfg(feature = "getrandom")]
{
let uuid = Uuid::new_v4();
assert_eq!(uuid.version(), Version::Random);
assert_eq!(uuid.variant(), Variant::Rfc4122);
}
}
}