Skip to content

Commit 6d847c7

Browse files
authored
Merge pull request #841 from uuid-rs/chore/unsafe-cleanup
Unsafe cleanup
2 parents 60a49eb + 675cccc commit 6d847c7

File tree

4 files changed

+165
-56
lines changed

4 files changed

+165
-56
lines changed

src/builder.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,7 @@ impl Uuid {
431431
/// ```
432432
#[inline]
433433
pub fn from_bytes_ref(bytes: &Bytes) -> &Uuid {
434-
// SAFETY: `Bytes` and `Uuid` have the same ABI
435-
unsafe { &*(bytes as *const Bytes as *const Uuid) }
434+
unsafe_transmute_ref!(bytes)
436435
}
437436

438437
// NOTE: There is no `from_u128_ref` because in little-endian

src/fmt.rs

Lines changed: 123 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111

1212
//! Adapters for alternative string formats.
1313
14-
use core::str::FromStr;
14+
use core::{convert::TryInto as _, str::FromStr};
1515

1616
use crate::{
17-
std::{borrow::Borrow, fmt, ptr, str},
17+
std::{borrow::Borrow, fmt, str},
1818
Error, Uuid, Variant,
1919
};
2020

@@ -67,25 +67,105 @@ impl fmt::UpperHex for Uuid {
6767

6868
/// Format a [`Uuid`] as a hyphenated string, like
6969
/// `67e55044-10b1-426f-9247-bb680e5fe0c8`.
70-
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
70+
#[derive(
71+
Clone,
72+
Copy,
73+
Debug,
74+
Default,
75+
Eq,
76+
Hash,
77+
Ord,
78+
PartialEq,
79+
PartialOrd,
80+
)]
81+
#[cfg_attr(
82+
all(uuid_unstable, feature = "zerocopy"),
83+
derive(
84+
zerocopy::IntoBytes,
85+
zerocopy::FromBytes,
86+
zerocopy::KnownLayout,
87+
zerocopy::Immutable,
88+
zerocopy::Unaligned
89+
)
90+
)]
7191
#[repr(transparent)]
7292
pub struct Hyphenated(Uuid);
7393

7494
/// Format a [`Uuid`] as a simple string, like
7595
/// `67e5504410b1426f9247bb680e5fe0c8`.
76-
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
96+
#[derive(
97+
Clone,
98+
Copy,
99+
Debug,
100+
Default,
101+
Eq,
102+
Hash,
103+
Ord,
104+
PartialEq,
105+
PartialOrd,
106+
)]
107+
#[cfg_attr(
108+
all(uuid_unstable, feature = "zerocopy"),
109+
derive(
110+
zerocopy::IntoBytes,
111+
zerocopy::FromBytes,
112+
zerocopy::KnownLayout,
113+
zerocopy::Immutable,
114+
zerocopy::Unaligned
115+
)
116+
)]
77117
#[repr(transparent)]
78118
pub struct Simple(Uuid);
79119

80120
/// Format a [`Uuid`] as a URN string, like
81121
/// `urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8`.
82-
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
122+
#[derive(
123+
Clone,
124+
Copy,
125+
Debug,
126+
Default,
127+
Eq,
128+
Hash,
129+
Ord,
130+
PartialEq,
131+
PartialOrd,
132+
)]
133+
#[cfg_attr(
134+
all(uuid_unstable, feature = "zerocopy"),
135+
derive(
136+
zerocopy::IntoBytes,
137+
zerocopy::FromBytes,
138+
zerocopy::KnownLayout,
139+
zerocopy::Immutable,
140+
zerocopy::Unaligned
141+
)
142+
)]
83143
#[repr(transparent)]
84144
pub struct Urn(Uuid);
85145

86146
/// Format a [`Uuid`] as a braced hyphenated string, like
87147
/// `{67e55044-10b1-426f-9247-bb680e5fe0c8}`.
88-
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
148+
#[derive(
149+
Clone,
150+
Copy,
151+
Debug,
152+
Default,
153+
Eq,
154+
Hash,
155+
Ord,
156+
PartialEq,
157+
PartialOrd,
158+
)]
159+
#[cfg_attr(
160+
all(uuid_unstable, feature = "zerocopy"),
161+
derive(
162+
zerocopy::IntoBytes,
163+
zerocopy::FromBytes,
164+
zerocopy::KnownLayout,
165+
zerocopy::Immutable,
166+
zerocopy::Unaligned
167+
)
168+
)]
89169
#[repr(transparent)]
90170
pub struct Braced(Uuid);
91171

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

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

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

132209
/// Get a [`Braced`] formatter.
@@ -138,8 +215,7 @@ impl Uuid {
138215
/// Get a borrowed [`Braced`] formatter.
139216
#[inline]
140217
pub fn as_braced(&self) -> &Braced {
141-
// SAFETY: `Uuid` and `Braced` have the same ABI
142-
unsafe { &*(self as *const Uuid as *const Braced) }
218+
unsafe_transmute_ref!(self)
143219
}
144220
}
145221

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

199-
// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
200276
// SAFETY: The encoded buffer is ASCII encoded
201-
unsafe {
202-
ptr::write(dst.cast(), format_simple(src, upper));
203-
str::from_utf8_unchecked_mut(buf)
204-
}
277+
unsafe { str::from_utf8_unchecked_mut(buf) }
205278
}
206279

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

