#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::json::simple::{SemiIndex as SimpleSemiIndex, State as SimpleState};
use crate::json::standard::{SemiIndex, State};
use crate::json::BitWriter;
const DOUBLE_QUOTE: i8 = b'"' as i8;
const BACKSLASH: i8 = b'\\' as i8;
const OPEN_BRACE: i8 = b'{' as i8;
const CLOSE_BRACE: i8 = b'}' as i8;
const OPEN_BRACKET: i8 = b'[' as i8;
const CLOSE_BRACKET: i8 = b']' as i8;
const COMMA: i8 = b',' as i8;
const COLON: i8 = b':' as i8;
#[derive(Debug, Clone, Copy)]
struct CharClass {
quotes: u32,
backslashes: u32,
opens: u32,
closes: u32,
delims: u32,
value_chars: u32,
}
#[inline]
#[target_feature(enable = "avx2")]
#[cfg(target_arch = "x86_64")]
unsafe fn classify_chars(chunk: __m256i) -> CharClass {
unsafe {
let v_quote = _mm256_set1_epi8(DOUBLE_QUOTE);
let v_backslash = _mm256_set1_epi8(BACKSLASH);
let v_open_brace = _mm256_set1_epi8(OPEN_BRACE);
let v_close_brace = _mm256_set1_epi8(CLOSE_BRACE);
let v_open_bracket = _mm256_set1_epi8(OPEN_BRACKET);
let v_close_bracket = _mm256_set1_epi8(CLOSE_BRACKET);
let v_comma = _mm256_set1_epi8(COMMA);
let v_colon = _mm256_set1_epi8(COLON);
let eq_quote = _mm256_cmpeq_epi8(chunk, v_quote);
let eq_backslash = _mm256_cmpeq_epi8(chunk, v_backslash);
let eq_open_brace = _mm256_cmpeq_epi8(chunk, v_open_brace);
let eq_close_brace = _mm256_cmpeq_epi8(chunk, v_close_brace);
let eq_open_bracket = _mm256_cmpeq_epi8(chunk, v_open_bracket);
let eq_close_bracket = _mm256_cmpeq_epi8(chunk, v_close_bracket);
let eq_comma = _mm256_cmpeq_epi8(chunk, v_comma);
let eq_colon = _mm256_cmpeq_epi8(chunk, v_colon);
let opens = _mm256_or_si256(eq_open_brace, eq_open_bracket);
let closes = _mm256_or_si256(eq_close_brace, eq_close_bracket);
let delims = _mm256_or_si256(eq_comma, eq_colon);
let v_a_lower = _mm256_set1_epi8(b'a' as i8);
let v_z_range = _mm256_set1_epi8((b'z' - b'a') as i8);
let sub_a = _mm256_sub_epi8(chunk, v_a_lower);
let lowercase = unsigned_le(sub_a, v_z_range);
let v_a_upper = _mm256_set1_epi8(b'A' as i8);
let sub_a_upper = _mm256_sub_epi8(chunk, v_a_upper);
let uppercase = unsigned_le(sub_a_upper, v_z_range);
let v_0 = _mm256_set1_epi8(b'0' as i8);
let v_9_range = _mm256_set1_epi8(9); let sub_0 = _mm256_sub_epi8(chunk, v_0);
let digit = unsigned_le(sub_0, v_9_range);
let v_period = _mm256_set1_epi8(b'.' as i8);
let v_minus = _mm256_set1_epi8(b'-' as i8);
let v_plus = _mm256_set1_epi8(b'+' as i8);
let eq_period = _mm256_cmpeq_epi8(chunk, v_period);
let eq_minus = _mm256_cmpeq_epi8(chunk, v_minus);
let eq_plus = _mm256_cmpeq_epi8(chunk, v_plus);
let alpha = _mm256_or_si256(lowercase, uppercase);
let alnum = _mm256_or_si256(alpha, digit);
let punct = _mm256_or_si256(_mm256_or_si256(eq_period, eq_minus), eq_plus);
let value_chars = _mm256_or_si256(alnum, punct);
CharClass {
quotes: _mm256_movemask_epi8(eq_quote) as u32,
backslashes: _mm256_movemask_epi8(eq_backslash) as u32,
opens: _mm256_movemask_epi8(opens) as u32,
closes: _mm256_movemask_epi8(closes) as u32,
delims: _mm256_movemask_epi8(delims) as u32,
value_chars: _mm256_movemask_epi8(value_chars) as u32,
}
}
}
#[inline]
#[target_feature(enable = "avx2")]
#[cfg(target_arch = "x86_64")]
unsafe fn unsigned_le(a: __m256i, b: __m256i) -> __m256i {
let min_ab = _mm256_min_epu8(a, b);
_mm256_cmpeq_epi8(min_ab, a)
}
#[inline]
fn process_chunk_standard(
class: CharClass,
mut state: State,
ib: &mut BitWriter,
bp: &mut BitWriter,
bytes: &[u8],
) -> State {
for i in 0..bytes.len().min(32) {
let bit = 1u32 << i;
let is_quote = (class.quotes & bit) != 0;
let is_backslash = (class.backslashes & bit) != 0;
let is_open = (class.opens & bit) != 0;
let is_close = (class.closes & bit) != 0;
let is_delim = (class.delims & bit) != 0;
let is_value_char = (class.value_chars & bit) != 0;
match state {
State::InJson => {
if is_open {
ib.write_1();
bp.write_1();
} else if is_close {
ib.write_0();
bp.write_0();
} else if is_delim {
ib.write_0();
} else if is_value_char {
ib.write_1();
bp.write_1();
bp.write_0();
state = State::InValue;
} else if is_quote {
ib.write_1();
bp.write_1();
bp.write_0();
state = State::InString;
} else {
ib.write_0();
}
}
State::InString => {
ib.write_0();
if is_quote {
state = State::InJson;
} else if is_backslash {
state = State::InEscape;
}
}
State::InEscape => {
ib.write_0();
state = State::InString;
}
State::InValue => {
if is_open {
ib.write_1();
bp.write_1();
state = State::InJson;
} else if is_close {
ib.write_0();
bp.write_0();
state = State::InJson;
} else if is_delim {
ib.write_0();
state = State::InJson;
} else if is_value_char {
ib.write_0();
} else {
ib.write_0();
state = State::InJson;
}
}
}
}
state
}
#[cfg(target_arch = "x86_64")]
pub fn build_semi_index_standard(json: &[u8]) -> SemiIndex {
unsafe { build_semi_index_standard_avx2(json) }
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn build_semi_index_standard_avx2(json: &[u8]) -> SemiIndex {
unsafe {
let word_capacity = json.len().div_ceil(64);
let mut ib = BitWriter::with_capacity(word_capacity);
let mut bp = BitWriter::with_capacity(word_capacity * 2);
let mut state = State::InJson;
let mut offset = 0;
while offset + 32 <= json.len() {
let chunk = _mm256_loadu_si256(json.as_ptr().add(offset) as *const __m256i);
let class = classify_chars(chunk);
state =
process_chunk_standard(class, state, &mut ib, &mut bp, &json[offset..offset + 32]);
offset += 32;
}
if offset < json.len() {
let mut padded = [0u8; 32];
let remaining = json.len() - offset;
padded[..remaining].copy_from_slice(&json[offset..]);
let chunk = _mm256_loadu_si256(padded.as_ptr() as *const __m256i);
let class = classify_chars(chunk);
state = process_chunk_standard(class, state, &mut ib, &mut bp, &json[offset..]);
}
SemiIndex {
state,
ib: ib.finish(),
bp: bp.finish(),
}
}
}
#[inline]
fn process_chunk_simple(
class: CharClass,
mut state: SimpleState,
ib: &mut BitWriter,
bp: &mut BitWriter,
bytes: &[u8],
) -> SimpleState {
for i in 0..bytes.len().min(32) {
let bit = 1u32 << i;
let is_quote = (class.quotes & bit) != 0;
let is_backslash = (class.backslashes & bit) != 0;
let is_open = (class.opens & bit) != 0;
let is_close = (class.closes & bit) != 0;
let is_delim = (class.delims & bit) != 0;
match state {
SimpleState::InJson => {
if is_open {
bp.write_1();
bp.write_1();
ib.write_1();
} else if is_close {
bp.write_0();
bp.write_0();
ib.write_1();
} else if is_delim {
bp.write_0();
bp.write_1();
ib.write_1();
} else if is_quote {
ib.write_0();
state = SimpleState::InString;
} else {
ib.write_0();
}
}
SimpleState::InString => {
ib.write_0();
if is_quote {
state = SimpleState::InJson;
} else if is_backslash {
state = SimpleState::InEscape;
}
}
SimpleState::InEscape => {
ib.write_0();
state = SimpleState::InString;
}
}
}
state
}
#[cfg(target_arch = "x86_64")]
pub fn build_semi_index_simple(json: &[u8]) -> SimpleSemiIndex {
unsafe { build_semi_index_simple_avx2(json) }
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn build_semi_index_simple_avx2(json: &[u8]) -> SimpleSemiIndex {
unsafe {
let word_capacity = json.len().div_ceil(64);
let mut ib = BitWriter::with_capacity(word_capacity);
let mut bp = BitWriter::with_capacity(word_capacity * 2);
let mut state = SimpleState::InJson;
let mut offset = 0;
while offset + 32 <= json.len() {
let chunk = _mm256_loadu_si256(json.as_ptr().add(offset) as *const __m256i);
let class = classify_chars(chunk);
state =
process_chunk_simple(class, state, &mut ib, &mut bp, &json[offset..offset + 32]);
offset += 32;
}
if offset < json.len() {
let mut padded = [0u8; 32];
let remaining = json.len() - offset;
padded[..remaining].copy_from_slice(&json[offset..]);
let chunk = _mm256_loadu_si256(padded.as_ptr() as *const __m256i);
let class = classify_chars(chunk);
state = process_chunk_simple(class, state, &mut ib, &mut bp, &json[offset..]);
}
SimpleSemiIndex {
state,
ib: ib.finish(),
bp: bp.finish(),
}
}
}
#[cfg(all(test, target_arch = "x86_64"))]
mod tests {
use super::*;
fn get_bit(words: &[u64], i: usize) -> bool {
let word_idx = i / 64;
let bit_idx = i % 64;
if word_idx < words.len() {
(words[word_idx] >> bit_idx) & 1 == 1
} else {
false
}
}
fn bits_to_string(words: &[u64], n: usize) -> String {
(0..n)
.map(|i| if get_bit(words, i) { '1' } else { '0' })
.collect()
}
#[test]
fn test_classify_chars() {
if !is_x86_feature_detected!("avx2") {
return;
}
unsafe {
let input = br#"{"hello":123} "#; let chunk = _mm256_loadu_si256(input.as_ptr() as *const __m256i);
let class = classify_chars(chunk);
assert_ne!(class.opens & (1 << 0), 0, "'{{' at position 0");
assert_ne!(class.closes & (1 << 12), 0, "'}}' at position 12");
assert_ne!(class.quotes & (1 << 1), 0, "'\"' at position 1");
assert_ne!(class.quotes & (1 << 7), 0, "'\"' at position 7");
assert_ne!(class.delims & (1 << 8), 0, "':' at position 8");
assert_ne!(class.value_chars & (1 << 9), 0, "'1' at position 9");
assert_ne!(class.value_chars & (1 << 10), 0, "'2' at position 10");
assert_ne!(class.value_chars & (1 << 11), 0, "'3' at position 11");
}
}
#[test]
fn test_avx2_matches_scalar_empty_object() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = b"{}";
let simd_result = build_semi_index_standard(json);
let scalar_result = crate::json::standard::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len())
);
assert_eq!(simd_result.state, scalar_result.state);
}
#[test]
fn test_avx2_matches_scalar_simple_object() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"a":"b"}"#;
let simd_result = build_semi_index_standard(json);
let scalar_result = crate::json::standard::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch"
);
assert_eq!(simd_result.state, scalar_result.state);
}
#[test]
fn test_avx2_matches_scalar_long_input() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"name":"value","number":12345,"array":[1,2,3]}"#;
let simd_result = build_semi_index_standard(json);
let scalar_result = crate::json::standard::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch for long input"
);
assert_eq!(simd_result.state, scalar_result.state);
}
#[test]
fn test_avx2_matches_scalar_exact_32_bytes() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"a":"b","c":"d","e":"fghijklm"}"#;
assert_eq!(json.len(), 32, "Test JSON should be exactly 32 bytes");
let simd_result = build_semi_index_standard(json);
let scalar_result = crate::json::standard::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch for 32-byte input"
);
}
#[test]
fn test_avx2_matches_scalar_64_bytes() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"k1":"v1","k2":"v2","k3":"v3","k4":"v4","k5":"val5","k6":"vvv"}"#;
assert_eq!(json.len(), 64, "Test JSON should be exactly 64 bytes");
let simd_result = build_semi_index_standard(json);
let scalar_result = crate::json::standard::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch for 64-byte input"
);
}
#[test]
fn test_simple_avx2_matches_scalar_empty_object() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = b"{}";
let simd_result = build_semi_index_simple(json);
let scalar_result = crate::json::simple::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch"
);
assert_eq!(
bits_to_string(&simd_result.bp, 4),
bits_to_string(&scalar_result.bp, 4),
"BP mismatch"
);
assert_eq!(simd_result.state, scalar_result.state);
}
#[test]
fn test_simple_avx2_matches_scalar_simple_object() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"a":"b"}"#;
let simd_result = build_semi_index_simple(json);
let scalar_result = crate::json::simple::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch"
);
assert_eq!(
bits_to_string(&simd_result.bp, 6),
bits_to_string(&scalar_result.bp, 6),
"BP mismatch"
);
}
#[test]
fn test_simple_avx2_matches_scalar_long_input() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"name":"value","number":12345,"array":[1,2,3]}"#;
let simd_result = build_semi_index_simple(json);
let scalar_result = crate::json::simple::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch for long input"
);
assert_eq!(simd_result.state, scalar_result.state);
}
#[test]
fn test_simple_avx2_matches_scalar_exact_32_bytes() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"a":"b","c":"d","e":"fghijklm"}"#;
assert_eq!(json.len(), 32, "Test JSON should be exactly 32 bytes");
let simd_result = build_semi_index_simple(json);
let scalar_result = crate::json::simple::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch for 32-byte input"
);
}
#[test]
fn test_simple_avx2_matches_scalar_escaped() {
if !is_x86_feature_detected!("avx2") {
return;
}
let json = br#"{"a":"b\"c"}"#;
let simd_result = build_semi_index_simple(json);
let scalar_result = crate::json::simple::build_semi_index(json);
assert_eq!(
bits_to_string(&simd_result.ib, json.len()),
bits_to_string(&scalar_result.ib, json.len()),
"IB mismatch"
);
assert_eq!(simd_result.state, scalar_result.state);
}
}