#[inline]
unsafe fn read_record(ptr: *const u8) -> Option<&'static [u8]> {
if ptr.is_null() {
return None;
}
let len = unsafe { core::ptr::read_unaligned(ptr.cast::<u32>()) } as usize;
let payload = unsafe { ptr.add(4) };
Some(unsafe { core::slice::from_raw_parts(payload, len) })
}
struct StrContainsArenaIc {
last_haystack: core::sync::atomic::AtomicPtr<u8>,
last_needle: core::sync::atomic::AtomicPtr<u8>,
last_result: core::sync::atomic::AtomicI32,
}
static STR_CONTAINS_ARENA_IC: StrContainsArenaIc = StrContainsArenaIc {
last_haystack: core::sync::atomic::AtomicPtr::new(core::ptr::null_mut()),
last_needle: core::sync::atomic::AtomicPtr::new(core::ptr::null_mut()),
last_result: core::sync::atomic::AtomicI32::new(0),
};
#[no_mangle]
pub unsafe extern "C" fn relon_llvm_str_contains_arena(
haystack_ptr: *const u8,
needle_ptr: *const u8,
) -> i32 {
if let Some(r) = ic_hit_slot(haystack_ptr, needle_ptr) {
return r;
}
unsafe { str_contains_arena_slow(haystack_ptr, needle_ptr) }
}
#[inline(always)]
fn ic_hit_slot(haystack_ptr: *const u8, needle_ptr: *const u8) -> Option<i32> {
use core::sync::atomic::Ordering;
if haystack_ptr.is_null() || needle_ptr.is_null() {
return None;
}
let cached_haystack = STR_CONTAINS_ARENA_IC.last_haystack.load(Ordering::Relaxed);
if !std::ptr::eq(cached_haystack, haystack_ptr) {
return None;
}
let cached_needle = STR_CONTAINS_ARENA_IC.last_needle.load(Ordering::Relaxed);
if !std::ptr::eq(cached_needle, needle_ptr) {
return None;
}
Some(STR_CONTAINS_ARENA_IC.last_result.load(Ordering::Relaxed))
}
#[cold]
#[inline(never)]
unsafe fn str_contains_arena_slow(haystack_ptr: *const u8, needle_ptr: *const u8) -> i32 {
use core::sync::atomic::Ordering;
let h_bytes = match unsafe { read_record(haystack_ptr) } {
Some(s) => s,
None => return 0,
};
let n_bytes = match unsafe { read_record(needle_ptr) } {
Some(s) => s,
None => return 0,
};
let result = compute_contains(h_bytes, n_bytes);
STR_CONTAINS_ARENA_IC
.last_haystack
.store(haystack_ptr as *mut u8, Ordering::Relaxed);
STR_CONTAINS_ARENA_IC
.last_needle
.store(needle_ptr as *mut u8, Ordering::Relaxed);
STR_CONTAINS_ARENA_IC
.last_result
.store(result, Ordering::Relaxed);
result
}
#[inline]
fn compute_contains(h_bytes: &[u8], n_bytes: &[u8]) -> i32 {
if n_bytes.is_empty() {
return 1;
}
if n_bytes.len() > h_bytes.len() {
return 0;
}
if n_bytes.len() == 1 {
let needle_byte = n_bytes[0];
return i32::from(h_bytes.contains(&needle_byte));
}
let h_str = match core::str::from_utf8(h_bytes) {
Ok(s) => s,
Err(_) => return 0,
};
let n_str = match core::str::from_utf8(n_bytes) {
Ok(s) => s,
Err(_) => return 0,
};
i32::from(h_str.contains(n_str))
}
#[inline]
pub fn relon_llvm_str_contains_arena_addr() -> usize {
relon_llvm_str_contains_arena as *const () as usize
}
pub const RELON_LLVM_STR_CONTAINS_ARENA_SYMBOL: &str = "relon_llvm_str_contains_arena";
#[no_mangle]
pub unsafe extern "C" fn relon_llvm_f64_to_str(bits: i64, dest: *mut u8) -> i32 {
use relon_ir::float_str::{format_f64_display, FLOAT_TO_STR_MAX_PAYLOAD};
if dest.is_null() {
return -1;
}
let mut payload = [0u8; FLOAT_TO_STR_MAX_PAYLOAD];
let len = match format_f64_display(bits as u64, &mut payload) {
Some(len) => len,
None => return -1,
};
unsafe {
core::ptr::write_unaligned(dest.cast::<u32>(), len as u32);
core::ptr::copy_nonoverlapping(payload.as_ptr(), dest.add(4), len);
}
len as i32
}
#[inline]
pub fn relon_llvm_f64_to_str_addr() -> usize {
relon_llvm_f64_to_str as *const () as usize
}
pub const RELON_LLVM_F64_TO_STR_SYMBOL: &str = "relon_llvm_f64_to_str";
#[cfg(test)]
mod tests {
use super::*;
fn build_two_records(haystack: &[u8], needle: &[u8]) -> (Vec<u8>, usize, usize) {
let mut buf = Vec::with_capacity(4 + haystack.len() + 4 + needle.len() + 16);
let h_off = buf.len();
buf.extend_from_slice(&(haystack.len() as u32).to_le_bytes());
buf.extend_from_slice(haystack);
let n_off = buf.len();
buf.extend_from_slice(&(needle.len() as u32).to_le_bytes());
buf.extend_from_slice(needle);
(buf, h_off, n_off)
}
fn lock_and_reset_ic() -> std::sync::MutexGuard<'static, ()> {
use core::sync::atomic::Ordering;
static IC_TEST_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
let guard = IC_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
STR_CONTAINS_ARENA_IC
.last_haystack
.store(core::ptr::null_mut(), Ordering::Relaxed);
STR_CONTAINS_ARENA_IC
.last_needle
.store(core::ptr::null_mut(), Ordering::Relaxed);
STR_CONTAINS_ARENA_IC
.last_result
.store(0, Ordering::Relaxed);
guard
}
#[test]
fn matches_short_needle() {
let _ic = lock_and_reset_ic();
let (buf, h_off, n_off) = build_two_records(b"axb", b"x");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 1);
}
#[test]
fn misses_when_needle_absent() {
let _ic = lock_and_reset_ic();
let (buf, h_off, n_off) = build_two_records(b"abc", b"z");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 0);
}
#[test]
fn matches_long_haystack_tail_needle() {
let _ic = lock_and_reset_ic();
let mut haystack = vec![b'a'; 255];
haystack.push(b'x');
let (buf, h_off, n_off) = build_two_records(&haystack, b"x");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 1);
}
#[test]
fn empty_needle_always_matches() {
let _ic = lock_and_reset_ic();
let (buf, h_off, n_off) = build_two_records(b"anything", b"");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 1);
}
#[test]
fn empty_haystack_nonempty_needle_misses() {
let _ic = lock_and_reset_ic();
let (buf, h_off, n_off) = build_two_records(b"", b"x");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 0);
}
#[test]
fn null_pointers_return_zero() {
let r = unsafe { relon_llvm_str_contains_arena(core::ptr::null(), core::ptr::null()) };
assert_eq!(r, 0);
}
#[test]
fn multibyte_utf8_needle() {
let _ic = lock_and_reset_ic();
let haystack = "hello 🦀 world".as_bytes();
let needle = "🦀".as_bytes();
let (buf, h_off, n_off) = build_two_records(haystack, needle);
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r, 1);
}
#[test]
fn address_helper_is_stable() {
let a = relon_llvm_str_contains_arena_addr();
let b = relon_llvm_str_contains_arena_addr();
assert_eq!(a, b);
assert!(a != 0);
}
#[test]
fn ic_returns_cached_result_on_repeated_call() {
let _ic = lock_and_reset_ic();
let (mut buf, h_off, n_off) = build_two_records(b"axb", b"x");
let h_ptr = unsafe { buf.as_ptr().add(h_off) };
let n_ptr = unsafe { buf.as_ptr().add(n_off) };
let r1 = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r1, 1);
let payload_off = h_off + 4;
buf[payload_off] = b'q';
buf[payload_off + 1] = b'q';
buf[payload_off + 2] = b'q';
let r2 = unsafe { relon_llvm_str_contains_arena(h_ptr, n_ptr) };
assert_eq!(r2, 1, "IC must return cached result on identical pointers");
}
#[test]
fn ic_misses_on_distinct_pointers() {
let _ic = lock_and_reset_ic();
let (buf_a, h_off_a, n_off_a) = build_two_records(b"axb", b"x");
let (buf_b, h_off_b, n_off_b) = build_two_records(b"qqq", b"z");
let h_a = unsafe { buf_a.as_ptr().add(h_off_a) };
let n_a = unsafe { buf_a.as_ptr().add(n_off_a) };
let h_b = unsafe { buf_b.as_ptr().add(h_off_b) };
let n_b = unsafe { buf_b.as_ptr().add(n_off_b) };
let r1 = unsafe { relon_llvm_str_contains_arena(h_a, n_a) };
let r2 = unsafe { relon_llvm_str_contains_arena(h_b, n_b) };
let r3 = unsafe { relon_llvm_str_contains_arena(h_a, n_a) };
assert_eq!(r1, 1);
assert_eq!(r2, 0);
assert_eq!(r3, 1);
}
fn shim_render_f64(v: f64) -> String {
let mut record = vec![0u8; relon_ir::float_str::FLOAT_TO_STR_RECORD_SIZE as usize];
let len = unsafe { relon_llvm_f64_to_str(v.to_bits() as i64, record.as_mut_ptr()) };
assert!(len >= 0, "shim failed for {v}");
let header = u32::from_le_bytes(record[0..4].try_into().unwrap());
assert_eq!(header as i32, len, "header length must match return value");
String::from_utf8(record[4..4 + len as usize].to_vec()).unwrap()
}
#[test]
fn f64_shim_matches_display_battery() {
for v in [
1.0,
-0.0,
0.0,
0.1,
567.34,
1e300,
5e-324,
-5e-324,
f64::NAN,
f64::INFINITY,
f64::NEG_INFINITY,
f64::MAX,
f64::MIN_POSITIVE,
] {
assert_eq!(shim_render_f64(v), format!("{v}"), "bytes drift for {v:?}");
}
assert_eq!(shim_render_f64(1.0), "1");
assert_eq!(shim_render_f64(-0.0), "-0");
assert_eq!(shim_render_f64(f64::NAN), "NaN");
assert_eq!(shim_render_f64(f64::INFINITY), "inf");
assert_eq!(shim_render_f64(f64::NEG_INFINITY), "-inf");
assert_eq!(shim_render_f64(-5e-324).len(), 327);
}
#[test]
fn f64_shim_null_dest_returns_negative() {
let r = unsafe { relon_llvm_f64_to_str(1.0f64.to_bits() as i64, core::ptr::null_mut()) };
assert_eq!(r, -1);
}
#[test]
fn f64_addr_helper_is_stable() {
let a = relon_llvm_f64_to_str_addr();
let b = relon_llvm_f64_to_str_addr();
assert_eq!(a, b);
assert!(a != 0);
}
}