Skip to content

Remove some unsafe; stabilize zerocopy #833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
7 changes: 1 addition & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,8 @@ version = "2"
optional = true
version = "1.1.3"

# Public (unstable): Used in `zerocopy` derive
# Unstable: also need RUSTFLAGS="--cfg uuid_unstable" to work
# This feature may break between releases, or be removed entirely before
# stabilization.
# See: https://github.com/uuid-rs/uuid/issues/588
# Public: Used in trait impls on `Uuid`
[dependencies.zerocopy]
optional = true
version = "0.8"
features = ["derive"]

Expand Down
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) }
zerocopy::transmute_ref!(bytes)
}

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

//! Adapters for alternative string formats.

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

use zerocopy::{transmute_ref, FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};

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

Expand Down Expand Up @@ -67,25 +69,85 @@ 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,
IntoBytes,
FromBytes,
KnownLayout,
Immutable,
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,
IntoBytes,
FromBytes,
KnownLayout,
Immutable,
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,
IntoBytes,
FromBytes,
KnownLayout,
Immutable,
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,
IntoBytes,
FromBytes,
KnownLayout,
Immutable,
Unaligned,
)]
#[repr(transparent)]
pub struct Braced(Uuid);

Expand All @@ -99,8 +161,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) }
transmute_ref!(self)
}

/// Get a [`Simple`] formatter.
Expand All @@ -112,8 +173,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) }
transmute_ref!(self)
}

/// Get a [`Urn`] formatter.
Expand All @@ -125,8 +185,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) }
transmute_ref!(self)
}

/// Get a [`Braced`] formatter.
Expand All @@ -138,8 +197,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) }
transmute_ref!(self)
}
}

Expand Down Expand Up @@ -194,58 +252,59 @@ 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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice cleanup 👍

*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();

#[derive(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 = zerocopy::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
17 changes: 7 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,16 +438,6 @@ pub enum Variant {
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
// NOTE: Also check `NonNilUuid` when ading new derives here
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
Expand All @@ -456,6 +446,13 @@ pub enum Variant {
feature = "bytemuck",
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
)]
#[derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned,
)]
pub struct Uuid(Bytes);

impl Uuid {
Expand Down