use crate::libcore;
#[cfg(target_arch = "x86")]
use core::arch::x86::{
__m256i, _mm256_add_epi8, _mm256_adds_epu8, _mm256_alignr_epi8, _mm256_and_si256,
_mm256_cmpgt_epi8, _mm256_extract_epi32, _mm256_lddqu_si256, _mm256_or_si256,
_mm256_permute2x128_si256, _mm256_set1_epi8, _mm256_set_epi8, _mm256_setzero_si256,
_mm256_shuffle_epi8, _mm256_srli_epi16, _mm256_sub_epi8, _mm256_subs_epu8, _mm256_testz_si256,
};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{
__m256i, _mm256_add_epi8, _mm256_adds_epu8, _mm256_alignr_epi8, _mm256_and_si256,
_mm256_cmpgt_epi8, _mm256_extract_epi32, _mm256_lddqu_si256, _mm256_or_si256,
_mm256_permute2x128_si256, _mm256_set1_epi8, _mm256_setzero_si256, _mm256_shuffle_epi8,
_mm256_srli_epi16, _mm256_sub_epi8, _mm256_subs_epu8, _mm256_testz_si256,
};
static FIRST_LEN_TABLE: [i8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
];
static FIRST_RANGE_TABLE: [i8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8,
];
static RANGE_MIN_TABLE: [i8; 32] = [
0,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0xa0i32 as i8,
0x80i32 as i8,
0x90i32 as i8,
0x80i32 as i8,
0xC2i32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0xa0i32 as i8,
0x80i32 as i8,
0x90i32 as i8,
0x80i32 as i8,
0xC2i32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
0x7Fi32 as i8,
];
static RANGE_MAX_TABLE: [i8; 32] = [
0x7Fi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0x9Fi32 as i8,
0xBFi32 as i8,
0x8Fi32 as i8,
0xF4i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x7Fi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0xBFi32 as i8,
0x9Fi32 as i8,
0xBFi32 as i8,
0x8fi32 as i8,
0xF4i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
0x80i32 as i8,
];
static DF_EE_TABLE: [i8; 32] = [
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,
];
static EF_FE_TABLE: [i8; 32] = [
0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
#[inline]
fn push_last_byte_of_a_to_b(a: __m256i, b: __m256i) -> __m256i {
unsafe {
return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 15);
}
}
#[inline]
fn push_last_2bytes_of_a_to_b(a: __m256i, b: __m256i) -> __m256i {
unsafe {
return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 14);
}
}
#[inline]
fn push_last_3bytes_of_a_to_b(a: __m256i, b: __m256i) -> __m256i {
unsafe {
return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 13);
}
}
pub fn is_utf8(bytes: &[u8]) -> bool {
let mut data = bytes.as_ptr();
let mut len = bytes.len();
unsafe {
if len >= 32 {
let mut prev_input = _mm256_setzero_si256();
let mut prev_first_len = _mm256_setzero_si256();
let mut error = _mm256_setzero_si256();
let first_len_tbl = _mm256_lddqu_si256(FIRST_LEN_TABLE.as_ptr() as *const __m256i);
let first_range_tbl = _mm256_lddqu_si256(FIRST_RANGE_TABLE.as_ptr() as *const __m256i);
let range_min_tbl = _mm256_lddqu_si256(RANGE_MIN_TABLE.as_ptr() as *const __m256i);
let range_max_tbl = _mm256_lddqu_si256(RANGE_MAX_TABLE.as_ptr() as *const __m256i);
let df_ee_tbl = _mm256_lddqu_si256(DF_EE_TABLE.as_ptr() as *const __m256i);
let ef_fe_tbl = _mm256_lddqu_si256(EF_FE_TABLE.as_ptr() as *const __m256i);
while len >= 32 {
let input = _mm256_lddqu_si256(data as *const __m256i);
let high_nibbles =
_mm256_and_si256(_mm256_srli_epi16(input, 4), _mm256_set1_epi8(0xF));
let first_len = _mm256_shuffle_epi8(first_len_tbl, high_nibbles);
let mut range = _mm256_shuffle_epi8(first_range_tbl, high_nibbles);
range = _mm256_or_si256(range, push_last_byte_of_a_to_b(prev_first_len, first_len));
let mut tmp1 = _mm256_subs_epu8(first_len, _mm256_set1_epi8(1));
let mut tmp2 = _mm256_subs_epu8(prev_first_len, _mm256_set1_epi8(1));
range = _mm256_or_si256(range, push_last_2bytes_of_a_to_b(tmp2, tmp1));
tmp1 = _mm256_subs_epu8(first_len, _mm256_set1_epi8(2));
tmp2 = _mm256_subs_epu8(prev_first_len, _mm256_set1_epi8(2));
range = _mm256_or_si256(range, push_last_3bytes_of_a_to_b(tmp2, tmp1));
let shift1 = push_last_byte_of_a_to_b(prev_input, input);
let pos = _mm256_sub_epi8(shift1, _mm256_set1_epi8(0xEFi32 as i8));
tmp1 = _mm256_subs_epu8(pos, _mm256_set1_epi8(240i32 as i8));
let mut range2 = _mm256_shuffle_epi8(df_ee_tbl, tmp1);
tmp2 = _mm256_adds_epu8(pos, _mm256_set1_epi8(112i32 as i8));
range2 = _mm256_add_epi8(range2, _mm256_shuffle_epi8(ef_fe_tbl, tmp2));
range = _mm256_add_epi8(range, range2);
let minv = _mm256_shuffle_epi8(range_min_tbl, range);
let maxv = _mm256_shuffle_epi8(range_max_tbl, range);
error = _mm256_or_si256(error, _mm256_cmpgt_epi8(minv, input));
error = _mm256_or_si256(error, _mm256_cmpgt_epi8(input, maxv));
prev_input = input;
prev_first_len = first_len;
data = data.offset(32);
len -= 32
}
if _mm256_testz_si256(error, error) == 0 {
return false;
}
let mut token4: i32 = _mm256_extract_epi32(prev_input, 7);
let token: *const i8 = &mut token4 as *mut i32 as *const i8;
let mut lookahead = 0;
if *token.offset(3) > 0xBFi32 as i8 {
lookahead = 1
} else if *token.offset(2) > 0xBFi32 as i8 {
lookahead = 2
} else if *token.offset(1) > 0xBFi32 as i8 {
lookahead = 3
}
data = data.offset(-(lookahead as isize));
len += lookahead
}
return libcore::is_utf8(core::slice::from_raw_parts(data, len));
}
}