use crate::{
classification::{quotes::QuoteClassifiedIterator, ResumeClassifierState},
input::{error::InputError, InputBlockIterator},
FallibleIterator, BLOCK_SIZE,
};
use cfg_if::cfg_if;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum BracketType {
Square,
Curly,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Structural {
Closing(BracketType, usize),
Colon(usize),
Opening(BracketType, usize),
Comma(usize),
}
use Structural::*;
impl Structural {
#[inline(always)]
#[must_use]
pub fn idx(self) -> usize {
match self {
Closing(_, idx) | Colon(idx) | Opening(_, idx) | Comma(idx) => idx,
}
}
#[inline(always)]
#[must_use]
pub fn offset(self, amount: usize) -> Self {
match self {
Closing(b, idx) => Closing(b, idx + amount),
Colon(idx) => Colon(idx + amount),
Opening(b, idx) => Opening(b, idx + amount),
Comma(idx) => Comma(idx + amount),
}
}
#[inline(always)]
#[must_use]
pub fn is_closing(&self) -> bool {
matches!(self, Closing(_, _))
}
#[inline(always)]
#[must_use]
pub fn is_opening(&self) -> bool {
matches!(self, Opening(_, _))
}
}
pub trait StructuralIterator<'i, I, Q, const N: usize>:
FallibleIterator<Item = Structural, Error = InputError>
where
I: InputBlockIterator<'i, N>,
{
fn stop(self) -> ResumeClassifierState<'i, I, Q, N>;
fn resume(state: ResumeClassifierState<'i, I, Q, N>) -> Self;
fn turn_colons_off(&mut self);
fn turn_colons_on(&mut self, idx: usize);
fn turn_commas_off(&mut self);
fn turn_commas_on(&mut self, idx: usize);
fn turn_colons_and_commas_on(&mut self, idx: usize);
fn turn_colons_and_commas_off(&mut self);
}
cfg_if! {
if #[cfg(any(doc, not(feature = "simd")))] {
mod nosimd;
type ClassifierImpl<'a, I, Q, const N: usize> = nosimd::SequentialClassifier<'a, I, Q, N>;
}
else if #[cfg(simd = "avx2")] {
mod avx2;
type ClassifierImpl<'a, I, Q> = avx2::Avx2Classifier<'a, I, Q>;
}
else {
compile_error!("Target architecture is not supported by SIMD features of this crate. Disable the default `simd` feature.");
}
}
#[inline(always)]
pub fn classify_structural_characters<'i, I, Q>(iter: Q) -> impl StructuralIterator<'i, I, Q, BLOCK_SIZE>
where
I: InputBlockIterator<'i, BLOCK_SIZE>,
Q: QuoteClassifiedIterator<'i, I, BLOCK_SIZE>,
{
ClassifierImpl::new(iter)
}
#[inline(always)]
pub fn resume_structural_classification<'i, I, Q>(
state: ResumeClassifierState<'i, I, Q, BLOCK_SIZE>,
) -> impl StructuralIterator<'i, I, Q, BLOCK_SIZE>
where
I: InputBlockIterator<'i, BLOCK_SIZE>,
Q: QuoteClassifiedIterator<'i, I, BLOCK_SIZE>,
{
ClassifierImpl::resume(state)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
classification::quotes::classify_quoted_sequences,
input::{Input, OwnedBytes},
result::empty::EmptyRecorder,
};
#[test]
fn resumption_without_commas_or_colons() {
use BracketType::*;
use Structural::*;
let json = r#"{"a": [42, 36, { "b": { "c": 1, "d": 2 } }]}"#;
let json_string = json.to_owned();
let input = OwnedBytes::new(&json_string).unwrap();
let iter = input.iter_blocks(&EmptyRecorder);
let quotes = classify_quoted_sequences(iter);
let mut classifier = classify_structural_characters(quotes);
assert_eq!(Some(Opening(Curly, 0)), classifier.next().unwrap());
assert_eq!(Some(Opening(Square, 6)), classifier.next().unwrap());
let resume_state = classifier.stop();
let mut resumed_classifier = resume_structural_classification(resume_state);
assert_eq!(Some(Opening(Curly, 15)), resumed_classifier.next().unwrap());
assert_eq!(Some(Opening(Curly, 22)), resumed_classifier.next().unwrap());
}
#[test]
fn resumption_with_commas_but_no_colons() {
use BracketType::*;
use Structural::*;
let json = r#"{"a": [42, 36, { "b": { "c": 1, "d": 2 } }]}"#;
let json_string = json.to_owned();
let input = OwnedBytes::new(&json_string).unwrap();
let iter = input.iter_blocks(&EmptyRecorder);
let quotes = classify_quoted_sequences(iter);
let mut classifier = classify_structural_characters(quotes);
classifier.turn_commas_on(0);
assert_eq!(Some(Opening(Curly, 0)), classifier.next().unwrap());
assert_eq!(Some(Opening(Square, 6)), classifier.next().unwrap());
assert_eq!(Some(Comma(9)), classifier.next().unwrap());
assert_eq!(Some(Comma(13)), classifier.next().unwrap());
let resume_state = classifier.stop();
let mut resumed_classifier = resume_structural_classification(resume_state);
assert_eq!(Some(Opening(Curly, 15)), resumed_classifier.next().unwrap());
assert_eq!(Some(Opening(Curly, 22)), resumed_classifier.next().unwrap());
assert_eq!(Some(Comma(30)), resumed_classifier.next().unwrap());
}
#[test]
fn resumption_with_colons_but_no_commas() {
use BracketType::*;
use Structural::*;
let json = r#"{"a": [42, 36, { "b": { "c": 1, "d": 2 } }]}"#;
let json_string = json.to_owned();
let input = OwnedBytes::new(&json_string).unwrap();
let iter = input.iter_blocks(&EmptyRecorder);
let quotes = classify_quoted_sequences(iter);
let mut classifier = classify_structural_characters(quotes);
classifier.turn_colons_on(0);
assert_eq!(Some(Opening(Curly, 0)), classifier.next().unwrap());
assert_eq!(Some(Colon(4)), classifier.next().unwrap());
assert_eq!(Some(Opening(Square, 6)), classifier.next().unwrap());
let resume_state = classifier.stop();
let mut resumed_classifier = resume_structural_classification(resume_state);
assert_eq!(Some(Opening(Curly, 15)), resumed_classifier.next().unwrap());
assert_eq!(Some(Colon(20)), resumed_classifier.next().unwrap());
assert_eq!(Some(Opening(Curly, 22)), resumed_classifier.next().unwrap());
assert_eq!(Some(Colon(27)), resumed_classifier.next().unwrap());
}
#[test]
fn resumption_with_commas_and_colons() {
use BracketType::*;
use Structural::*;
let json = r#"{"a": [42, 36, { "b": { "c": 1, "d": 2 } }]}"#;
let json_string = json.to_owned();
let input = OwnedBytes::new(&json_string).unwrap();
let iter = input.iter_blocks(&EmptyRecorder);
let quotes = classify_quoted_sequences(iter);
let mut classifier = classify_structural_characters(quotes);
classifier.turn_commas_on(0);
classifier.turn_colons_on(0);
assert_eq!(Some(Opening(Curly, 0)), classifier.next().unwrap());
assert_eq!(Some(Colon(4)), classifier.next().unwrap());
assert_eq!(Some(Opening(Square, 6)), classifier.next().unwrap());
assert_eq!(Some(Comma(9)), classifier.next().unwrap());
assert_eq!(Some(Comma(13)), classifier.next().unwrap());
let resume_state = classifier.stop();
let mut resumed_classifier = resume_structural_classification(resume_state);
assert_eq!(Some(Opening(Curly, 15)), resumed_classifier.next().unwrap());
assert_eq!(Some(Colon(20)), resumed_classifier.next().unwrap());
assert_eq!(Some(Opening(Curly, 22)), resumed_classifier.next().unwrap());
assert_eq!(Some(Colon(27)), resumed_classifier.next().unwrap());
assert_eq!(Some(Comma(30)), resumed_classifier.next().unwrap());
}
}