Skip to content
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

Make as_slice() an implementation detail #359

Closed
wants to merge 2 commits into from
Closed
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
4 changes: 4 additions & 0 deletions bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ harness = false
name = "comparison"
harness = false

[[bench]]
name = "micro"
harness = false

[[bench]]
name = "random"
harness = false
85 changes: 85 additions & 0 deletions bench/benches/micro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::time::{
Duration,
Instant,
};

use compact_str::CompactString;
use criterion::{
black_box,
criterion_group,
criterion_main,
Criterion,
};

const EMPTY: &str = "";
const SMALL: &str = "small";
const BIG: &str = "This string has thirty-four chars.";
const HUGE: &str = include_str!("../data/moby10b.txt");

macro_rules! benchmarks_simple {
($($method:ident),+) => {
$(
fn $method(c: &mut Criterion) {
benchmarks_simple!(@ c $method EMPTY SMALL BIG HUGE);
}
)+
};

(@ $c:ident $method:ident $($length:ident)+) => {$(
$c.bench_function(stringify!($method $length), |b| {
let string = CompactString::new(black_box($length));
b.iter(|| {
let _ = black_box(black_box(&string).$method());
})
});
)+}
}

macro_rules! benchmarks_complex {
($($method:ident [$expr:expr])+) => {
$(
fn $method(c: &mut Criterion) {
benchmarks_complex!(@ c $method [$expr] EMPTY SMALL BIG HUGE);
}
)+
};

(@ $c:ident $method:ident [$expr:expr] $($length:ident)+) => {$(
$c.bench_function(stringify!($method $length), |b| {
b.iter_custom(|iters| {
let mut duration = Duration::default();
for _ in 0..iters {
let mut string = CompactString::new(black_box($length));
let start = Instant::now();
$expr(black_box(&mut string));
duration += start.elapsed();
drop(string);
}
duration
});
});

$c.bench_function(stringify!($method $length repeated), |b| {
let mut string = CompactString::new(black_box($length));
b.iter(|| {
$expr(black_box(&mut string));
});
});
)+}
}

benchmarks_simple!(as_bytes, as_str, capacity, is_empty, is_heap_allocated, len);

benchmarks_complex! {
as_mut_bytes [|s: &mut CompactString| { let _ = black_box(unsafe { s.as_mut_bytes() }); }]
as_mut_ptr [|s: &mut CompactString| { let _ = black_box(s.as_mut_ptr()); }]
as_mut_str [|s: &mut CompactString| { let _ = black_box(s.as_mut_str()); }]
}

criterion_group! {
micro,
as_bytes, as_str, capacity, is_empty, is_heap_allocated, len,
as_mut_bytes, as_mut_ptr, as_mut_str,
}

criterion_main!(micro);
10 changes: 5 additions & 5 deletions compact_str/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ impl CompactString {
/// ```
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0.as_slice()[..self.len()]
self.as_str().as_bytes()
}

// TODO: Implement a `try_as_mut_slice(...)` that will fail if it results in cloning?
Expand Down Expand Up @@ -996,16 +996,16 @@ impl CompactString {
unsafe { self.set_len(new_len) };
}

/// Converts a [`CompactString`] to a raw pointer.
/// Return a reference to the underlying buffer
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.0.as_slice().as_ptr()
self.0.as_ptr()
}

/// Converts a mutable [`CompactString`] to a raw pointer.
/// Return a mutable reference to the underlying buffer
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
unsafe { self.0.as_mut_buf().as_mut_ptr() }
self.0.as_mut_ptr()
}

/// Insert string character at an index.
Expand Down
87 changes: 63 additions & 24 deletions compact_str/src/repr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ impl Repr {

/// Returns the string content, and only the string content, as a slice of bytes.
#[inline]
pub fn as_slice(&self) -> &[u8] {
fn as_slice(&self) -> &[u8] {
// initially has the value of the stack pointer, conditionally becomes the heap pointer
let mut pointer = self as *const Self as *const u8;
let heap_pointer = self.0 as *const u8;
Expand Down Expand Up @@ -439,6 +439,33 @@ impl Repr {
len == 0
}

/// Return a reference to the underlying buffer
#[inline]
pub fn as_ptr(&self) -> *const u8 {
// initially has the value of the stack pointer, conditionally becomes the heap pointer
let mut pointer = ensure_read_ptr(self as *const Self as *const u8);
let heap_pointer = ensure_read_ptr(self.0 as *const u8);
if self.last_byte() >= HEAP_MASK {
pointer = heap_pointer;
}
pointer
}

/// Return a mutable reference to the underlying buffer
pub fn as_mut_ptr(&mut self) -> *mut u8 {
if let Some(static_string) = self.as_static_str() {
*self = Repr::new(static_string).unwrap_with_msg();
}

// initially has the value of the stack pointer, conditionally becomes the heap pointer
let mut pointer = ensure_read_ptr_mut(self as *mut Self as *mut u8);
let heap_pointer = ensure_read_ptr_mut(self.0 as *mut u8);
if self.last_byte() >= HEAP_MASK {
pointer = heap_pointer;
}
pointer
}

/// Returns the overall capacity of the underlying buffer
#[inline]
pub fn capacity(&self) -> usize {
Expand Down Expand Up @@ -784,30 +811,42 @@ impl Extend<String> for Repr {
}
}

/// Returns the supplied value, and ensures that the value is eagerly loaded into a register.
#[inline(always)]
fn ensure_read(value: usize) -> usize {
// SAFETY: This assembly instruction is a noop that only affects the instruction ordering.
#[cfg(all(
not(miri),
any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "loongarch",
target_arch = "riscv",
)
))]
unsafe {
core::arch::asm!(
"/* {value} */",
value = in(reg) value,
options(nomem, nostack),
);
};
macro_rules! ensure_read {
($($ident:ident $([$constaint:tt])? ($($type:tt)+);)+) => {$(
/// Returns the supplied value, and ensures that the value is eagerly loaded into a
/// register.
#[inline(always)]
fn $ident $(<$constaint>)? (value: $($type)+) -> $($type)+ {
// SAFETY: This assembly instruction is a noop that only affects the instruction
// ordering.
#[cfg(all(
not(miri),
any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "loongarch",
target_arch = "riscv",
)
))]
unsafe {
core::arch::asm!(
"/* {value} */",
value = in(reg) value,
options(nostack, preserves_flags, readonly),
);
};

value
}
)+};
}

value
ensure_read! {
ensure_read(usize);
ensure_read_ptr[T](*const T);
ensure_read_ptr_mut[T](*mut T);
}

#[cfg(test)]
Expand Down
7 changes: 7 additions & 0 deletions compact_str/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1961,3 +1961,10 @@ fn test_is_empty() {
}
}
}

#[test]
fn test_drain_from_static_string() {
let mut compact =
CompactString::const_new("I am a very long string. Well, longer than 24 bytes at least.");
let _ = compact.drain(4..=10);
}
Loading