Skip to content

Commit

Permalink
try bitvec to avoid allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed May 29, 2024
1 parent 1194b3e commit c54e14b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 21 deletions.
1 change: 1 addition & 0 deletions crates/jiter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ahash = "0.8.0"
smallvec = "1.11.0"
pyo3 = { version = "0.21.0", optional = true }
lexical-parse-float = { version = "0.8.5", features = ["format"] }
bitvec = "1.0.1"

[features]
python = ["dep:pyo3", "dep:pyo3-build-config"]
Expand Down
48 changes: 27 additions & 21 deletions crates/jiter/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,22 @@ pub(crate) fn take_value_skip(
recursion_limit: u8,
allow_inf_nan: bool,
) -> JsonResult<()> {
#[derive(Copy, Clone)]
enum Recursion {
Array,
Object,
}

let mut recursion_stack: SmallVec<[Recursion; 8]> = smallvec::smallvec![];
let mut recursion_stack = bitvec::bitarr![0; 256];
let recursion_limit: usize = recursion_limit.into();
let mut current_recursion = 0;

const ARRAY: bool = false;
const OBJECT: bool = true;

macro_rules! push_recursion {
($value:expr) => {
if current_recursion >= recursion_limit {
return Err(json_error!(RecursionLimitExceeded, parser.index));
}
recursion_stack.set(current_recursion, $value);
current_recursion += 1
};
}

loop {
match peek {
Expand All @@ -256,10 +264,7 @@ pub(crate) fn take_value_skip(
}
Peek::Array => {
if let Some(next_peek) = parser.array_first()? {
recursion_stack.push(Recursion::Array);
if recursion_stack.len() > recursion_limit {
return crate::errors::json_err!(RecursionLimitExceeded, parser.index);
}
push_recursion!(ARRAY);
peek = next_peek;

// immediately jump to process the next value
Expand All @@ -268,10 +273,7 @@ pub(crate) fn take_value_skip(
}
Peek::Object => {
if parser.object_first::<StringDecoderRange>(tape)?.is_some() {
recursion_stack.push(Recursion::Object);
if recursion_stack.len() > recursion_limit {
return crate::errors::json_err!(RecursionLimitExceeded, parser.index);
}
push_recursion!(OBJECT);
peek = parser.peek()?;

// immediately jump to process the next value
Expand All @@ -293,22 +295,26 @@ pub(crate) fn take_value_skip(

// now try to advance position in the current array or object
peek = loop {
match recursion_stack.last().copied() {
Some(Recursion::Array) => {
let next_recursion = match current_recursion.checked_sub(1) {
Some(r) => r,
// no recursion left, we are done
None => return Ok(()),
};

match recursion_stack[next_recursion] {
ARRAY => {
if let Some(next_peek) = parser.array_step()? {
break next_peek;
}
}
Some(Recursion::Object) => {
OBJECT => {
if parser.object_step::<StringDecoderRange>(tape)?.is_some() {
break parser.peek()?;
}
}
// no more recursion, all done
None => return Ok(()),
}

recursion_stack.pop();
current_recursion = next_recursion;
};
}
}

0 comments on commit c54e14b

Please sign in to comment.