#![allow(unsafe_code)]
use serde::{Deserialize, Serialize};
use serde_json::{Error as JsonError, Value};
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn validate_utf8_or_err(input: &[u8]) -> Result<(), JsonError> {
let ok = unsafe { crate::simd::json::validate_utf8_simd(input) };
if ok {
return Ok(());
}
Err(serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid UTF-8",
)))
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn strip_whitespace_simd_aware(input: &[u8], ws_positions: &[usize]) -> Vec<u8> {
let mut cleaned = Vec::with_capacity(input.len().saturating_sub(ws_positions.len()));
let mut state = StripState::default();
for (i, &byte) in input.iter().enumerate() {
if let Some(b) = state.next_output_byte(byte, i, ws_positions) {
cleaned.push(b);
}
}
cleaned
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
#[derive(Default)]
struct StripState {
in_string: bool,
escape_next: bool,
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
impl StripState {
fn next_output_byte(&mut self, byte: u8, index: usize, ws_positions: &[usize]) -> Option<u8> {
if self.escape_next {
self.escape_next = false;
return Some(byte);
}
if byte == b'\\' && self.in_string {
self.escape_next = true;
return Some(byte);
}
if byte == b'"' {
self.in_string = !self.in_string;
return Some(byte);
}
if !self.in_string && ws_positions.binary_search(&index).is_ok() {
return None;
}
Some(byte)
}
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
pub fn parse_json_fast<T: for<'de> Deserialize<'de>>(input: &[u8]) -> Result<T, JsonError> {
if !is_x86_feature_detected!("avx2") {
return serde_json::from_slice(input);
}
validate_utf8_or_err(input)?;
let ws_positions = unsafe { crate::simd::json::find_whitespace_simd(input) };
if ws_positions.len() < input.len() / 10 {
return serde_json::from_slice(input);
}
let cleaned = strip_whitespace_simd_aware(input, &ws_positions);
serde_json::from_slice(&cleaned)
}
#[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
pub fn parse_json_fast<T: for<'de> Deserialize<'de>>(input: &[u8]) -> Result<T, JsonError> {
serde_json::from_slice(input)
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
pub fn serialize_json_fast<T: Serialize>(value: &T) -> Result<Vec<u8>, JsonError> {
let json = serde_json::to_vec(value)?;
if is_x86_feature_detected!("avx2") {
unsafe {
if !crate::simd::json::validate_utf8_simd(&json) {
return Err(serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Generated invalid UTF-8",
)));
}
}
}
Ok(json)
}
#[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
pub fn serialize_json_fast<T: Serialize>(value: &T) -> Result<Vec<u8>, JsonError> {
serde_json::to_vec(value)
}
pub fn parse_json_batch<T: for<'de> Deserialize<'de>>(
inputs: &[&[u8]],
) -> Vec<Result<T, JsonError>> {
inputs.iter().map(|input| parse_json_fast(input)).collect()
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
struct PrettyPrintCtx {
indent: usize,
in_string: bool,
escape_next: bool,
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn push_open_bracket(result: &mut String, byte: u8, ctx: &mut PrettyPrintCtx) {
result.push(byte as char);
ctx.indent += 2;
result.push('\n');
result.push_str(&" ".repeat(ctx.indent));
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn push_close_bracket(result: &mut String, byte: u8, ctx: &mut PrettyPrintCtx) {
ctx.indent = ctx.indent.saturating_sub(2);
result.push('\n');
result.push_str(&" ".repeat(ctx.indent));
result.push(byte as char);
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn append_non_string_byte(result: &mut String, byte: u8, ctx: &mut PrettyPrintCtx) {
match byte {
b'{' | b'[' => push_open_bracket(result, byte, ctx),
b'}' | b']' => push_close_bracket(result, byte, ctx),
b',' => {
result.push(',');
result.push('\n');
result.push_str(&" ".repeat(ctx.indent));
},
b':' => result.push_str(": "),
b' ' | b'\t' | b'\n' | b'\r' => { },
_ => result.push(byte as char),
}
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn process_pretty_byte(
result: &mut String,
byte: u8,
i: usize,
escapes: &[usize],
ctx: &mut PrettyPrintCtx,
) {
if ctx.escape_next {
ctx.escape_next = false;
result.push(byte as char);
return;
}
if escapes.binary_search(&i).is_ok() {
if byte == b'\\' && ctx.in_string {
ctx.escape_next = true;
} else if byte == b'"' {
ctx.in_string = !ctx.in_string;
}
}
if ctx.in_string {
result.push(byte as char);
} else {
append_non_string_byte(result, byte, ctx);
}
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
fn pretty_print_simd(value: &Value) -> Result<String, JsonError> {
let compact = serde_json::to_vec(value)?;
let escapes = unsafe { crate::simd::json::find_escapes_simd(&compact) };
let mut result = String::with_capacity(compact.len() * 2);
let mut ctx = PrettyPrintCtx {
indent: 0,
in_string: false,
escape_next: false,
};
for (i, &byte) in compact.iter().enumerate() {
process_pretty_byte(&mut result, byte, i, &escapes, &mut ctx);
}
Ok(result)
}
pub fn pretty_print_fast(value: &Value) -> Result<String, JsonError> {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if is_x86_feature_detected!("avx2") {
return pretty_print_simd(value);
}
}
serde_json::to_string_pretty(value)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_parse_json_fast() {
let input = r#"{"name": "test", "value": 42, "nested": {"array": [1, 2, 3]}}"#;
let result: Value = parse_json_fast(input.as_bytes()).unwrap();
assert_eq!(result["name"], "test");
assert_eq!(result["value"], 42);
assert_eq!(result["nested"]["array"][0], 1);
}
#[test]
fn test_serialize_json_fast() {
let value = json!({
"message": "Hello, SIMD!",
"numbers": [1, 2, 3, 4, 5],
"nested": {
"key": "value"
}
});
let serialized = serialize_json_fast(&value).unwrap();
let parsed: Value = serde_json::from_slice(&serialized).unwrap();
assert_eq!(parsed, value);
}
#[test]
fn test_batch_parsing() {
let inputs = vec![
r#"{"id": 1}"#.as_bytes(),
r#"{"id": 2}"#.as_bytes(),
r#"{"id": 3}"#.as_bytes(),
];
let results: Vec<Result<Value, _>> = parse_json_batch(&inputs);
assert_eq!(results.len(), 3);
assert_eq!(results[0].as_ref().unwrap()["id"], 1);
assert_eq!(results[1].as_ref().unwrap()["id"], 2);
assert_eq!(results[2].as_ref().unwrap()["id"], 3);
}
#[test]
fn test_pretty_print() {
let value = json!({"compact": true, "array": [1, 2, 3]});
let pretty = pretty_print_fast(&value).unwrap();
assert!(pretty.contains('\n'));
assert!(pretty.contains(" "));
}
}