diff --git a/proxmox-uuid/Cargo.toml b/proxmox-uuid/Cargo.toml index 307bcb33..7499ea7e 100644 --- a/proxmox-uuid/Cargo.toml +++ b/proxmox-uuid/Cargo.toml @@ -18,3 +18,6 @@ default = [] [dev-dependencies] serde = "1.0" serde_json = "1.0" + +[target.'cfg(target_arch="wasm32")'.dependencies] +js-sys = "0.3.55" diff --git a/proxmox-uuid/src/lib.rs b/proxmox-uuid/src/lib.rs index 44882980..9089c29e 100644 --- a/proxmox-uuid/src/lib.rs +++ b/proxmox-uuid/src/lib.rs @@ -3,12 +3,15 @@ use std::borrow::{Borrow, BorrowMut}; use std::fmt; -#[link(name = "uuid")] -extern "C" { - fn uuid_generate(out: *mut [u8; 16]); - fn uuid_unparse_lower(input: *const [u8; 16], out: *mut u8); - fn uuid_unparse_upper(input: *const [u8; 16], out: *mut u8); -} +#[cfg(not(target_arch="wasm32"))] +mod lib_uuid_bindings; +#[cfg(not(target_arch="wasm32"))] +use lib_uuid_bindings::*; + +#[cfg(target_arch="wasm32")] +mod wasm; +#[cfg(target_arch="wasm32")] +pub use wasm::*; /// An error parsing a uuid from a string. #[derive(Debug, Clone, Copy)] @@ -177,30 +180,6 @@ impl fmt::Display for Uuid { } } -impl fmt::LowerHex for Uuid { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut buf = [0u8; 37]; - unsafe { - uuid_unparse_lower(self.as_bytes(), buf.as_mut_ptr()); - } - write!(f, "{}", unsafe { - std::str::from_utf8_unchecked(&buf[..36]) - }) - } -} - -impl fmt::UpperHex for Uuid { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut buf = [0u8; 37]; - unsafe { - uuid_unparse_upper(self.as_bytes(), buf.as_mut_ptr()); - } - write!(f, "{}", unsafe { - std::str::from_utf8_unchecked(&buf[..36]) - }) - } -} - impl std::str::FromStr for Uuid { type Err = UuidError; @@ -209,20 +188,6 @@ impl std::str::FromStr for Uuid { } } -#[cfg(feature = "serde")] -impl serde::Serialize for Uuid { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut buf = [0u8; 37]; - unsafe { - uuid_unparse_lower(self.as_bytes(), buf.as_mut_ptr()); - } - serializer.serialize_str(unsafe { std::str::from_utf8_unchecked(&buf[..36]) }) - } -} - //forward_deserialize_to_from_str!(Uuid); #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Uuid { diff --git a/proxmox-uuid/src/lib_uuid_bindings.rs b/proxmox-uuid/src/lib_uuid_bindings.rs new file mode 100644 index 00000000..0ecd994d --- /dev/null +++ b/proxmox-uuid/src/lib_uuid_bindings.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use crate::Uuid; + +#[link(name = "uuid")] +extern "C" { + pub fn uuid_generate(out: *mut [u8; 16]); + fn uuid_unparse_lower(input: *const [u8; 16], out: *mut u8); + fn uuid_unparse_upper(input: *const [u8; 16], out: *mut u8); +} + +impl fmt::LowerHex for Uuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut buf = [0u8; 37]; + unsafe { + uuid_unparse_lower(self.as_bytes(), buf.as_mut_ptr()); + } + write!(f, "{}", unsafe { + std::str::from_utf8_unchecked(&buf[..36]) + }) + } +} + +impl fmt::UpperHex for Uuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut buf = [0u8; 37]; + unsafe { + uuid_unparse_upper(self.as_bytes(), buf.as_mut_ptr()); + } + write!(f, "{}", unsafe { + std::str::from_utf8_unchecked(&buf[..36]) + }) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Uuid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut buf = [0u8; 37]; + unsafe { + uuid_unparse_lower(self.as_bytes(), buf.as_mut_ptr()); + } + serializer.serialize_str(unsafe { std::str::from_utf8_unchecked(&buf[..36]) }) + } +} diff --git a/proxmox-uuid/src/wasm.rs b/proxmox-uuid/src/wasm.rs new file mode 100644 index 00000000..6f26deef --- /dev/null +++ b/proxmox-uuid/src/wasm.rs @@ -0,0 +1,102 @@ +pub fn uuid_generate(out: *mut [u8; 16]) { + + // TODO: implement soemthing better than this + + let time = js_sys::Date::now() as u64; + let random1 = (js_sys::Math::random() * f64::MAX) as u64; + let random2 = (js_sys::Math::random() * f64::MAX) as u64; + let random3 = (js_sys::Math::random() * f64::MAX) as u64; + let random4 = (js_sys::Math::random() * f64::MAX) as u64; + + let mut bytes1 = [0u8; 16]; + let mut bytes2 = [0u8; 16]; + let mut bytes3 = [0u8; 16]; + + bytes1[0..8].copy_from_slice(&random1.to_le_bytes()); + bytes1[8..16].copy_from_slice(&random2.to_le_bytes()); + + let random3 = random3.to_le_bytes(); + + bytes2[0..4].copy_from_slice(&random3[0..4]); + bytes2[4..12].copy_from_slice(&random4.to_le_bytes()); + bytes2[12..16].copy_from_slice(&random3[4..8]); + + bytes3[0..8].copy_from_slice(&time.to_le_bytes()); + bytes3[8..16].copy_from_slice(&time.to_le_bytes()); + + if out.is_null() { return; } + + let out = unsafe { out.as_mut().unwrap() }; + + for i in 0..16 { + let v = bytes1[i] ^ bytes2[i] ^ bytes3[i]; + out[i] = v; + } +} + +// Copied from uuid crate: https://github.com/uuid-rs/uuid.git +// adopted types to our needs + +const UPPER: [u8; 16] = [ + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', + b'C', b'D', b'E', b'F', +]; +const LOWER: [u8; 16] = [ + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', + b'c', b'd', b'e', b'f', +]; +/// The segments of a UUID's [u8; 16] corresponding to each group. +const BYTE_POSITIONS: [usize; 6] = [0, 4, 6, 8, 10, 16]; +/// The locations that hyphens are written into the buffer, after each +/// group. +const HYPHEN_POSITIONS: [usize; 4] = [8, 13, 18, 23]; + +pub fn uuid_encode( + uuid: &[u8; 16], + upper: bool, +) -> String { + let mut buffer = [0u8; 36]; + + let hex = if upper { &UPPER } else { &LOWER }; + + for group in 0..5 { + let hyphens_before = group; + for idx in BYTE_POSITIONS[group]..BYTE_POSITIONS[group + 1] { + let b = uuid[idx]; + let out_idx = hyphens_before + 2 * idx; + + buffer[out_idx] = hex[(b >> 4) as usize]; + buffer[out_idx + 1] = hex[(b & 0b1111) as usize]; + } + + if group != 4 { + buffer[HYPHEN_POSITIONS[group]] = b'-'; + } + } + + std::str::from_utf8(&mut buffer[..]) + .expect("found non-ASCII output characters while encoding a UUID") + .to_string() +} + +impl fmt::LowerHex for Uuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", uuid_encode(self.as_bytes(), false)) + } +} + +impl fmt::UpperHex for Uuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", uuid_encode(self.as_bytes(), true)) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Uuid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&uuid_encode(self.as_bytes(), false)) + } +}