From 6611088e559267cb0bb8c359bfeb1bec822fd5af Mon Sep 17 00:00:00 2001 From: Colden Cullen Date: Fri, 14 Aug 2026 17:42:32 -0700 Subject: [PATCH] feat: implement AsRef<[u8]> for Atom Also adds disambiguating functions as_str() and as_bytes() to mirror std apis --- integration-tests/src/bench.rs | 30 +++++++++---- integration-tests/src/lib.rs | 77 ++++++++++++++++++++++++++++++---- src/atom.rs | 50 +++++++++++++--------- src/trivial_impls.rs | 10 ++++- 4 files changed, 131 insertions(+), 36 deletions(-) diff --git a/integration-tests/src/bench.rs b/integration-tests/src/bench.rs index 45e7199..04ec795 100644 --- a/integration-tests/src/bench.rs +++ b/integration-tests/src/bench.rs @@ -83,13 +83,25 @@ macro_rules! bench_one ( } ); - (as_ref $x:expr, $_y:expr) => ( + (as_str $x:expr, $_y:expr) => ( #[bench] - fn as_ref_x_1000(b: &mut Bencher) { + fn as_str_x_1000(b: &mut Bencher) { let x = $x; b.iter(|| { for _ in 0..1000 { - black_box(x.as_ref()); + black_box(x.as_str()); + } + }); + } + ); + + (as_bytes $x:expr, $_y:expr) => ( + #[bench] + fn as_bytes_x_1000(b: &mut Bencher) { + let x = $x; + b.iter(|| { + for _ in 0..1000 { + black_box(x.as_bytes()); } }); } @@ -152,22 +164,22 @@ bench_all!([eq ne lt clone_string] for medium_string = "xyzzy01", "xyzzy02"); bench_all!([eq ne lt clone_string] for longer_string = super::longer_dynamic_a, super::longer_dynamic_b); -bench_all!([eq ne intern as_ref clone is_static lt] +bench_all!([eq ne intern as_str as_bytes clone is_static lt] for static_atom = test_atom!("defaults"), test_atom!("font-weight")); -bench_all!([intern as_ref clone is_inline] +bench_all!([intern as_str as_bytes clone is_inline] for short_inline_atom = mk("e"), mk("f")); -bench_all!([eq ne intern as_ref clone is_inline lt] +bench_all!([eq ne intern as_str as_bytes clone is_inline lt] for medium_inline_atom = mk("xyzzy01"), mk("xyzzy02")); -bench_all!([intern as_ref clone is_dynamic] +bench_all!([intern as_str as_bytes clone is_dynamic] for min_dynamic_atom = mk("xyzzy001"), mk("xyzzy002")); -bench_all!([eq ne intern as_ref clone is_dynamic lt] +bench_all!([eq ne intern as_str as_bytes clone is_dynamic lt] for longer_dynamic_atom = mk(super::longer_dynamic_a), mk(super::longer_dynamic_b)); -bench_all!([intern as_ref clone is_static] +bench_all!([intern as_str as_bytes clone is_static] for static_at_runtime = mk("defaults"), mk("font-weight")); bench_all!([ne lt x_static y_inline] diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index a788d93..d4e92e8 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -22,24 +22,87 @@ include!(concat!(env!("OUT_DIR"), "/test_atom.rs")); pub type Atom = TestAtom; #[test] -fn test_as_slice() { +fn test_as_str() { let s0 = Atom::from(""); - assert!(s0.as_ref() == ""); + assert!(s0.as_str() == ""); let s1 = Atom::from("class"); - assert!(s1.as_ref() == "class"); + assert!(s1.as_str() == "class"); let i0 = Atom::from("blah"); - assert!(i0.as_ref() == "blah"); + assert!(i0.as_str() == "blah"); let s0 = Atom::from("BLAH"); - assert!(s0.as_ref() == "BLAH"); + assert!(s0.as_str() == "BLAH"); let d0 = Atom::from("zzzzzzzzzz"); - assert!(d0.as_ref() == "zzzzzzzzzz"); + assert!(d0.as_str() == "zzzzzzzzzz"); let d1 = Atom::from("ZZZZZZZZZZ"); - assert!(d1.as_ref() == "ZZZZZZZZZZ"); + assert!(d1.as_str() == "ZZZZZZZZZZ"); +} + +#[test] +fn test_as_bytes() { + let s0 = Atom::from(""); + assert!(s0.as_bytes() == b""); + + let s1 = Atom::from("class"); + assert!(s1.as_bytes() == b"class"); + + let i0 = Atom::from("blah"); + assert!(i0.as_bytes() == b"blah"); + + let s0 = Atom::from("BLAH"); + assert!(s0.as_bytes() == b"BLAH"); + + let d0 = Atom::from("zzzzzzzzzz"); + assert!(d0.as_bytes() == b"zzzzzzzzzz"); + + let d1 = Atom::from("ZZZZZZZZZZ"); + assert!(d1.as_bytes() == b"ZZZZZZZZZZ"); +} + +#[test] +fn test_as_ref_str() { + let s0 = Atom::from(""); + assert!(AsRef::::as_ref(&s0) == ""); + + let s1 = Atom::from("class"); + assert!(AsRef::::as_ref(&s1) == "class"); + + let i0 = Atom::from("blah"); + assert!(AsRef::::as_ref(&i0) == "blah"); + + let s0 = Atom::from("BLAH"); + assert!(AsRef::::as_ref(&s0) == "BLAH"); + + let d0 = Atom::from("zzzzzzzzzz"); + assert!(AsRef::::as_ref(&d0) == "zzzzzzzzzz"); + + let d1 = Atom::from("ZZZZZZZZZZ"); + assert!(AsRef::::as_ref(&d1) == "ZZZZZZZZZZ"); +} + +#[test] +fn test_as_ref_bytes() { + let s0 = Atom::from(""); + assert!(AsRef::<[u8]>::as_ref(&s0) == b""); + + let s1 = Atom::from("class"); + assert!(AsRef::<[u8]>::as_ref(&s1) == b"class"); + + let i0 = Atom::from("blah"); + assert!(AsRef::<[u8]>::as_ref(&i0) == b"blah"); + + let s0 = Atom::from("BLAH"); + assert!(AsRef::<[u8]>::as_ref(&s0) == b"BLAH"); + + let d0 = Atom::from("zzzzzzzzzz"); + assert!(AsRef::<[u8]>::as_ref(&d0) == b"zzzzzzzzzz"); + + let d1 = Atom::from("ZZZZZZZZZZ"); + assert!(AsRef::<[u8]>::as_ref(&d1) == b"ZZZZZZZZZZ"); } #[test] diff --git a/src/atom.rs b/src/atom.rs index 5a8aa7f..db383b1 100644 --- a/src/atom.rs +++ b/src/atom.rs @@ -193,6 +193,18 @@ impl Atom { Err(hash) } } + + /// Get a reference to the underlying str. + #[inline] + pub fn as_str(&self) -> &str { + self // auto-deref + } + + /// Get a reference to the bytes of the underlying str. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.as_str().as_bytes() + } } impl Default for Atom { @@ -317,7 +329,7 @@ impl PartialOrd for Atom { if self.unsafe_data == other.unsafe_data { return Some(Equal); } - self.as_ref().partial_cmp(other.as_ref()) + self.as_str().partial_cmp(other.as_ref()) } } @@ -327,7 +339,7 @@ impl Ord for Atom { if self.unsafe_data == other.unsafe_data { return Equal; } - self.as_ref().cmp(other.as_ref()) + self.as_str().cmp(other.as_ref()) } } @@ -392,24 +404,24 @@ impl Atom { #[inline(always)] fn inline_atom_slice(x: &NonZeroU64) -> &[u8] { - let x: *const NonZeroU64 = x; - let mut data = x as *const u8; - // All except the lowest byte, which is first in little-endian, last in big-endian. - if cfg!(target_endian = "little") { - data = unsafe { data.offset(1) }; - } - let len = 7; - unsafe { slice::from_raw_parts(data, len) } + let x: *const NonZeroU64 = x; + let mut data = x as *const u8; + // All except the lowest byte, which is first in little-endian, last in big-endian. + if cfg!(target_endian = "little") { + data = unsafe { data.offset(1) }; + } + let len = 7; + unsafe { slice::from_raw_parts(data, len) } } #[inline(always)] -fn inline_atom_slice_mut(x: &mut u64) -> &mut [u8] { - let x: *mut u64 = x; - let mut data = x as *mut u8; - // All except the lowest byte, which is first in little-endian, last in big-endian. - if cfg!(target_endian = "little") { - data = unsafe { data.offset(1) }; - } - let len = 7; - unsafe { slice::from_raw_parts_mut(data, len) } +fn inline_atom_slice_mut(x: &mut u64) -> &mut [u8] { + let x: *mut u64 = x; + let mut data = x as *mut u8; + // All except the lowest byte, which is first in little-endian, last in big-endian. + if cfg!(target_endian = "little") { + data = unsafe { data.offset(1) }; + } + let len = 7; + unsafe { slice::from_raw_parts_mut(data, len) } } diff --git a/src/trivial_impls.rs b/src/trivial_impls.rs index 960dde0..05c2af1 100644 --- a/src/trivial_impls.rs +++ b/src/trivial_impls.rs @@ -65,8 +65,16 @@ impl fmt::Display for Atom { } impl AsRef for Atom { + #[inline] fn as_ref(&self) -> &str { - self + self.as_str() + } +} + +impl AsRef<[u8]> for Atom { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() } }