Merge branch 'memory'

This commit is contained in:
Henri Sivonen
2018-01-18 10:46:44 +02:00
7 changed files with 3314 additions and 143 deletions
+6
View File
@@ -10,6 +10,8 @@ cargo-fuzz = true
[dependencies.encoding_rs]
path = ".."
[dependencies.safe_encoding_rs_mem]
path = "../../safe_encoding_rs_mem"
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
@@ -27,3 +29,7 @@ path = "fuzzers/fuzz_encodings.rs"
[[bin]]
name = "fuzz_labels"
path = "fuzzers/fuzz_labels.rs"
[[bin]]
name = "fuzz_mem"
path = "fuzzers/fuzz_mem.rs"
+342
View File
@@ -0,0 +1,342 @@
// Copyright 2015-2016 Mozilla Foundation. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
extern crate encoding_rs;
extern crate safe_encoding_rs_mem;
fn check_utf8(data: &[u8]) {
if let Err(_) = ::std::str::from_utf8(data) {
panic!("Bogus UTF-8.");
}
}
fn check_utf16(data: &[u16]) {
let mut prev_was_high_surrogate = false;
for unit in data {
if *unit >= 0xD800 && *unit <= 0xDBFF {
assert!(!prev_was_high_surrogate);
prev_was_high_surrogate = true;
} else if *unit >= 0xDC00 && *unit <= 0xDFFF {
assert!(prev_was_high_surrogate);
prev_was_high_surrogate = false;
} else {
assert!(!prev_was_high_surrogate);
prev_was_high_surrogate = false;
}
}
assert!(!prev_was_high_surrogate);
}
fn as_u16_slice(data: &[u8]) -> &[u16] {
unsafe {
let ptr = data.as_ptr();
let len = data.len();
if len < 2 {
return ::std::slice::from_raw_parts(ptr as *const u16, 0);
}
let (adj_ptr, adj_len) = if ptr as usize & 1 == 0 {
(ptr, len / 2)
} else {
(ptr.offset(1), (len - 1) / 2)
};
::std::slice::from_raw_parts(adj_ptr as *const u16, adj_len)
}
}
trait EigthOrSixteen: Clone {
fn zero() -> Self;
}
impl EigthOrSixteen for u8 {
fn zero() -> u8 {
0
}
}
impl EigthOrSixteen for u16 {
fn zero() -> u16 {
0
}
}
fn vec_with_len<T: EigthOrSixteen>(len: usize) -> Vec<T> {
let mut vec: Vec<T> = Vec::with_capacity(len);
vec.resize(len, T::zero());
vec
}
fn string_with_len(len: usize) -> String {
let mut s = String::with_capacity(len);
for _ in 0..len {
s.push('\u{0}');
}
s
}
fn fuzz_is_ascii(data: &[u8]) {
assert_eq!(encoding_rs::mem::is_ascii(data), safe_encoding_rs_mem::is_ascii(data));
}
fn fuzz_is_basic_latin(data: &[u16]) {
assert_eq!(encoding_rs::mem::is_basic_latin(data), safe_encoding_rs_mem::is_basic_latin(data));
}
fn fuzz_is_utf8_latin1(data: &[u8]) {
assert_eq!(encoding_rs::mem::is_utf8_latin1(data), safe_encoding_rs_mem::is_utf8_latin1(data));
}
fn fuzz_is_str_latin1(data: &[u8]) {
if let Ok(s) = std::str::from_utf8(data) {
assert_eq!(encoding_rs::mem::is_str_latin1(s), safe_encoding_rs_mem::is_str_latin1(s));
}
}
fn fuzz_is_utf16_latin1(data: &[u16]) {
assert_eq!(encoding_rs::mem::is_utf16_latin1(data), safe_encoding_rs_mem::is_utf16_latin1(data));
}
fn fuzz_convert_utf8_to_utf16(data: &[u8]) {
let needed = data.len() + 1;
let mut dst = vec_with_len::<u16>(needed);
let mut safe_dst = vec_with_len::<u16>(needed);
let len = encoding_rs::mem::convert_utf8_to_utf16(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_utf8_to_utf16(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
check_utf16(&dst[..]);
}
fn fuzz_convert_str_to_utf16(data: &[u8]) {
if let Ok(s) = std::str::from_utf8(data) {
let needed = s.len();
let mut dst = vec_with_len::<u16>(needed);
let mut safe_dst = vec_with_len::<u16>(needed);
let len = encoding_rs::mem::convert_str_to_utf16(s, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_str_to_utf16(s, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
check_utf16(&dst[..]);
}
}
fn fuzz_convert_utf16_to_utf8(data: &[u16]) {
let needed = data.len() * 3 + 1;
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
let len = encoding_rs::mem::convert_utf16_to_utf8(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_utf16_to_utf8(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
check_utf8(&dst[..]);
}
fn fuzz_convert_utf16_to_str(data: &[u16]) {
let needed = data.len() * 3 + 1;
let mut dst = string_with_len(needed);
let mut safe_dst = string_with_len(needed);
let len = encoding_rs::mem::convert_utf16_to_str(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_utf16_to_str(data, &mut safe_dst[..]);
check_utf8(dst.as_bytes());
check_utf8(safe_dst.as_bytes());
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
fn fuzz_convert_latin1_to_utf16(data: &[u8]) {
let needed = data.len();
let mut dst = vec_with_len::<u16>(needed);
let mut safe_dst = vec_with_len::<u16>(needed);
encoding_rs::mem::convert_latin1_to_utf16(data, &mut dst[..]);
safe_encoding_rs_mem::convert_latin1_to_utf16(data, &mut safe_dst[..]);
assert_eq!(dst, safe_dst);
check_utf16(&dst[..]);
}
fn fuzz_convert_latin1_to_utf8(data: &[u8]) {
let needed = data.len() * 2;
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
let len = encoding_rs::mem::convert_latin1_to_utf8(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_latin1_to_utf8(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
check_utf8(&dst[..]);
}
fn fuzz_convert_latin1_to_str(data: &[u8]) {
let needed = data.len() * 2;
let mut dst = string_with_len(needed);
let mut safe_dst = string_with_len(needed);
let len = encoding_rs::mem::convert_latin1_to_str(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_latin1_to_str(data, &mut safe_dst[..]);
check_utf8(dst.as_bytes());
check_utf8(safe_dst.as_bytes());
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
fn fuzz_convert_utf8_to_latin1_lossy(data: &[u8]) {
let needed = data.len();
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
let len = encoding_rs::mem::convert_utf8_to_latin1_lossy(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::convert_utf8_to_latin1_lossy(data, &mut safe_dst[..]);
if safe_encoding_rs_mem::is_utf8_latin1(data) {
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
}
fn fuzz_convert_utf16_to_latin1_lossy(data: &[u16]) {
let needed = data.len();
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
encoding_rs::mem::convert_utf16_to_latin1_lossy(data, &mut dst[..]);
safe_encoding_rs_mem::convert_utf16_to_latin1_lossy(data, &mut safe_dst[..]);
if safe_encoding_rs_mem::is_utf16_latin1(data) {
assert_eq!(dst, safe_dst);
}
}
fn fuzz_utf16_valid_up_to(data: &[u16]) {
let up_to = encoding_rs::mem::utf16_valid_up_to(data);
let safe_up_to = safe_encoding_rs_mem::utf16_valid_up_to(data);
assert_eq!(up_to, safe_up_to);
}
fn fuzz_ensure_utf16_validity(data: &[u16]) {
let needed = data.len();
let mut dst = vec_with_len::<u16>(needed);
let mut safe_dst = vec_with_len::<u16>(needed);
dst.copy_from_slice(data);
safe_dst.copy_from_slice(data);
encoding_rs::mem::ensure_utf16_validity(&mut dst[..]);
safe_encoding_rs_mem::ensure_utf16_validity(&mut safe_dst[..]);
assert_eq!(dst, safe_dst);
check_utf16(&dst[..]);
}
fn fuzz_copy_ascii_to_ascii(data: &[u8]) {
let needed = data.len();
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
let len = encoding_rs::mem::copy_ascii_to_ascii(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::copy_ascii_to_ascii(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
fn fuzz_copy_ascii_to_basic_latin(data: &[u8]) {
let needed = data.len();
let mut dst = vec_with_len::<u16>(needed);
let mut safe_dst = vec_with_len::<u16>(needed);
let len = encoding_rs::mem::copy_ascii_to_basic_latin(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::copy_ascii_to_basic_latin(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
fn fuzz_copy_basic_latin_to_ascii(data: &[u16]) {
let needed = data.len();
let mut dst = vec_with_len::<u8>(needed);
let mut safe_dst = vec_with_len::<u8>(needed);
let len = encoding_rs::mem::copy_basic_latin_to_ascii(data, &mut dst[..]);
let safe_len = safe_encoding_rs_mem::copy_basic_latin_to_ascii(data, &mut safe_dst[..]);
dst.truncate(len);
safe_dst.truncate(safe_len);
assert_eq!(len, safe_len);
assert_eq!(dst, safe_dst);
}
fn fuzz_is_utf8_bidi(data: &[u8]) {
assert_eq!(encoding_rs::mem::is_utf8_bidi(data), safe_encoding_rs_mem::is_utf8_bidi(data));
}
fn fuzz_is_str_bidi(data: &[u8]) {
if let Ok(s) = std::str::from_utf8(data) {
assert_eq!(encoding_rs::mem::is_str_bidi(s), safe_encoding_rs_mem::is_str_bidi(s));
}
}
fn fuzz_is_utf16_bidi(data: &[u16]) {
assert_eq!(encoding_rs::mem::is_utf16_bidi(data), safe_encoding_rs_mem::is_utf16_bidi(data));
}
// is_char_bidi() and is_utf16_code_unit_bidi() are tested exhaustively, so no need to fuzz them.as_u16_slice
fn fuzz_check_utf8_for_latin1_and_bidi(data: &[u8]) {
assert_eq!(encoding_rs::mem::check_utf8_for_latin1_and_bidi(data), safe_encoding_rs_mem::check_utf8_for_latin1_and_bidi(data));
}
fn fuzz_check_str_for_latin1_and_bidi(data: &[u8]) {
if let Ok(s) = std::str::from_utf8(data) {
assert_eq!(encoding_rs::mem::check_str_for_latin1_and_bidi(s), safe_encoding_rs_mem::check_str_for_latin1_and_bidi(s));
}
}
fn fuzz_check_utf16_for_latin1_and_bidi(data: &[u16]) {
assert_eq!(encoding_rs::mem::check_utf16_for_latin1_and_bidi(data), safe_encoding_rs_mem::check_utf16_for_latin1_and_bidi(data));
}
fuzz_target!(
|data: &[u8]| {
if let Some(first) = data.first() {
match *first {
0 => fuzz_is_ascii(&data[1..]),
1 => fuzz_is_basic_latin(as_u16_slice(&data[1..])),
2 => fuzz_is_utf8_latin1(&data[1..]),
3 => fuzz_is_str_latin1(&data[1..]),
4 => fuzz_is_utf16_latin1(as_u16_slice(&data[1..])),
5 => fuzz_convert_utf8_to_utf16(&data[1..]),
6 => fuzz_convert_str_to_utf16(&data[1..]),
7 => fuzz_convert_utf16_to_utf8(as_u16_slice(&data[1..])),
8 => fuzz_convert_utf16_to_str(as_u16_slice(&data[1..])),
9 => fuzz_convert_latin1_to_utf16(&data[1..]),
10 => fuzz_convert_latin1_to_utf8(&data[1..]),
11 => fuzz_convert_latin1_to_str(&data[1..]),
12 => fuzz_convert_utf8_to_latin1_lossy(&data[1..]),
13 => fuzz_convert_utf16_to_latin1_lossy(as_u16_slice(&data[1..])),
14 => fuzz_utf16_valid_up_to(as_u16_slice(&data[1..])),
15 => fuzz_ensure_utf16_validity(as_u16_slice(&data[1..])),
16 => fuzz_copy_ascii_to_ascii(&data[1..]),
17 => fuzz_copy_ascii_to_basic_latin(&data[1..]),
18 => fuzz_copy_basic_latin_to_ascii(as_u16_slice(&data[1..])),
19 => fuzz_is_utf8_bidi(&data[1..]),
20 => fuzz_is_str_bidi(&data[1..]),
21 => fuzz_is_utf16_bidi(as_u16_slice(&data[1..])),
22 => fuzz_check_utf8_for_latin1_and_bidi(&data[1..]),
23 => fuzz_check_str_for_latin1_and_bidi(&data[1..]),
24 => fuzz_check_utf16_for_latin1_and_bidi(as_u16_slice(&data[1..])),
_ => return,
}
}
// Comment to make rustfmt not introduce a compilation error
}
);
+285 -97
View File
@@ -24,6 +24,14 @@
#[cfg(all(feature = "simd-accel", any(target_feature = "sse2", all(target_endian = "little", target_arch = "aarch64"))))]
use simd_funcs::*;
// `as` truncates, so works on 32-bit, too.
#[allow(dead_code)]
pub const ASCII_MASK: usize = 0x80808080_80808080u64 as usize;
// `as` truncates, so works on 32-bit, too.
#[allow(dead_code)]
pub const BASIC_LATIN_MASK: usize = 0xFF80FF80_FF80FF80u64 as usize;
#[allow(unused_macros)]
macro_rules! ascii_naive {
($name:ident,
@@ -212,6 +220,62 @@ macro_rules! basic_latin_alu {
});
}
#[allow(unused_macros)]
macro_rules! latin1_alu {
($name:ident,
$src_unit:ty,
$dst_unit:ty,
$stride_fn:ident) => (
#[cfg_attr(feature = "cargo-clippy", allow(never_loop))]
#[inline(always)]
pub unsafe fn $name(src: *const $src_unit, dst: *mut $dst_unit, len: usize) {
let mut offset = 0usize;
// This loop is only broken out of as a `goto` forward
loop {
let mut until_alignment = {
if ::std::mem::size_of::<$src_unit>() < ::std::mem::size_of::<$dst_unit>() {
// unpack
let src_until_alignment = (ALIGNMENT - ((src as usize) & ALIGNMENT_MASK)) & ALIGNMENT_MASK;
if (dst.offset(src_until_alignment as isize) as usize) & ALIGNMENT_MASK != 0 {
break;
}
src_until_alignment
} else {
// pack
let dst_until_alignment = (ALIGNMENT - ((dst as usize) & ALIGNMENT_MASK)) & ALIGNMENT_MASK;
if (src.offset(dst_until_alignment as isize) as usize) & ALIGNMENT_MASK != 0 {
break;
}
dst_until_alignment
}
};
if until_alignment + STRIDE_SIZE <= len {
while until_alignment != 0 {
let code_unit = *(src.offset(offset as isize));
*(dst.offset(offset as isize)) = code_unit as $dst_unit;
offset += 1;
until_alignment -= 1;
}
let len_minus_stride = len - STRIDE_SIZE;
loop {
$stride_fn(src.offset(offset as isize) as *const usize,
dst.offset(offset as isize) as *mut usize);
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
}
break;
}
while offset < len {
let code_unit = *(src.offset(offset as isize));
*(dst.offset(offset as isize)) = code_unit as $dst_unit;
offset += 1;
}
});
}
#[allow(unused_macros)]
macro_rules! ascii_simd_check_align {
($name:ident,
@@ -294,6 +358,89 @@ macro_rules! ascii_simd_check_align {
});
}
#[allow(unused_macros)]
macro_rules! latin1_simd_check_align {
($name:ident,
$src_unit:ty,
$dst_unit:ty,
$stride_both_aligned:ident,
$stride_src_aligned:ident,
$stride_dst_aligned:ident,
$stride_neither_aligned:ident) => (
#[inline(always)]
pub unsafe fn $name(src: *const $src_unit, dst: *mut $dst_unit, len: usize) {
let mut offset = 0usize;
if STRIDE_SIZE <= len {
let len_minus_stride = len - STRIDE_SIZE;
// XXX Should we first process one stride unconditinoally as unaligned to
// avoid the cost of the branchiness below if the first stride fails anyway?
// XXX Should we just use unaligned SSE2 access unconditionally? It seems that
// on Haswell, it would make sense to just use unaligned and not bother
// checking. Need to benchmark older architectures before deciding.
let dst_masked = (dst as usize) & ALIGNMENT_MASK;
if ((src as usize) & ALIGNMENT_MASK) == 0 {
if dst_masked == 0 {
loop {
$stride_both_aligned(src.offset(offset as isize),
dst.offset(offset as isize));
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
} else {
loop {
$stride_src_aligned(src.offset(offset as isize),
dst.offset(offset as isize));
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
}
} else {
if dst_masked == 0 {
loop {
$stride_dst_aligned(src.offset(offset as isize),
dst.offset(offset as isize));
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
} else {
loop {
$stride_neither_aligned(src.offset(offset as isize),
dst.offset(offset as isize));
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
}
}
}
while offset < len {
let code_unit = *(src.offset(offset as isize));
// On x86_64, this loop autovectorizes but in the pack
// case there are instructions whose purpose is to make sure
// each u16 in the vector is truncated before packing. However,
// since we don't care about saturating behavior of SSE2 packing
// when the input isn't Latin1, those instructions are useless.
// Unfortunately, using the `assume` intrinsic to lie to the
// optimizer doesn't make LLVM omit the trunctation that we
// don't need. Possibly this loop could be manually optimized
// to do the sort of thing that LLVM does but without the
// ANDing the read vectors of u16 with a constant that discards
// the high half of each u16. As far as I can tell, the
// optimization assumes that doing a SIMD read past the end of
// the array is OK.
*(dst.offset(offset as isize)) = code_unit as $dst_unit;
offset += 1;
}
});
}
#[allow(unused_macros)]
macro_rules! ascii_simd_unalign {
($name:ident,
@@ -328,6 +475,34 @@ macro_rules! ascii_simd_unalign {
});
}
#[allow(unused_macros)]
macro_rules! latin1_simd_unalign {
($name:ident,
$src_unit:ty,
$dst_unit:ty,
$stride_neither_aligned:ident) => (
#[inline(always)]
pub unsafe fn $name(src: *const $src_unit, dst: *mut $dst_unit, len: usize) {
let mut offset = 0usize;
if STRIDE_SIZE <= len {
let len_minus_stride = len - STRIDE_SIZE;
loop {
$stride_neither_aligned(src.offset(offset as isize),
dst.offset(offset as isize));
offset += STRIDE_SIZE;
if offset > len_minus_stride {
break;
}
}
}
while offset < len {
let code_unit = *(src.offset(offset as isize));
*(dst.offset(offset as isize)) = code_unit as $dst_unit;
offset += 1;
}
});
}
#[allow(unused_macros)]
macro_rules! ascii_to_ascii_simd_stride {
($name:ident,
@@ -336,7 +511,7 @@ macro_rules! ascii_to_ascii_simd_stride {
#[inline(always)]
pub unsafe fn $name(src: *const u8, dst: *mut u8) -> bool {
let simd = $load(src);
if !is_ascii(simd) {
if !simd_is_ascii(simd) {
return false;
}
$store(dst, simd);
@@ -352,7 +527,7 @@ macro_rules! ascii_to_basic_latin_simd_stride {
#[inline(always)]
pub unsafe fn $name(src: *const u8, dst: *mut u16) -> bool {
let simd = $load(src);
if !is_ascii(simd) {
if !simd_is_ascii(simd) {
return false;
}
let (first, second) = simd_unpack(simd);
@@ -362,6 +537,20 @@ macro_rules! ascii_to_basic_latin_simd_stride {
});
}
#[allow(unused_macros)]
macro_rules! unpack_simd_stride {
($name:ident,
$load:ident,
$store:ident) => (
#[inline(always)]
pub unsafe fn $name(src: *const u8, dst: *mut u16) {
let simd = $load(src);
let (first, second) = simd_unpack(simd);
$store(dst, first);
$store(dst.offset(8), second);
});
}
#[allow(unused_macros)]
macro_rules! basic_latin_to_ascii_simd_stride {
($name:ident,
@@ -371,7 +560,7 @@ macro_rules! basic_latin_to_ascii_simd_stride {
pub unsafe fn $name(src: *const u16, dst: *mut u8) -> bool {
let first = $load(src);
let second = $load(src.offset(8));
if is_basic_latin(first | second) {
if simd_is_basic_latin(first | second) {
$store(dst, simd_pack(first, second));
true
} else {
@@ -380,23 +569,40 @@ macro_rules! basic_latin_to_ascii_simd_stride {
});
}
#[allow(unused_macros)]
macro_rules! pack_simd_stride {
($name:ident,
$load:ident,
$store:ident) => (
#[inline(always)]
pub unsafe fn $name(src: *const u16, dst: *mut u8) {
let first = $load(src);
let second = $load(src.offset(8));
$store(dst, simd_pack(first, second));
});
}
cfg_if! {
if #[cfg(all(feature = "simd-accel", target_endian = "little", target_arch = "aarch64"))] {
// SIMD with the same instructions for aligned and unaligned loads and stores
pub const STRIDE_SIZE: usize = 16;
const ALIGNMENT: usize = 8;
// pub const ALIGNMENT: usize = 8;
ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_neither_aligned, load16_unaligned, store16_unaligned);
ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_neither_aligned, load16_unaligned, store8_unaligned);
unpack_simd_stride!(unpack_stride_neither_aligned, load16_unaligned, store8_unaligned);
basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_neither_aligned, load8_unaligned, store16_unaligned);
pack_simd_stride!(pack_stride_neither_aligned, load8_unaligned, store16_unaligned);
ascii_simd_unalign!(ascii_to_ascii, u8, u8, ascii_to_ascii_stride_neither_aligned);
ascii_simd_unalign!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_neither_aligned);
ascii_simd_unalign!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_neither_aligned);
latin1_simd_unalign!(unpack_latin1, u8, u16, unpack_stride_neither_aligned);
latin1_simd_unalign!(pack_latin1, u16, u8, pack_stride_neither_aligned);
} else if #[cfg(all(feature = "simd-accel", target_feature = "sse2"))] {
// SIMD with different instructions for aligned and unaligned loads and stores.
//
@@ -406,7 +612,7 @@ cfg_if! {
pub const STRIDE_SIZE: usize = 16;
const ALIGNMENT_MASK: usize = 15;
pub const ALIGNMENT_MASK: usize = 15;
ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_both_aligned, load16_aligned, store16_aligned);
ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_src_aligned, load16_aligned, store16_unaligned);
@@ -418,31 +624,37 @@ cfg_if! {
ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_dst_aligned, load16_unaligned, store8_aligned);
ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_neither_aligned, load16_unaligned, store8_unaligned);
unpack_simd_stride!(unpack_stride_both_aligned, load16_aligned, store8_aligned);
unpack_simd_stride!(unpack_stride_src_aligned, load16_aligned, store8_unaligned);
unpack_simd_stride!(unpack_stride_dst_aligned, load16_unaligned, store8_aligned);
unpack_simd_stride!(unpack_stride_neither_aligned, load16_unaligned, store8_unaligned);
basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_both_aligned, load8_aligned, store16_aligned);
basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_src_aligned, load8_aligned, store16_unaligned);
basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_dst_aligned, load8_unaligned, store16_aligned);
basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_neither_aligned, load8_unaligned, store16_unaligned);
pack_simd_stride!(pack_stride_both_aligned, load8_aligned, store16_aligned);
pack_simd_stride!(pack_stride_src_aligned, load8_aligned, store16_unaligned);
pack_simd_stride!(pack_stride_dst_aligned, load8_unaligned, store16_aligned);
pack_simd_stride!(pack_stride_neither_aligned, load8_unaligned, store16_unaligned);
ascii_simd_check_align!(ascii_to_ascii, u8, u8, ascii_to_ascii_stride_both_aligned, ascii_to_ascii_stride_src_aligned, ascii_to_ascii_stride_dst_aligned, ascii_to_ascii_stride_neither_aligned);
ascii_simd_check_align!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_both_aligned, ascii_to_basic_latin_stride_src_aligned, ascii_to_basic_latin_stride_dst_aligned, ascii_to_basic_latin_stride_neither_aligned);
ascii_simd_check_align!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_both_aligned, basic_latin_to_ascii_stride_src_aligned, basic_latin_to_ascii_stride_dst_aligned, basic_latin_to_ascii_stride_neither_aligned);
latin1_simd_check_align!(unpack_latin1, u8, u16, unpack_stride_both_aligned, unpack_stride_src_aligned, unpack_stride_dst_aligned, unpack_stride_neither_aligned);
latin1_simd_check_align!(pack_latin1, u16, u8, pack_stride_both_aligned, pack_stride_src_aligned, pack_stride_dst_aligned, pack_stride_neither_aligned);
} else if #[cfg(all(target_endian = "little", target_pointer_width = "64"))] {
// Aligned ALU word, little-endian, 64-bit
pub const STRIDE_SIZE: usize = 16;
const ALIGNMENT: usize = 8;
pub const ALIGNMENT: usize = 8;
const ALIGNMENT_MASK: usize = 7;
pub const ALIGNMENT_MASK: usize = 7;
#[inline(always)]
unsafe fn ascii_to_basic_latin_stride_little_64(src: *const usize, dst: *mut usize) -> bool {
let word = *src;
let second_word = *(src.offset(1));
// Check if the words contains non-ASCII
if (word & ASCII_MASK) | (second_word & ASCII_MASK) != 0 {
return false;
}
unsafe fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {
let first = ((0x00000000_FF000000usize & word) << 24) |
((0x00000000_00FF0000usize & word) << 16) |
((0x00000000_0000FF00usize & word) << 8) |
@@ -463,18 +675,10 @@ cfg_if! {
*(dst.offset(1)) = second;
*(dst.offset(2)) = third;
*(dst.offset(3)) = fourth;
true
}
#[inline(always)]
unsafe fn basic_latin_to_ascii_stride_little_64(src: *const usize, dst: *mut usize) -> bool {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
if (first & BASIC_LATIN_MASK) | (second & BASIC_LATIN_MASK) | (third & BASIC_LATIN_MASK) | (fourth & BASIC_LATIN_MASK) != 0 {
return false;
}
unsafe fn pack_alu(first: usize, second: usize, third: usize, fourth: usize, dst: *mut usize) {
let word = ((0x00FF0000_00000000usize & second) << 8) |
((0x000000FF_00000000usize & second) << 16) |
((0x00000000_00FF0000usize & second) << 24) |
@@ -493,28 +697,18 @@ cfg_if! {
(0x00000000_000000FFusize & third);
*dst = word;
*(dst.offset(1)) = second_word;
true
}
basic_latin_alu!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_little_64);
basic_latin_alu!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_little_64);
} else if #[cfg(all(target_endian = "little", target_pointer_width = "32"))] {
// Aligned ALU word, little-endian, 32-bit
pub const STRIDE_SIZE: usize = 8;
const ALIGNMENT: usize = 4;
pub const ALIGNMENT: usize = 4;
const ALIGNMENT_MASK: usize = 3;
pub const ALIGNMENT_MASK: usize = 3;
#[inline(always)]
unsafe fn ascii_to_basic_latin_stride_little_32(src: *const usize, dst: *mut usize) -> bool {
let word = *src;
let second_word = *(src.offset(1));
// Check if the words contains non-ASCII
if (word & ASCII_MASK) | (second_word & ASCII_MASK) != 0 {
return false;
}
unsafe fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {
let first = ((0x0000FF00usize & word) << 8) |
(0x000000FFusize & word);
let second = ((0xFF000000usize & word) >> 8) |
@@ -527,18 +721,10 @@ cfg_if! {
*(dst.offset(1)) = second;
*(dst.offset(2)) = third;
*(dst.offset(3)) = fourth;
return true;
}
#[inline(always)]
unsafe fn basic_latin_to_ascii_stride_little_32(src: *const usize, dst: *mut usize) -> bool {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
if (first & BASIC_LATIN_MASK) | (second & BASIC_LATIN_MASK) | (third & BASIC_LATIN_MASK) | (fourth & BASIC_LATIN_MASK) != 0 {
return false;
}
unsafe fn pack_alu(first: usize, second: usize, third: usize, fourth: usize, dst: *mut usize) {
let word = ((0x00FF0000usize & second) << 8) |
((0x000000FFusize & second) << 16) |
((0x00FF0000usize & first) >> 8) |
@@ -549,28 +735,18 @@ cfg_if! {
(0x000000FFusize & third);
*dst = word;
*(dst.offset(1)) = second_word;
return true;
}
basic_latin_alu!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_little_32);
basic_latin_alu!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_little_32);
} else if #[cfg(all(target_endian = "big", target_pointer_width = "64"))] {
// Aligned ALU word, big-endian, 64-bit
pub const STRIDE_SIZE: usize = 16;
const ALIGNMENT: usize = 8;
pub const ALIGNMENT: usize = 8;
const ALIGNMENT_MASK: usize = 7;
pub const ALIGNMENT_MASK: usize = 7;
#[inline(always)]
unsafe fn ascii_to_basic_latin_stride_big_64(src: *const usize, dst: *mut usize) -> bool {
let word = *src;
let second_word = *(src.offset(1));
// Check if the words contains non-ASCII
if (word & ASCII_MASK) | (second_word & ASCII_MASK) != 0 {
return false;
}
unsafe fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {
let first = ((0xFF000000_00000000usize & word) >> 8) |
((0x00FF0000_00000000usize & word) >> 16) |
((0x0000FF00_00000000usize & word) >> 24) |
@@ -591,18 +767,10 @@ cfg_if! {
*(dst.offset(1)) = second;
*(dst.offset(2)) = third;
*(dst.offset(3)) = fourth;
return true;
}
#[inline(always)]
unsafe fn basic_latin_to_ascii_stride_big_64(src: *const usize, dst: *mut usize) -> bool {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
if (first & BASIC_LATIN_MASK) | (second & BASIC_LATIN_MASK) | (third & BASIC_LATIN_MASK) | (fourth & BASIC_LATIN_MASK) != 0 {
return false;
}
unsafe fn pack_alu(first: usize, second: usize, third: usize, fourth: usize, dst: *mut usize) {
let word = ((0x00FF0000_00000000usize & first) << 8) |
((0x000000FF_00000000usize & first) << 16) |
((0x00000000_00FF0000usize & first) << 24) |
@@ -621,28 +789,18 @@ cfg_if! {
(0x00000000_000000FFusize & fourth);
*dst = word;
*(dst.offset(1)) = second_word;
return true;
}
basic_latin_alu!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_big_64);
basic_latin_alu!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_big_64);
} else if #[cfg(all(target_endian = "big", target_pointer_width = "32"))] {
// Aligned ALU word, big-endian, 32-bit
pub const STRIDE_SIZE: usize = 8;
const ALIGNMENT: usize = 4;
pub const ALIGNMENT: usize = 4;
const ALIGNMENT_MASK: usize = 3;
pub const ALIGNMENT_MASK: usize = 3;
#[inline(always)]
unsafe fn ascii_to_basic_latin_stride_big_32(src: *const usize, dst: *mut usize) -> bool {
let word = *src;
let second_word = *(src.offset(1));
// Check if the words contains non-ASCII
if (word & ASCII_MASK) | (second_word & ASCII_MASK) != 0 {
return false;
}
unsafe fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {
let first = ((0xFF000000usize & word) >> 8) |
((0x00FF0000usize & word) >> 16);
let second = ((0x0000FF00usize & word) << 8) |
@@ -655,18 +813,10 @@ cfg_if! {
*(dst.offset(1)) = second;
*(dst.offset(2)) = third;
*(dst.offset(3)) = fourth;
return true;
}
#[inline(always)]
unsafe fn basic_latin_to_ascii_stride_big_32(src: *const usize, dst: *mut usize) -> bool {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
if (first & BASIC_LATIN_MASK) | (second & BASIC_LATIN_MASK) | (third & BASIC_LATIN_MASK) | (fourth & BASIC_LATIN_MASK) != 0 {
return false;
}
unsafe fn pack_alu(first: usize, second: usize, third: usize, fourth: usize, dst: *mut usize) {
let word = ((0x00FF0000usize & first) << 8) |
((0x000000FFusize & first) << 16) |
((0x00FF0000usize & second) >> 8) |
@@ -677,11 +827,7 @@ cfg_if! {
(0x000000FFusize & fourth);
*dst = word;
*(dst.offset(1)) = second_word;
return true;
}
basic_latin_alu!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_big_32);
basic_latin_alu!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_big_32);
} else {
ascii_naive!(ascii_to_ascii, u8, u8);
ascii_naive!(ascii_to_basic_latin, u8, u16);
@@ -716,7 +862,7 @@ cfg_if! {
let len_minus_stride = len - STRIDE_SIZE;
loop {
let simd = unsafe { load16_unaligned(src.offset(offset as isize)) };
if !is_ascii(simd) {
if !simd_is_ascii(simd) {
break;
}
offset += STRIDE_SIZE;
@@ -787,9 +933,51 @@ cfg_if! {
None
}
} else {
// `as` truncates, so works on 32-bit, too.
const ASCII_MASK: usize = 0x80808080_80808080u64 as usize;
const BASIC_LATIN_MASK: usize = 0xFF80FF80_FF80FF80u64 as usize;
#[inline(always)]
unsafe fn unpack_latin1_stride_alu(src: *const usize, dst: *mut usize) {
let word = *src;
let second_word = *(src.offset(1));
unpack_alu(word, second_word, dst);
}
#[inline(always)]
unsafe fn pack_latin1_stride_alu(src: *const usize, dst: *mut usize) {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
pack_alu(first, second, third, fourth, dst);
}
#[inline(always)]
unsafe fn ascii_to_basic_latin_stride_alu(src: *const usize, dst: *mut usize) -> bool {
let word = *src;
let second_word = *(src.offset(1));
// Check if the words contains non-ASCII
if (word & ASCII_MASK) | (second_word & ASCII_MASK) != 0 {
return false;
}
unpack_alu(word, second_word, dst);
true
}
#[inline(always)]
unsafe fn basic_latin_to_ascii_stride_alu(src: *const usize, dst: *mut usize) -> bool {
let first = *src;
let second = *(src.offset(1));
let third = *(src.offset(2));
let fourth = *(src.offset(3));
if (first & BASIC_LATIN_MASK) | (second & BASIC_LATIN_MASK) | (third & BASIC_LATIN_MASK) | (fourth & BASIC_LATIN_MASK) != 0 {
return false;
}
pack_alu(first, second, third, fourth, dst);
true
}
basic_latin_alu!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_alu);
basic_latin_alu!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_alu);
latin1_alu!(unpack_latin1, u8, u16, unpack_latin1_stride_alu);
latin1_alu!(pack_latin1, u16, u8, pack_latin1_stride_alu);
#[inline(always)]
unsafe fn ascii_to_ascii_stride(src: *const usize, dst: *mut usize) -> Option<usize> {
+46 -10
View File
@@ -17,6 +17,10 @@
//! streamability goals are browser-oriented, and that FFI-friendliness is a
//! goal.
//!
//! Additionally, the `mem` module provides functions that are useful for
//! applications that need to be able to deal with legacy in-memory
//! representations of Unicode.
//!
//! # Availability
//!
//! The code is available under the
@@ -491,7 +495,7 @@
//! </tbody>
//! </table>
#![cfg_attr(feature = "simd-accel", feature(cfg_target_feature, platform_intrinsics))]
#![cfg_attr(feature = "simd-accel", feature(cfg_target_feature, platform_intrinsics, core_intrinsics))]
#[macro_use]
extern crate cfg_if;
@@ -539,6 +543,8 @@ mod handles;
mod data;
mod variant;
pub mod mem;
use variant::*;
use utf_8::utf8_valid_up_to;
use ascii::ascii_valid_up_to;
@@ -2030,20 +2036,20 @@ static ENCODINGS_IN_LABEL_SORT: [&'static Encoding; 219] = [&WINDOWS_1252_INIT,
/// # Streaming vs. Non-Streaming
///
/// When you have the entire input in a single buffer, you can use the
/// methods [`decode()`][1], [`decode_with_bom_removal()`][2],
/// [`decode_without_bom_handling()`][3],
/// [`decode_without_bom_handling_and_without_replacement()`][4] and
/// [`encode()`][5]. (These methods are available to Rust callers only and are
/// methods [`decode()`][3], [`decode_with_bom_removal()`][3],
/// [`decode_without_bom_handling()`][5],
/// [`decode_without_bom_handling_and_without_replacement()`][6] and
/// [`encode()`][7]. (These methods are available to Rust callers only and are
/// not available in the C API.) Unlike the rest of the API available to Rust,
/// these methods perform heap allocations. You should the `Decoder` and
/// `Encoder` objects when your input is split into multiple buffers or when
/// you want to control the allocation of the output buffers.
///
/// [1]: #method.decode
/// [2]: #method.decode_with_bom_removal
/// [3]: #method.decode_without_bom_handling
/// [4]: #method.decode_without_bom_handling_and_without_replacement
/// [5]: #method.encode
/// [3]: #method.decode
/// [4]: #method.decode_with_bom_removal
/// [5]: #method.decode_without_bom_handling
/// [6]: #method.decode_without_bom_handling_and_without_replacement
/// [7]: #method.encode
///
/// # Instances
///
@@ -2222,6 +2228,7 @@ impl Encoding {
/// unsafe fallback for labels that `for_label()` maps to `Some(REPLACEMENT)`.
///
/// Available via the C wrapper.
#[inline]
pub fn for_label_no_replacement(label: &[u8]) -> Option<&'static Encoding> {
match Encoding::for_label(label) {
None => None,
@@ -2246,6 +2253,7 @@ impl Encoding {
/// or UTF-16BE BOM or `None` otherwise.
///
/// Available via the C wrapper.
#[inline]
pub fn for_bom(buffer: &[u8]) -> Option<(&'static Encoding, usize)> {
if buffer.starts_with(b"\xEF\xBB\xBF") {
Some((UTF_8, 3))
@@ -2264,6 +2272,7 @@ impl Encoding {
/// `document.characterSet` property.
///
/// Available via the C wrapper.
#[inline]
pub fn name(&'static self) -> &'static str {
self.name
}
@@ -2272,6 +2281,7 @@ impl Encoding {
/// `char`. (Only true if the output encoding is UTF-8.)
///
/// Available via the C wrapper.
#[inline]
pub fn can_encode_everything(&'static self) -> bool {
self.output_encoding() == UTF_8
}
@@ -2280,12 +2290,14 @@ impl Encoding {
/// U+0000...U+007F and vice versa.
///
/// Available via the C wrapper.
#[inline]
pub fn is_ascii_compatible(&'static self) -> bool {
!(self == REPLACEMENT || self == UTF_16BE || self == UTF_16LE || self == ISO_2022_JP)
}
/// Checks whether the bytes 0x00...0x7F map mostly to the characters
/// U+0000...U+007F and vice versa.
#[inline]
fn is_potentially_borrowable(&'static self) -> bool {
!(self == REPLACEMENT || self == UTF_16BE || self == UTF_16LE)
}
@@ -2294,6 +2306,7 @@ impl Encoding {
/// UTF-16BE, UTF-16LE and replacement and the encoding itself otherwise.
///
/// Available via the C wrapper.
#[inline]
pub fn output_encoding(&'static self) -> &'static Encoding {
if self == REPLACEMENT || self == UTF_16BE || self == UTF_16LE {
UTF_8
@@ -2336,6 +2349,7 @@ impl Encoding {
/// `usize`.
///
/// Available to Rust only.
#[inline]
pub fn decode<'a>(&'static self, bytes: &'a [u8]) -> (Cow<'a, str>, &'static Encoding, bool) {
let (encoding, without_bom) = match Encoding::for_bom(bytes) {
Some((encoding, bom_length)) => (encoding, &bytes[bom_length..]),
@@ -2378,6 +2392,7 @@ impl Encoding {
/// `usize`.
///
/// Available to Rust only.
#[inline]
pub fn decode_with_bom_removal<'a>(&'static self, bytes: &'a [u8]) -> (Cow<'a, str>, bool) {
let without_bom = if self == UTF_8 && bytes.starts_with(b"\xEF\xBB\xBF") {
&bytes[3..]
@@ -2689,6 +2704,7 @@ impl Encoding {
/// for UTF-8, UTF-16LE or UTF-16BE instead of this encoding.
///
/// Available via the C wrapper.
#[inline]
pub fn new_decoder(&'static self) -> Decoder {
Decoder::new(self, self.new_variant_decoder(), BomHandling::Sniff)
}
@@ -2702,6 +2718,7 @@ impl Encoding {
/// encoding.
///
/// Available via the C wrapper.
#[inline]
pub fn new_decoder_with_bom_removal(&'static self) -> Decoder {
Decoder::new(self, self.new_variant_decoder(), BomHandling::Remove)
}
@@ -2717,6 +2734,7 @@ impl Encoding {
/// instead of this method to cause the BOM to be removed.
///
/// Available via the C wrapper.
#[inline]
pub fn new_decoder_without_bom_handling(&'static self) -> Decoder {
Decoder::new(self, self.new_variant_decoder(), BomHandling::Off)
}
@@ -2724,6 +2742,7 @@ impl Encoding {
/// Instantiates a new encoder for the output encoding of this encoding.
///
/// Available via the C wrapper.
#[inline]
pub fn new_encoder(&'static self) -> Encoder {
let enc = self.output_encoding();
enc.variant.new_encoder(enc)
@@ -2767,6 +2786,7 @@ impl Encoding {
}
impl PartialEq for Encoding {
#[inline]
fn eq(&self, other: &Encoding) -> bool {
(self as *const Encoding) == (other as *const Encoding)
}
@@ -2775,12 +2795,14 @@ impl PartialEq for Encoding {
impl Eq for Encoding {}
impl Hash for Encoding {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
(self as *const Encoding).hash(state);
}
}
impl std::fmt::Debug for Encoding {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Encoding {{ {} }}", self.name)
}
@@ -2788,6 +2810,7 @@ impl std::fmt::Debug for Encoding {
#[cfg(feature = "serde")]
impl Serialize for Encoding {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
@@ -3054,6 +3077,7 @@ impl Decoder {
/// of the decoder.
///
/// Available via the C wrapper.
#[inline]
pub fn encoding(&self) -> &'static Encoding {
self.encoding
}
@@ -3769,12 +3793,14 @@ impl Encoder {
}
/// The `Encoding` this `Encoder` is for.
#[inline]
pub fn encoding(&self) -> &'static Encoding {
self.encoding
}
/// Returns `true` if this is an ISO-2022-JP encoder that's not in the
/// ASCII state and `false` otherwise.
#[inline]
pub fn has_pending_state(&self) -> bool {
self.variant.has_pending_state()
}
@@ -4111,6 +4137,16 @@ fn in_range16(i: u16, start: u16, end: u16) -> bool {
i.wrapping_sub(start) < (end - start)
}
#[inline(always)]
fn in_range32(i: u32, start: u32, end: u32) -> bool {
i.wrapping_sub(start) < (end - start)
}
#[inline(always)]
fn in_inclusive_range8(i: u8, start: u8, end: u8) -> bool {
i.wrapping_sub(start) <= (end - start)
}
#[inline(always)]
fn in_inclusive_range16(i: u16, start: u16, end: u16) -> bool {
i.wrapping_sub(start) <= (end - start)
+2484
View File
File diff suppressed because it is too large Load Diff
+134 -21
View File
@@ -21,6 +21,7 @@ pub unsafe fn load16_unaligned(ptr: *const u8) -> u8x16 {
simd
}
#[allow(dead_code)]
#[inline(always)]
pub unsafe fn load16_aligned(ptr: *const u8) -> u8x16 {
*(ptr as *const u8x16)
@@ -31,6 +32,7 @@ pub unsafe fn store16_unaligned(ptr: *mut u8, s: u8x16) {
::std::ptr::copy_nonoverlapping(&s as *const u8x16 as *const u8, ptr, 16);
}
#[allow(dead_code)]
#[inline(always)]
pub unsafe fn store16_aligned(ptr: *mut u8, s: u8x16) {
*(ptr as *mut u8x16) = s;
@@ -43,6 +45,7 @@ pub unsafe fn load8_unaligned(ptr: *const u16) -> u16x8 {
simd
}
#[allow(dead_code)]
#[inline(always)]
pub unsafe fn load8_aligned(ptr: *const u16) -> u16x8 {
*(ptr as *const u16x8)
@@ -53,6 +56,7 @@ pub unsafe fn store8_unaligned(ptr: *mut u16, s: u16x8) {
::std::ptr::copy_nonoverlapping(&s as *const u16x8 as *const u8, ptr as *mut u8, 16);
}
#[allow(dead_code)]
#[inline(always)]
pub unsafe fn store8_aligned(ptr: *mut u16, s: u16x8) {
*(ptr as *mut u16x8) = s;
@@ -89,7 +93,7 @@ cfg_if! {
cfg_if! {
if #[cfg(target_feature = "sse2")] {
#[inline(always)]
pub fn is_ascii(s: u8x16) -> bool {
pub fn simd_is_ascii(s: u8x16) -> bool {
unsafe {
let signed: i8x16 = ::std::mem::transmute_copy(&s);
x86_mm_movemask_epi8(signed) == 0
@@ -101,16 +105,42 @@ cfg_if! {
}
#[inline(always)]
pub fn is_ascii(s: u8x16) -> bool {
pub fn simd_is_ascii(s: u8x16) -> bool {
unsafe {
aarch64_vmaxvq_u8(s) < 0x80
}
}
} else {
#[inline(always)]
pub fn is_ascii(s: u8x16) -> bool {
let highest_ascii = u8x16::splat(0x7F);
!s.gt(highest_ascii).any()
pub fn simd_is_ascii(s: u8x16) -> bool {
let above_ascii = u8x16::splat(0x80);
s.lt(above_ascii).all()
}
}
}
cfg_if! {
if #[cfg(target_feature = "sse2")] {
#[inline(always)]
pub fn simd_is_str_latin1(s: u8x16) -> bool {
if simd_is_ascii(s) {
return true;
}
let above_str_latin1 = u8x16::splat(0xC4);
s.lt(above_str_latin1).all()
}
} else if #[cfg(target_arch = "aarch64")]{
#[inline(always)]
pub fn simd_is_str_latin1(s: u8x16) -> bool {
unsafe {
aarch64_vmaxvq_u8(s) < 0xC4
}
}
} else {
#[inline(always)]
pub fn simd_is_str_latin1(s: u8x16) -> bool {
let above_str_latin1 = u8x16::splat(0xC4);
s.lt(above_str_latin1).all()
}
}
}
@@ -122,20 +152,103 @@ cfg_if! {
}
#[inline(always)]
pub fn is_basic_latin(s: u16x8) -> bool {
pub fn simd_is_basic_latin(s: u16x8) -> bool {
unsafe {
aarch64_vmaxvq_u16(s) < 0x80
}
}
#[inline(always)]
pub fn simd_is_latin1(s: u16x8) -> bool {
unsafe {
aarch64_vmaxvq_u16(s) < 0x100
}
}
} else {
#[inline(always)]
pub fn is_basic_latin(s: u16x8) -> bool {
let highest_ascii = u16x8::splat(0x7F);
!s.gt(highest_ascii).any()
pub fn simd_is_basic_latin(s: u16x8) -> bool {
let above_ascii = u16x8::splat(0x80);
s.lt(above_ascii).all()
}
#[inline(always)]
pub fn simd_is_latin1(s: u16x8) -> bool {
// For some reason, on SSE2 this formulation
// seems faster in this case while the above
// function is better the other way round...
let highest_latin1 = u16x8::splat(0xFF);
!s.gt(highest_latin1).any()
}
}
}
#[inline(always)]
pub fn contains_surrogates(s: u16x8) -> bool {
let mask = u16x8::splat(0xF800);
let surrogate_bits = u16x8::splat(0xD800);
(s & mask).eq(surrogate_bits).any()
}
cfg_if! {
if #[cfg(target_arch = "aarch64")]{
macro_rules! aarch64_return_false_if_below_hebrew {
($s:ident) => ({
unsafe {
if aarch64_vmaxvq_u16($s) < 0x0590 {
return false;
}
}
})
}
macro_rules! non_aarch64_return_false_if_all {
($s:ident) => ()
}
} else {
macro_rules! aarch64_return_false_if_below_hebrew {
($s:ident) => ()
}
macro_rules! non_aarch64_return_false_if_all {
($s:ident) => ({
if $s.all() {
return false;
}
})
}
}
}
macro_rules! in_range16x8 {
($s:ident, $start:expr, $end:expr) => ({
// SIMD sub is wrapping
($s - u16x8::splat($start)).lt(u16x8::splat($end - $start))
})
}
#[inline(always)]
pub fn is_u16x8_bidi(s: u16x8) -> bool {
// We try to first quickly refute the RTLness of the vector. If that
// fails, we do the real RTL check, so in that case we end up wasting
// the work for the up-front quick checks. Even the quick-check is
// two-fold in order to return `false` ASAP if everything is below
// Hebrew.
aarch64_return_false_if_below_hebrew!(s);
let below_hebrew = s.lt(u16x8::splat(0x0590));
non_aarch64_return_false_if_all!(below_hebrew);
if (below_hebrew | in_range16x8!(s, 0x0900, 0x200F) | in_range16x8!(s, 0x2068, 0xD802)).all() {
return false;
}
// Quick refutation failed. Let's do the full check.
(in_range16x8!(s, 0x0590, 0x0900) | in_range16x8!(s, 0xFB50, 0xFE00) | in_range16x8!(s, 0xFE70, 0xFF00) | in_range16x8!(s, 0xD802, 0xD804) | in_range16x8!(s, 0xD83A, 0xD83C) | s.eq(u16x8::splat(0x200F)) | s.eq(u16x8::splat(0x202B)) | s.eq(u16x8::splat(0x202E)) | s.eq(u16x8::splat(0x2067))).any()
}
#[inline(always)]
pub fn simd_unpack(s: u8x16) -> (u16x8, u16x8) {
unsafe {
@@ -206,7 +319,7 @@ mod tests {
}
#[test]
fn test_is_basic_latin_success() {
fn test_simd_is_basic_latin_success() {
let ascii: [u8; 16] = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
0x72, 0x73, 0x74, 0x75, 0x76];
let basic_latin: [u16; 16] = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70,
@@ -216,7 +329,7 @@ mod tests {
let mut vec = Vec::with_capacity(16);
vec.resize(16, 0u8);
let ptr = vec.as_mut_ptr();
assert!(is_basic_latin(first | second));
assert!(simd_is_basic_latin(first | second));
unsafe {
store16_unaligned(ptr, simd_pack(first, second));
}
@@ -224,46 +337,46 @@ mod tests {
}
#[test]
fn test_is_basic_latin_c0() {
fn test_simd_is_basic_latin_c0() {
let input: [u16; 16] = [0x61, 0x62, 0x63, 0x81, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
0x72, 0x73, 0x74, 0x75, 0x76];
let first = unsafe { load8_unaligned(input.as_ptr()) };
let second = unsafe { load8_unaligned(input.as_ptr().offset(8)) };
assert!(!is_basic_latin(first | second));
assert!(!simd_is_basic_latin(first | second));
}
#[test]
fn test_is_basic_latin_0fff() {
fn test_simd_is_basic_latin_0fff() {
let input: [u16; 16] = [0x61, 0x62, 0x63, 0x0FFF, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76];
let first = unsafe { load8_unaligned(input.as_ptr()) };
let second = unsafe { load8_unaligned(input.as_ptr().offset(8)) };
assert!(!is_basic_latin(first | second));
assert!(!simd_is_basic_latin(first | second));
}
#[test]
fn test_is_basic_latin_ffff() {
fn test_simd_is_basic_latin_ffff() {
let input: [u16; 16] = [0x61, 0x62, 0x63, 0xFFFF, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76];
let first = unsafe { load8_unaligned(input.as_ptr()) };
let second = unsafe { load8_unaligned(input.as_ptr().offset(8)) };
assert!(!is_basic_latin(first | second));
assert!(!simd_is_basic_latin(first | second));
}
#[test]
fn test_is_ascii_success() {
fn test_simd_is_ascii_success() {
let ascii: [u8; 16] = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
0x72, 0x73, 0x74, 0x75, 0x76];
let simd = unsafe { load16_unaligned(ascii.as_ptr()) };
assert!(is_ascii(simd));
assert!(simd_is_ascii(simd));
}
#[test]
fn test_is_ascii_failure() {
fn test_simd_is_ascii_failure() {
let input: [u8; 16] = [0x61, 0x62, 0x63, 0x64, 0x81, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
0x72, 0x73, 0x74, 0x75, 0x76];
let simd = unsafe { load16_unaligned(input.as_ptr()) };
assert!(!is_ascii(simd));
assert!(!simd_is_ascii(simd));
}
#[cfg(target_feature = "sse2")]
+17 -15
View File
@@ -34,21 +34,21 @@ cfg_if! {
}
}
const UTF8_NORMAL_TRAIL: u8 = 1 << 3;
pub const UTF8_NORMAL_TRAIL: u8 = 1 << 3;
const UTF8_THREE_BYTE_SPECIAL_LOWER_BOUND_TRAIL: u8 = 1 << 4;
pub const UTF8_THREE_BYTE_SPECIAL_LOWER_BOUND_TRAIL: u8 = 1 << 4;
const UTF8_THREE_BYTE_SPECIAL_UPPER_BOUND_TRAIL: u8 = 1 << 5;
pub const UTF8_THREE_BYTE_SPECIAL_UPPER_BOUND_TRAIL: u8 = 1 << 5;
const UTF8_FOUR_BYTE_SPECIAL_LOWER_BOUND_TRAIL: u8 = 1 << 6;
pub const UTF8_FOUR_BYTE_SPECIAL_LOWER_BOUND_TRAIL: u8 = 1 << 6;
const UTF8_FOUR_BYTE_SPECIAL_UPPER_BOUND_TRAIL: u8 = 1 << 7;
pub const UTF8_FOUR_BYTE_SPECIAL_UPPER_BOUND_TRAIL: u8 = 1 << 7;
// BEGIN GENERATED CODE. PLEASE DO NOT EDIT.
// Instead, please regenerate using generate-encoding-data.py
/// Bit is 1 if the trail is invalid.
static UTF8_TRAIL_INVALID: [u8; 256] =
pub static UTF8_TRAIL_INVALID: [u8; 256] =
[248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
@@ -433,16 +433,18 @@ pub struct Utf8Decoder {
}
impl Utf8Decoder {
pub fn new_inner() -> Utf8Decoder {
Utf8Decoder {
code_point: 0,
bytes_seen: 0,
bytes_needed: 0,
lower_boundary: 0x80u8,
upper_boundary: 0xBFu8,
}
}
pub fn new() -> VariantDecoder {
VariantDecoder::Utf8(
Utf8Decoder {
code_point: 0,
bytes_seen: 0,
bytes_needed: 0,
lower_boundary: 0x80u8,
upper_boundary: 0xBFu8,
}
)
VariantDecoder::Utf8(Utf8Decoder::new_inner())
}
fn extra_from_state(&self) -> usize {