Skip to content

Commit d653207

Browse files
committed
Add the non-zero optimization to Atom
1 parent a317539 commit d653207

File tree

1 file changed

+35
-23
lines changed

1 file changed

+35
-23
lines changed

src/atom.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::fmt;
1818
use std::hash::{Hash, Hasher};
1919
use std::marker::PhantomData;
2020
use std::mem;
21+
use std::num::NonZeroU64;
2122
use std::ops;
2223
use std::slice;
2324
use std::str;
@@ -110,8 +111,7 @@ impl StringCache {
110111
ptr
111112
}
112113

113-
fn remove(&mut self, key: u64) {
114-
let ptr = key as *mut StringCacheEntry;
114+
fn remove(&mut self, ptr: *mut StringCacheEntry) {
115115
let bucket_index = {
116116
let value: &StringCacheEntry = unsafe { &*ptr };
117117
debug_assert!(value.ref_count.load(SeqCst) == 0);
@@ -236,7 +236,7 @@ pub type DefaultAtom = Atom<EmptyStaticAtomSet>;
236236
#[derive(PartialEq, Eq)]
237237
// NOTE: Deriving PartialEq requires that a given string must always be interned the same way.
238238
pub struct Atom<Static> {
239-
unsafe_data: u64,
239+
unsafe_data: NonZeroU64,
240240
phantom: PhantomData<Static>,
241241
}
242242

@@ -266,7 +266,10 @@ impl<Static> Atom<Static> {
266266
#[doc(hidden)]
267267
pub const fn pack_static(n: u32) -> Self {
268268
Self {
269-
unsafe_data: (STATIC_TAG as u64) | ((n as u64) << STATIC_SHIFT_BITS),
269+
unsafe_data: unsafe {
270+
// STATIC_TAG ensure this is non-zero
271+
NonZeroU64::new_unchecked((STATIC_TAG as u64) | ((n as u64) << STATIC_SHIFT_BITS))
272+
},
270273
phantom: PhantomData,
271274
}
272275
}
@@ -281,7 +284,7 @@ impl<Static: StaticAtomSet> Atom<Static> {
281284
/// Return the internal repersentation. For testing.
282285
#[doc(hidden)]
283286
pub fn unsafe_data(&self) -> u64 {
284-
self.unsafe_data
287+
self.unsafe_data.get()
285288
}
286289

287290
/// Return true if this is a static Atom. For testing.
@@ -322,7 +325,7 @@ impl<Static: StaticAtomSet> Atom<Static> {
322325
let entry = entry as *mut StringCacheEntry;
323326
u64_hash_as_u32(unsafe { (*entry).hash })
324327
}
325-
Inline(..) => u64_hash_as_u32(self.unsafe_data),
328+
Inline(..) => u64_hash_as_u32(self.unsafe_data.get()),
326329
}
327330
}
328331
}
@@ -405,7 +408,7 @@ impl<Static: StaticAtomSet> Clone for Atom<Static> {
405408
#[inline(always)]
406409
fn clone(&self) -> Self {
407410
unsafe {
408-
match from_packed_dynamic(self.unsafe_data) {
411+
match from_packed_dynamic(self.unsafe_data.get()) {
409412
Some(entry) => {
410413
let entry = entry as *mut StringCacheEntry;
411414
(*entry).ref_count.fetch_add(1, SeqCst);
@@ -425,11 +428,14 @@ impl<Static> Drop for Atom<Static> {
425428
fn drop(&mut self) {
426429
// Out of line to guide inlining.
427430
fn drop_slow<Static>(this: &mut Atom<Static>) {
428-
STRING_CACHE.lock().unwrap().remove(this.unsafe_data);
431+
STRING_CACHE
432+
.lock()
433+
.unwrap()
434+
.remove(this.unsafe_data.get() as *mut StringCacheEntry);
429435
}
430436

431437
unsafe {
432-
match from_packed_dynamic(self.unsafe_data) {
438+
match from_packed_dynamic(self.unsafe_data.get()) {
433439
Some(entry) => {
434440
let entry = entry as *mut StringCacheEntry;
435441
if (*entry).ref_count.fetch_sub(1, SeqCst) == 1 {
@@ -608,9 +614,9 @@ enum UnpackedAtom {
608614
}
609615

610616
#[inline(always)]
611-
fn inline_atom_slice(x: &u64) -> &[u8] {
617+
fn inline_atom_slice(x: &NonZeroU64) -> &[u8] {
612618
unsafe {
613-
let x: *const u64 = x;
619+
let x: *const NonZeroU64 = x;
614620
let mut data = x as *const u8;
615621
// All except the lowest byte, which is first in little-endian, last in big-endian.
616622
if cfg!(target_endian = "little") {
@@ -643,22 +649,24 @@ impl UnpackedAtom {
643649
match self {
644650
Static(n) => Atom::pack_static(n),
645651
Dynamic(p) => {
646-
let unsafe_data = p as u64;
647-
debug_assert!(0 == unsafe_data & TAG_MASK);
652+
let data = p as u64;
653+
debug_assert!(0 == data & TAG_MASK);
648654
Atom {
649-
unsafe_data,
655+
// Callers are responsible for calling this with a valid, non-null pointer
656+
unsafe_data: NonZeroU64::new_unchecked(data),
650657
phantom: PhantomData,
651658
}
652659
}
653660
Inline(len, buf) => {
654661
debug_assert!((len as usize) <= MAX_INLINE_LEN);
655-
let mut unsafe_data: u64 = (INLINE_TAG as u64) | ((len as u64) << 4);
662+
let mut data: u64 = (INLINE_TAG as u64) | ((len as u64) << 4);
656663
{
657-
let dest = inline_atom_slice_mut(&mut unsafe_data);
664+
let dest = inline_atom_slice_mut(&mut data);
658665
dest.copy_from_slice(&buf)
659666
}
660667
Atom {
661-
unsafe_data,
668+
// INLINE_TAG ensures this is never zero
669+
unsafe_data: NonZeroU64::new_unchecked(data),
662670
phantom: PhantomData,
663671
}
664672
}
@@ -667,14 +675,14 @@ impl UnpackedAtom {
667675

668676
/// Unpack a key, extracting information from a single u64 into useable structs.
669677
#[inline(always)]
670-
unsafe fn from_packed(data: u64) -> UnpackedAtom {
678+
unsafe fn from_packed(data: NonZeroU64) -> UnpackedAtom {
671679
debug_assert!(DYNAMIC_TAG == 0); // Dynamic is untagged
672680

673-
match (data & TAG_MASK) as u8 {
674-
DYNAMIC_TAG => Dynamic(data as *mut ()),
675-
STATIC_TAG => Static((data >> STATIC_SHIFT_BITS) as u32),
681+
match (data.get() & TAG_MASK) as u8 {
682+
DYNAMIC_TAG => Dynamic(data.get() as *mut ()),
683+
STATIC_TAG => Static((data.get() >> STATIC_SHIFT_BITS) as u32),
676684
INLINE_TAG => {
677-
let len = ((data & 0xf0) >> 4) as usize;
685+
let len = ((data.get() & 0xf0) >> 4) as usize;
678686
debug_assert!(len <= MAX_INLINE_LEN);
679687
let mut buf: [u8; 7] = [0; 7];
680688
let src = inline_atom_slice(&data);
@@ -701,7 +709,7 @@ unsafe fn from_packed_dynamic(data: u64) -> Option<*mut ()> {
701709
///
702710
/// It's undefined behavior to call this on a non-inline atom!!
703711
#[inline(always)]
704-
unsafe fn inline_orig_bytes<'a>(data: &'a u64) -> &'a [u8] {
712+
unsafe fn inline_orig_bytes<'a>(data: &'a NonZeroU64) -> &'a [u8] {
705713
match UnpackedAtom::from_packed(*data) {
706714
Inline(len, _) => {
707715
let src = inline_atom_slice(&data);
@@ -736,6 +744,10 @@ mod tests {
736744
8
737745
}
738746
);
747+
assert_eq!(
748+
mem::size_of::<Option<DefaultAtom>>(),
749+
mem::size_of::<DefaultAtom>(),
750+
);
739751
assert_eq!(
740752
mem::size_of::<super::StringCacheEntry>(),
741753
8 + 4 * mem::size_of::<usize>()

0 commit comments

Comments
 (0)