Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,7 @@ impl Uuid {
/// ```
#[inline]
pub fn from_bytes_ref(bytes: &Bytes) -> &Uuid {
// SAFETY: `Bytes` and `Uuid` have the same ABI
unsafe { &*(bytes as *const Bytes as *const Uuid) }
unsafe_transmute_ref!(bytes)
}

// NOTE: There is no `from_u128_ref` because in little-endian
Expand Down
166 changes: 123 additions & 43 deletions src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

//! Adapters for alternative string formats.

use core::str::FromStr;
use core::{convert::TryInto as _, str::FromStr};

use crate::{
std::{borrow::Borrow, fmt, ptr, str},
std::{borrow::Borrow, fmt, str},
Error, Uuid, Variant,
};

Expand Down Expand Up @@ -67,25 +67,105 @@ impl fmt::UpperHex for Uuid {

/// Format a [`Uuid`] as a hyphenated string, like
/// `67e55044-10b1-426f-9247-bb680e5fe0c8`.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[repr(transparent)]
pub struct Hyphenated(Uuid);

/// Format a [`Uuid`] as a simple string, like
/// `67e5504410b1426f9247bb680e5fe0c8`.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[repr(transparent)]
pub struct Simple(Uuid);

/// Format a [`Uuid`] as a URN string, like
/// `urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8`.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[repr(transparent)]
pub struct Urn(Uuid);

/// Format a [`Uuid`] as a braced hyphenated string, like
/// `{67e55044-10b1-426f-9247-bb680e5fe0c8}`.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[repr(transparent)]
pub struct Braced(Uuid);

Expand All @@ -99,8 +179,7 @@ impl Uuid {
/// Get a borrowed [`Hyphenated`] formatter.
#[inline]
pub fn as_hyphenated(&self) -> &Hyphenated {
// SAFETY: `Uuid` and `Hyphenated` have the same ABI
unsafe { &*(self as *const Uuid as *const Hyphenated) }
unsafe_transmute_ref!(self)
}

/// Get a [`Simple`] formatter.
Expand All @@ -112,8 +191,7 @@ impl Uuid {
/// Get a borrowed [`Simple`] formatter.
#[inline]
pub fn as_simple(&self) -> &Simple {
// SAFETY: `Uuid` and `Simple` have the same ABI
unsafe { &*(self as *const Uuid as *const Simple) }
unsafe_transmute_ref!(self)
}

/// Get a [`Urn`] formatter.
Expand All @@ -125,8 +203,7 @@ impl Uuid {
/// Get a borrowed [`Urn`] formatter.
#[inline]
pub fn as_urn(&self) -> &Urn {
// SAFETY: `Uuid` and `Urn` have the same ABI
unsafe { &*(self as *const Uuid as *const Urn) }
unsafe_transmute_ref!(self)
}

/// Get a [`Braced`] formatter.
Expand All @@ -138,8 +215,7 @@ impl Uuid {
/// Get a borrowed [`Braced`] formatter.
#[inline]
pub fn as_braced(&self) -> &Braced {
// SAFETY: `Uuid` and `Braced` have the same ABI
unsafe { &*(self as *const Uuid as *const Braced) }
unsafe_transmute_ref!(self)
}
}

Expand Down Expand Up @@ -194,58 +270,62 @@ const fn format_hyphenated(src: &[u8; 16], upper: bool) -> [u8; 36] {
#[inline]
fn encode_simple<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
let buf = &mut buffer[..Simple::LENGTH];
let dst = buf.as_mut_ptr();
let buf: &mut [u8; Simple::LENGTH] = buf.try_into().unwrap();
*buf = format_simple(src, upper);

// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
// SAFETY: The encoded buffer is ASCII encoded
unsafe {
ptr::write(dst.cast(), format_simple(src, upper));
str::from_utf8_unchecked_mut(buf)
}
unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_hyphenated<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
let buf = &mut buffer[..Hyphenated::LENGTH];
let dst = buf.as_mut_ptr();
let buf: &mut [u8; Hyphenated::LENGTH] = buf.try_into().unwrap();
*buf = format_hyphenated(src, upper);

// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
// SAFETY: The encoded buffer is ASCII encoded
unsafe {
ptr::write(dst.cast(), format_hyphenated(src, upper));
str::from_utf8_unchecked_mut(buf)
}
unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_braced<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
let buf = &mut buffer[..Braced::LENGTH];
buf[0] = b'{';
buf[Braced::LENGTH - 1] = b'}';
let buf = &mut buffer[..Hyphenated::LENGTH + 2];
let buf: &mut [u8; Hyphenated::LENGTH + 2] = buf.try_into().unwrap();

#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(zerocopy::IntoBytes)
)]
#[repr(C)]
struct Braced {
open_curly: u8,
hyphenated: [u8; Hyphenated::LENGTH],
close_curly: u8,
}

// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
// SAFETY: The encoded buffer is ASCII encoded
unsafe {
let dst = buf.as_mut_ptr().add(1);
let braced = Braced {
open_curly: b'{',
hyphenated: format_hyphenated(src, upper),
close_curly: b'}',
};

ptr::write(dst.cast(), format_hyphenated(src, upper));
str::from_utf8_unchecked_mut(buf)
}
*buf = unsafe_transmute!(braced);

// SAFETY: The encoded buffer is ASCII encoded
unsafe { str::from_utf8_unchecked_mut(buf) }
}

#[inline]
fn encode_urn<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
let buf = &mut buffer[..Urn::LENGTH];
buf[..9].copy_from_slice(b"urn:uuid:");

// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
// SAFETY: The encoded buffer is ASCII encoded
unsafe {
let dst = buf.as_mut_ptr().add(9);
let dst = &mut buf[9..(9 + Hyphenated::LENGTH)];
let dst: &mut [u8; Hyphenated::LENGTH] = dst.try_into().unwrap();
*dst = format_hyphenated(src, upper);

ptr::write(dst.cast(), format_hyphenated(src, upper));
str::from_utf8_unchecked_mut(buf)
}
// SAFETY: The encoded buffer is ASCII encoded
unsafe { str::from_utf8_unchecked_mut(buf) }
}

impl Hyphenated {
Expand Down
22 changes: 11 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ extern crate std;
#[macro_use]
extern crate core as std;

#[macro_use]
mod macros;

mod builder;
mod error;
mod non_nil;
Expand Down Expand Up @@ -267,9 +270,6 @@ mod sha1;

mod external;

#[macro_use]
mod macros;

#[doc(hidden)]
#[cfg(feature = "macro-diagnostics")]
pub extern crate uuid_macro_internal;
Expand Down Expand Up @@ -438,6 +438,14 @@ pub enum Variant {
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
// NOTE: Also check `NonNilUuid` when ading new derives here
#[cfg_attr(
feature = "borsh",
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
)]
#[cfg_attr(
feature = "bytemuck",
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
)]
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
Expand All @@ -448,14 +456,6 @@ pub enum Variant {
zerocopy::Unaligned
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
)]
#[cfg_attr(
feature = "bytemuck",
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
)]
pub struct Uuid(Bytes);

impl Uuid {
Expand Down
30 changes: 30 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,33 @@ define_uuid_macro! {
///
/// [uuid::Uuid]: https://docs.rs/uuid/*/uuid/struct.Uuid.html
}

// Internal macros

// These `transmute` macros are a stepping stone towards `zerocopy` integration.
// When the `zerocopy` feature is enabled, which it is in CI, the transmutes are
// checked by it

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
macro_rules! unsafe_transmute_ref(
($e:expr) => { unsafe { core::mem::transmute::<&_, &_>($e) } }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
macro_rules! unsafe_transmute_ref(
($e:expr) => { zerocopy::transmute_ref!($e) }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
macro_rules! unsafe_transmute(
($e:expr) => { unsafe { core::mem::transmute::<_, _>($e) } }
);

// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
macro_rules! unsafe_transmute(
($e:expr) => { zerocopy::transmute!($e) }
);
Loading