#![allow(unsafe_code)]
use std::borrow::Cow;
#[inline]
pub fn capitalize_first_simd(s: &str) -> Cow<'_, str> {
if s.is_empty() {
return Cow::Borrowed(s);
}
let bytes = s.as_bytes();
let first_byte = bytes[0];
if first_byte < 0x80 {
if first_byte.is_ascii_uppercase() {
return Cow::Borrowed(s);
} else if first_byte.is_ascii_lowercase() {
if is_ascii_simd(&bytes[1..]) {
let mut buf = bytes.to_vec();
buf[0] = first_byte - b'a' + b'A';
return Cow::Owned(
String::from_utf8(buf)
.expect("capitalize_first_simd: ASCII bytes preserve UTF-8 validity"),
);
} else {
return capitalize_first_scalar(s);
}
} else {
return Cow::Borrowed(s);
}
}
capitalize_first_scalar(s)
}
#[inline]
pub fn find_separator_simd(haystack: &[u8], needle: u8) -> Option<usize> {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("sse2") {
return unsafe { find_separator_sse2(haystack, needle) };
}
}
haystack.iter().position(|&b| b == needle)
}
#[inline]
fn capitalize_first_scalar(s: &str) -> Cow<'_, str> {
let first_byte = s.as_bytes()[0];
if first_byte < 0x80 {
if first_byte.is_ascii_uppercase() {
Cow::Borrowed(s)
} else if first_byte.is_ascii_lowercase() {
let mut buf = s.as_bytes().to_vec();
buf[0] = first_byte - b'a' + b'A';
Cow::Owned(
String::from_utf8(buf)
.expect("capitalize_first_scalar: ASCII bytes preserve UTF-8 validity"),
)
} else {
Cow::Borrowed(s)
}
} else {
let mut chars = s.chars();
match chars.next() {
Some(first) => {
let upper: String = first.to_uppercase().collect();
if upper.len() == 1 && upper.as_bytes()[0] == first_byte {
Cow::Borrowed(s)
} else {
Cow::Owned(upper + chars.as_str())
}
}
None => Cow::Borrowed(s),
}
}
}
#[inline]
fn is_ascii_simd(bytes: &[u8]) -> bool {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("sse2") {
return unsafe { is_ascii_sse2(bytes) };
}
}
bytes.iter().all(|&b| b < 0x80)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn is_ascii_sse2(bytes: &[u8]) -> bool {
use core::arch::x86_64::*;
let mut i = 0;
let high_bit = _mm_set1_epi8(0x80_u8 as i8);
while i + 16 <= bytes.len() {
let chunk = _mm_loadu_si128(bytes.as_ptr().add(i) as *const __m128i);
let masked = _mm_and_si128(chunk, high_bit);
if _mm_movemask_epi8(masked) != 0 {
return false;
}
i += 16;
}
while i < bytes.len() {
if bytes[i] >= 0x80 {
return false;
}
i += 1;
}
true
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn find_separator_sse2(haystack: &[u8], needle: u8) -> Option<usize> {
use core::arch::x86_64::*;
let mut i = 0;
let needle_vec = _mm_set1_epi8(needle as i8);
while i + 16 <= haystack.len() {
let chunk = _mm_loadu_si128(haystack.as_ptr().add(i) as *const __m128i);
let eq = _mm_cmpeq_epi8(chunk, needle_vec);
let mask = _mm_movemask_epi8(eq) as u32;
if mask != 0 {
return Some(i + mask.trailing_zeros() as usize);
}
i += 16;
}
while i < haystack.len() {
if haystack[i] == needle {
return Some(i);
}
i += 1;
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capitalize_empty() {
assert_eq!(capitalize_first_simd(""), "");
}
#[test]
fn test_capitalize_single_byte() {
assert_eq!(capitalize_first_simd("a"), "A");
assert_eq!(capitalize_first_simd("A"), "A");
assert_eq!(capitalize_first_simd("1"), "1");
assert_eq!(capitalize_first_simd("_"), "_");
}
#[test]
fn test_capitalize_ascii_upper() {
assert_eq!(capitalize_first_simd("Customer"), "Customer");
assert_eq!(capitalize_first_simd("Index"), "Index");
}
#[test]
fn test_capitalize_ascii_lower() {
assert_eq!(capitalize_first_simd("customer"), "Customer");
assert_eq!(capitalize_first_simd("index"), "Index");
assert_eq!(capitalize_first_simd("get_list"), "Get_list");
}
#[test]
fn test_capitalize_non_ascii_chinese() {
assert_eq!(capitalize_first_simd("中文"), "中文");
assert_eq!(capitalize_first_simd("客户"), "客户");
}
#[test]
fn test_capitalize_non_ascii_emoji() {
assert_eq!(capitalize_first_simd("😀hello"), "😀hello");
}
#[test]
fn test_capitalize_mixed() {
assert_eq!(capitalize_first_simd("a中文"), "A中文");
assert_eq!(capitalize_first_simd("A中文"), "A中文");
}
#[test]
fn test_capitalize_long_ascii() {
let s = "a".repeat(100);
let expected = "A".to_string() + &"a".repeat(99);
assert_eq!(capitalize_first_simd(&s), expected);
}
#[test]
fn test_find_separator_basic() {
assert_eq!(find_separator_simd(b"hello/world", b'/'), Some(5));
assert_eq!(find_separator_simd(b"/hello", b'/'), Some(0));
assert_eq!(find_separator_simd(b"hello/", b'/'), Some(5));
}
#[test]
fn test_find_separator_not_found() {
assert_eq!(find_separator_simd(b"hello", b'/'), None);
assert_eq!(find_separator_simd(b"", b'/'), None);
}
#[test]
fn test_find_separator_multiple() {
assert_eq!(find_separator_simd(b"a/b/c", b'/'), Some(1));
}
#[test]
fn test_find_separator_long() {
let s = b"aaaaaaaaaaaaaaaa/b"; assert_eq!(find_separator_simd(s, b'/'), Some(16));
}
#[test]
fn test_find_separator_aligned_16() {
let s = b"aaaaaaaaaaaaaaa/"; assert_eq!(find_separator_simd(s, b'/'), Some(15));
}
#[test]
fn test_find_separator_cross_boundary() {
let s = b"aaaaaaaaaaaaaaaaa/b"; assert_eq!(find_separator_simd(s, b'/'), Some(17));
}
#[test]
fn test_is_ascii_empty() {
assert!(is_ascii_simd(b""));
}
#[test]
fn test_is_ascii_pure() {
assert!(is_ascii_simd(b"hello"));
assert!(is_ascii_simd(b"abcdefghijklmnopqrstuvwxyz"));
}
#[test]
fn test_is_ascii_with_non_ascii() {
assert!(!is_ascii_simd(&[0x80]));
assert!(!is_ascii_simd(b"hello\xff"));
}
#[test]
fn test_is_ascii_long() {
let s = vec![b'a'; 100];
assert!(is_ascii_simd(&s));
let mut s = vec![b'a'; 100];
s[50] = 0x80;
assert!(!is_ascii_simd(&s));
}
}