Skip to content

Commit 661283a

Browse files
committed
Allowing simd acceleration and added testcases
Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
1 parent 8a5bf85 commit 661283a

File tree

2 files changed

+76
-21
lines changed

2 files changed

+76
-21
lines changed

mozjs/src/conversions.rs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,56 @@ impl FromJSValConvertible for f64 {
526526
}
527527
}
528528

529+
#[cfg(all(
530+
any(target_arch = "x86", target_arch = "x86_64"),
531+
any(target_feature = "avx", target_feature = "sse2")
532+
))]
533+
/// Copies chars to the string using simd instructions
534+
unsafe fn fast_copy(chars: &[u8]) -> String {
535+
use std::arch::x86_64;
536+
537+
let mut s = String::with_capacity(chars.len());
538+
let mut count = 0;
539+
let num_iter = chars.len() / 32;
540+
let v = s.as_mut_vec();
541+
(0..num_iter).for_each(|i| {
542+
if cfg!(target_feature = "avx") {
543+
let simd =
544+
x86_64::_mm256_loadu_si256(chars.as_ptr().add(i * 32) as *const x86_64::__m256i);
545+
x86_64::_mm256_storeu_si256(v.as_ptr().add(i * 32) as *mut x86_64::__m256i, simd);
546+
count += 32;
547+
} else {
548+
let simd = x86_64::_mm_load_si128(chars.as_ptr().add(i * 16) as *const x86_64::__m128i);
549+
x86_64::_mm_store_si128(v.as_ptr().add(i * 16) as *mut x86_64::__m128i, simd);
550+
count += 16;
551+
}
552+
});
553+
554+
// bytes that do not fit into the simd instruction
555+
for i in count..chars.len() {
556+
chars.as_ptr().add(i).copy_to(v.as_mut_ptr().add(i), 1);
557+
count += 1;
558+
}
559+
v.set_len(count);
560+
s
561+
}
562+
563+
#[cfg(not(any(
564+
all(
565+
any(target_arch = "x86", target_arch = "x86_64"),
566+
any(target_feature = "avx", target_feature = "sse"),
567+
)
568+
)))]
569+
/// Copies chars to the string using a slower method instructions
570+
unsafe fn fast_copy(chars: &[u8]) -> String {
571+
let mut v = Vec::with_capacity(chars.len() * 2);
572+
v.set_len(chars.len() * 2);
573+
let real_size = encoding_rs::mem::convert_latin1_to_utf8(chars, v.as_mut_slice());
574+
575+
v.truncate(real_size);
576+
String::from_utf8_unchecked(v)
577+
}
578+
529579
/// Converts a `JSString`, encoded in "Latin1" (i.e. U+0000-U+00FF encoded as 0x00-0xFF) into a
530580
/// `String`.
531581
pub unsafe fn latin1_to_string(cx: *mut JSContext, s: *mut JSString) -> String {
@@ -536,12 +586,7 @@ pub unsafe fn latin1_to_string(cx: *mut JSContext, s: *mut JSString) -> String {
536586
assert!(!chars.is_null());
537587

538588
let chars = slice::from_raw_parts(chars, length as usize);
539-
let mut v = Vec::with_capacity(length * 2);
540-
v.set_len(length * 2);
541-
542-
let real_size = encoding_rs::mem::convert_latin1_to_utf8(chars, v.as_mut_slice());
543-
v.truncate(real_size);
544-
String::from_utf8_unchecked(v)
589+
fast_copy(chars)
545590
}
546591

547592
/// Converts a `JSString` into a `String`, regardless of used encoding.

mozjs/tests/external_string.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,13 @@ fn external_string() {
3636
));
3737
let _ac = JSAutoRealm::new(context, global.get());
3838

39-
let latin1_base = "test latin-1";
40-
let latin1_boxed = latin1_base.as_bytes().to_vec().into_boxed_slice();
41-
let latin1_chars = Box::into_raw(latin1_boxed).cast::<u8>();
42-
43-
let callbacks = CreateJSExternalStringCallbacks(
44-
&EXTERNAL_STRING_CALLBACKS_TRAPS,
45-
latin1_base.len() as *mut c_void,
46-
);
47-
rooted!(in(context) let latin1_jsstr = JS_NewExternalStringLatin1(
48-
context,
49-
latin1_chars,
50-
latin1_base.len(),
51-
callbacks
52-
));
53-
assert_eq!(jsstr_to_string(context, latin1_jsstr.get()), latin1_base);
39+
test_latin1_string(context, "test latin1");
40+
test_latin1_string(context, "abcdefghijklmnop"); // exactly 16 bytes
41+
test_latin1_string(context, "abcdefghijklmnopq"); // 18 bytes
42+
test_latin1_string(context, "abcdefghijklmno"); // 15 bytes
43+
test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcdef"); //32 bytes
44+
test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcde"); //31 bytes
45+
test_latin1_string(context, "abcdefghijklmnopqrstuvwxyzabcdefg"); //33 bytes
5446

5547
let utf16_base = "test utf-16 $€ \u{10437}\u{24B62}";
5648
let utf16_boxed = utf16_base
@@ -74,6 +66,24 @@ fn external_string() {
7466
}
7567
}
7668

69+
#[cfg(test)]
70+
unsafe fn test_latin1_string(context: *mut mozjs::jsapi::JSContext, latin1_base: &str) {
71+
let latin1_boxed = latin1_base.as_bytes().to_vec().into_boxed_slice();
72+
let latin1_chars = Box::into_raw(latin1_boxed).cast::<u8>();
73+
74+
let callbacks = CreateJSExternalStringCallbacks(
75+
&EXTERNAL_STRING_CALLBACKS_TRAPS,
76+
latin1_base.len() as *mut c_void,
77+
);
78+
rooted!(in(context) let latin1_jsstr = JS_NewExternalStringLatin1(
79+
context,
80+
latin1_chars,
81+
latin1_base.len(),
82+
callbacks
83+
));
84+
assert_eq!(jsstr_to_string(context, latin1_jsstr.get()), latin1_base);
85+
}
86+
7787
static EXTERNAL_STRING_CALLBACKS_TRAPS: JSExternalStringCallbacksTraps =
7888
JSExternalStringCallbacksTraps {
7989
latin1Finalize: Some(latin1::finalize),

0 commit comments

Comments
 (0)