212-
// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
213286
// SAFETY: The encoded buffer is ASCII encoded
214-
unsafe {
215-
ptr::write(dst.cast(), format_hyphenated(src, upper));
216-
str::from_utf8_unchecked_mut(buf)
217-
}
287+
unsafe { str::from_utf8_unchecked_mut(buf) }
218288
}
219289

220290
#[inline]
221291
fn encode_braced<'b>(src: &[u8; 16], buffer: &'b mut [u8], upper: bool) -> &'b mut str {
222-
let buf = &mut buffer[..Braced::LENGTH];
223-
buf[0] = b'{';
224-
buf[Braced::LENGTH - 1] = b'}';
292+
let buf = &mut buffer[..Hyphenated::LENGTH + 2];
293+
let buf: &mut [u8; Hyphenated::LENGTH + 2] = buf.try_into().unwrap();
294+
295+
#[cfg_attr(
296+
all(uuid_unstable, feature = "zerocopy"),
297+
derive(zerocopy::IntoBytes)
298+
)]
299+
#[repr(C)]
300+
struct Braced {
301+
open_curly: u8,
302+
hyphenated: [u8; Hyphenated::LENGTH],
303+
close_curly: u8,
304+
}
225305

226-
// SAFETY: `buf` is guaranteed to be at least `LEN` bytes
227-
// SAFETY: The encoded buffer is ASCII encoded
228-
unsafe {
229-
let dst = buf.as_mut_ptr().add(1);
306+
let braced = Braced {
307+
open_curly: b'{',
308+
hyphenated: format_hyphenated(src, upper),
309+
close_curly: b'}',
310+
};
230311

231-
ptr::write(dst.cast(), format_hyphenated(src, upper));
232-
str::from_utf8_unchecked_mut(buf)
233-
}
312+
*buf = unsafe_transmute!(braced);
313+
314+
// SAFETY: The encoded buffer is ASCII encoded
315+
unsafe { str::from_utf8_unchecked_mut(buf) }
234316
}
235317

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

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

246-
ptr::write(dst.cast(), format_hyphenated(src, upper));
247-
str::from_utf8_unchecked_mut(buf)
248-
}
327+
// SAFETY: The encoded buffer is ASCII encoded
328+
unsafe { str::from_utf8_unchecked_mut(buf) }
249329
}
250330

251331
impl Hyphenated {

src/lib.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ extern crate std;
223223
#[macro_use]
224224
extern crate core as std;
225225

226+
#[macro_use]
227+
mod macros;
228+
226229
mod builder;
227230
mod error;
228231
mod non_nil;
@@ -267,9 +270,6 @@ mod sha1;
267270

268271
mod external;
269272

270-
#[macro_use]
271-
mod macros;
272-
273273
#[doc(hidden)]
274274
#[cfg(feature = "macro-diagnostics")]
275275
pub extern crate uuid_macro_internal;
@@ -438,6 +438,14 @@ pub enum Variant {
438438
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
439439
#[repr(transparent)]
440440
// NOTE: Also check `NonNilUuid` when ading new derives here
441+
#[cfg_attr(
442+
feature = "borsh",
443+
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
444+
)]
445+
#[cfg_attr(
446+
feature = "bytemuck",
447+
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
448+
)]
441449
#[cfg_attr(
442450
all(uuid_unstable, feature = "zerocopy"),
443451
derive(
@@ -448,14 +456,6 @@ pub enum Variant {
448456
zerocopy::Unaligned
449457
)
450458
)]
451-
#[cfg_attr(
452-
feature = "borsh",
453-
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
454-
)]
455-
#[cfg_attr(
456-
feature = "bytemuck",
457-
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
458-
)]
459459
pub struct Uuid(Bytes);
460460

461461
impl Uuid {

src/macros.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,33 @@ define_uuid_macro! {
8686
///
8787
/// [uuid::Uuid]: https://docs.rs/uuid/*/uuid/struct.Uuid.html
8888
}
89+
90+
// Internal macros
91+
92+
// These `transmute` macros are a stepping stone towards `zerocopy` integration.
93+
// When the `zerocopy` feature is enabled, which it is in CI, the transmutes are
94+
// checked by it
95+
96+
// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
97+
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
98+
macro_rules! unsafe_transmute_ref(
99+
($e:expr) => { unsafe { core::mem::transmute::<&_, &_>($e) } }
100+
);
101+
102+
// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
103+
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
104+
macro_rules! unsafe_transmute_ref(
105+
($e:expr) => { zerocopy::transmute_ref!($e) }
106+
);
107+
108+
// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
109+
#[cfg(not(all(uuid_unstable, feature = "zerocopy")))]
110+
macro_rules! unsafe_transmute(
111+
($e:expr) => { unsafe { core::mem::transmute::<_, _>($e) } }
112+
);
113+
114+
// SAFETY: Callers must ensure this call would be safe when handled by zerocopy
115+
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
116+
macro_rules! unsafe_transmute(
117+
($e:expr) => { zerocopy::transmute!($e) }
118+
);

0 commit comments

Comments
 (0)