#![cfg_attr(feature = "commas", doc = "Structural::Comma(16),")]
use std::marker::PhantomData;
use crate::{
debug,
depth::{resume_depth_classification, DepthBlock, DepthIterator, DepthIteratorResumeOutcome},
quotes::{QuoteClassifiedIterator, ResumeClassifierState},
};
use cfg_if::cfg_if;
use replace_with::replace_with_or_abort;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Structural {
Closing(usize),
Colon(usize),
Opening(usize),
#[cfg(feature = "commas")]
Comma(usize),
}
use Structural::*;
impl Structural {
#[inline(always)]
#[must_use]
pub fn idx(self) -> usize {
match self {
Closing(idx) => idx,
Colon(idx) => idx,
Opening(idx) => idx,
#[cfg(feature = "commas")]
Comma(idx) => idx,
}
}
#[inline(always)]
#[must_use]
pub fn offset(self, amount: usize) -> Self {
match self {
Closing(idx) => Closing(idx + amount),
Colon(idx) => Colon(idx + amount),
Opening(idx) => Opening(idx + amount),
#[cfg(feature = "commas")]
Comma(idx) => Comma(idx + amount),
}
}
}
pub(crate) struct ClassifierWithSkipping<'b, Q, I>
where
Q: QuoteClassifiedIterator<'b>,
I: StructuralIterator<'b, Q>,
{
classifier: I,
phantom: PhantomData<&'b Q>,
}
impl<'b, Q, I> ClassifierWithSkipping<'b, Q, I>
where
Q: QuoteClassifiedIterator<'b>,
I: StructuralIterator<'b, Q>,
{
pub(crate) fn new(classifier: I) -> Self {
Self {
classifier,
phantom: PhantomData,
}
}
pub(crate) fn skip(&mut self, opening: u8) {
debug!("Skipping");
replace_with_or_abort(&mut self.classifier, |classifier| {
let resume_state = classifier.stop();
let DepthIteratorResumeOutcome(first_vector, mut depth_classifier) =
resume_depth_classification(resume_state, opening);
let mut current_vector = first_vector.or_else(|| depth_classifier.next());
let mut current_depth = 1;
'outer: while let Some(ref mut vector) = current_vector {
vector.add_depth(current_depth);
debug!("Fetched vector, current depth is {current_depth}");
debug!("Estimate: {}", vector.estimate_lowest_possible_depth());
if vector.estimate_lowest_possible_depth() <= 0 {
while vector.advance_to_next_depth_decrease() {
if vector.get_depth() == 0 {
debug!("Encountered depth 0, breaking.");
break 'outer;
}
}
}
current_depth = vector.depth_at_end();
current_vector = depth_classifier.next();
}
debug!("Skipping complete, resuming structural classification.");
let resume_state = depth_classifier.stop(current_vector);
debug!("Finished at {}", resume_state.get_idx());
I::resume(resume_state)
});
}
pub(crate) fn stop(self) -> ResumeClassifierState<'b, Q> {
self.classifier.stop()
}
}
impl<'b, Q, I> std::ops::Deref for ClassifierWithSkipping<'b, Q, I>
where
Q: QuoteClassifiedIterator<'b>,
I: StructuralIterator<'b, Q>,
{
type Target = I;
fn deref(&self) -> &Self::Target {
&self.classifier
}
}
impl<'b, Q, I> std::ops::DerefMut for ClassifierWithSkipping<'b, Q, I>
where
Q: QuoteClassifiedIterator<'b>,
I: StructuralIterator<'b, Q>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.classifier
}
}
pub trait StructuralIterator<'a, I: QuoteClassifiedIterator<'a>>:
Iterator<Item = Structural> + 'a
{
fn stop(self) -> ResumeClassifierState<'a, I>;
fn resume(state: ResumeClassifierState<'a, I>) -> Self;
}
cfg_if! {
if #[cfg(any(doc, not(feature = "simd")))] {
mod nosimd;
use nosimd::*;
#[inline(always)]
pub fn classify_structural_characters<'a, I: QuoteClassifiedIterator<'a>>(
iter: I,
) -> impl StructuralIterator<'a, I> {
SequentialClassifier::new(iter)
}
#[inline(always)]
pub fn resume_structural_classification<'a, I: QuoteClassifiedIterator<'a>>(
state: ResumeClassifierState<'a, I>
) -> impl StructuralIterator<'a, I> {
SequentialClassifier::resume(state)
}
}
else if #[cfg(simd = "avx2")] {
mod avx2;
use avx2::Avx2Classifier;
#[inline(always)]
pub fn classify_structural_characters<'a, I: QuoteClassifiedIterator<'a>>(
iter: I,
) -> impl StructuralIterator<'a, I> {
Avx2Classifier::new(iter)
}
#[inline(always)]
pub fn resume_structural_classification<'a, I: QuoteClassifiedIterator<'a>>(
state: ResumeClassifierState<'a, I>
) -> impl StructuralIterator<'a, I> {
Avx2Classifier::resume(state)
}
}
else {
compile_error!("Target architecture is not supported by SIMD features of this crate. Disable the default `simd` feature.");
}
}
#[cfg(test)]
mod tests {
use crate::quotes::classify_quoted_sequences;
use super::*;
use aligners::AlignedBytes;
#[test]
fn resumption() {
use Structural::*;
let json = r#"{"a": [42, 36, { "b": { "c": 1, "d": 2 } }]}"#;
let bytes = AlignedBytes::new_padded(json.as_bytes());
let quotes = classify_quoted_sequences(&bytes);
let mut classifier = classify_structural_characters(quotes);
assert_eq!(Some(Opening(0)), classifier.next());
assert_eq!(Some(Colon(4)), classifier.next());
assert_eq!(Some(Opening(6)), classifier.next());
#[cfg(feature = "commas")]
assert_eq!(Some(Comma(9)), classifier.next());
#[cfg(feature = "commas")]
assert_eq!(Some(Comma(13)), classifier.next());
let resume_state = classifier.stop();
let mut resumed_classifier = resume_structural_classification(resume_state);
assert_eq!(Some(Opening(15)), resumed_classifier.next());
assert_eq!(Some(Colon(20)), resumed_classifier.next());
assert_eq!(Some(Opening(22)), resumed_classifier.next());
assert_eq!(Some(Colon(27)), resumed_classifier.next());
#[cfg(feature = "commas")]
assert_eq!(Some(Comma(30)), resumed_classifier.next());
}
}