use hakmem::lanes::U8x16;
use hakmem::prelude::*;
const LO: [u8; 16] = [16, 0, 0, 0, 0, 0, 0, 0, 0, 32, 34, 12, 1, 44, 0, 0];
const HI: [u8; 16] = [32, 0, 17, 2, 0, 4, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0];
fn masks(block: &[u8; 64]) -> (u64, u64, u64) {
let (mut structural, mut quotes, mut backslashes) = (0u64, 0u64, 0u64);
for (i, chunk) in block.as_chunks::<16>().0.iter().enumerate() {
let lanes = U8x16::load(chunk);
let class = lanes.lut16_nibbles(LO, HI);
let s = class
.and(U8x16::splat(15))
.cmp_eq(U8x16::zero())
.not()
.to_bitmask();
let q = lanes.cmp_eq(U8x16::splat(b'"')).to_bitmask();
let b = lanes.cmp_eq(U8x16::splat(b'\\')).to_bitmask();
structural |= u64::from(s) << (16 * i);
quotes |= u64::from(q) << (16 * i);
backslashes |= u64::from(b) << (16 * i);
}
(structural, quotes, backslashes)
}
fn structure(json: &[u8]) -> Vec<usize> {
let (mut out, mut escape_carry, mut in_string) = (Vec::new(), false, false);
for (b, chunk) in json.chunks(64).enumerate() {
let mut block = [b' '; 64];
block[..chunk.len()].copy_from_slice(chunk);
let (structural, quotes, backslashes) = masks(&block);
let escaped;
(escaped, escape_carry) = backslashes.find_escaped(escape_carry);
let strings;
(strings, in_string) = (quotes & !escaped).prefix_xor_carry(in_string);
out.extend(
(structural & !strings)
.positions()
.map(|p| 64 * b + p as usize),
);
}
out
}
fn main() {
let json = br#"{"name": "a [tricky] \"string\", with: {braces}", "note": "hmm \" the quote is byte 64, [more] after", "list": [1, 2, {"k": "v"}]}"#;
let found = structure(json);
let expected: Vec<usize> = json
.iter()
.enumerate()
.filter(|&(_, c)| b"{}[]:,".contains(c))
.map(|(i, _)| i)
.filter(|&i| {
let mut inside = false;
let mut escaped = false;
for &c in &json[..i] {
match c {
b'\\' if !escaped => escaped = true,
b'"' if !escaped => inside = !inside,
_ => escaped = false,
}
if c != b'\\' {
escaped = false;
}
}
!inside
})
.collect();
assert_eq!(found, expected);
let shown: String = found.iter().map(|&i| json[i] as char).collect();
println!(
"{} structural characters outside strings: {shown}",
found.len()
);
}