Skip to content

Commit

Permalink
separate _parse_array and _parse_object
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Mar 25, 2024
1 parent c8a669e commit c3f6b6b
Showing 1 changed file with 69 additions and 41 deletions.
110 changes: 69 additions & 41 deletions src/python.rs
Expand Up @@ -78,16 +78,8 @@ impl<'j> PythonParser<'j> {
match $result {
Ok(k) => k,
Err(e) => {
return if self.allow_partial {
match e.error_type {
JsonErrorType::EofWhileParsingList
| JsonErrorType::EofWhileParsingObject
| JsonErrorType::EofWhileParsingString
| JsonErrorType::EofWhileParsingValue
| JsonErrorType::ExpectedListCommaOrEnd
| JsonErrorType::ExpectedObjectCommaOrEnd => Ok($partial_value.into_any()),
_ => Err(e),
}
return if self._allow_partial_err(&e) {
Ok($partial_value.into_any())
} else {
Err(e)
}
Expand Down Expand Up @@ -116,17 +108,10 @@ impl<'j> PythonParser<'j> {
Peek::Array => {
let list = if let Some(peek_first) = tri!(self.parser.array_first(), PyList::empty_bound(py)) {
let mut vec: SmallVec<[Bound<'_, PyAny>; 8]> = SmallVec::with_capacity(8);
let v = tri!(
self._check_take_value::<StringCache>(py, peek_first),
PyList::empty_bound(py)
);
vec.push(v);
while let Some(peek) = tri!(self.parser.array_step(), PyList::new_bound(py, vec)) {
let v = tri!(
self._check_take_value::<StringCache>(py, peek),
PyList::new_bound(py, vec)
);
vec.push(v);
if let Err(e) = self._parse_array::<StringCache>(py, peek_first, &mut vec) {
if !self._allow_partial_err(&e) {
return Err(e);
}
}
PyList::new_bound(py, vec)
} else {
Expand All @@ -136,26 +121,9 @@ impl<'j> PythonParser<'j> {
}
Peek::Object => {
let dict = PyDict::new_bound(py);

let set_item = |key: Bound<'py, PyAny>, value: Bound<'py, PyAny>| {
let r = unsafe { ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr()) };
// AFAIK this shouldn't happen since the key will always be a string which is hashable
// we panic here rather than returning a result and using `?` below as it's up to 14% faster
// presumably because there are fewer branches
if r == -1 {
panic!("PyDict_SetItem failed")
}
};
if let Some(first_key) = tri!(self.parser.object_first::<StringDecoder>(&mut self.tape), dict) {
let first_key = StringCache::get(py, first_key.as_str());
let peek = tri!(self.parser.peek(), dict);
let first_value = tri!(self._check_take_value::<StringCache>(py, peek), dict);
set_item(first_key, first_value);
while let Some(key) = tri!(self.parser.object_step::<StringDecoder>(&mut self.tape), dict) {
let key = StringCache::get(py, key.as_str());
let peek = tri!(self.parser.peek(), dict);
let value = tri!(self._check_take_value::<StringCache>(py, peek), dict);
set_item(key, value);
if let Err(e) = self._parse_object::<StringCache>(py, &dict) {
if !self._allow_partial_err(&e) {
return Err(e);

Check warning on line 126 in src/python.rs

View check run for this annotation

Codecov / codecov/patch

src/python.rs#L126

Added line #L126 was not covered by tests
}
}
Ok(dict.into_any())
Expand All @@ -180,6 +148,66 @@ impl<'j> PythonParser<'j> {
}
}

fn _parse_array<'py, StringCache: StringMaybeCache>(
&mut self,
py: Python<'py>,
peek_first: Peek,
vec: &mut SmallVec<[Bound<'py, PyAny>; 8]>,
) -> JsonResult<()> {
let v = self._check_take_value::<StringCache>(py, peek_first)?;
vec.push(v);
while let Some(peek) = self.parser.array_step()? {
let v = self._check_take_value::<StringCache>(py, peek)?;
vec.push(v);
}
Ok(())
}

fn _parse_object<'py, StringCache: StringMaybeCache>(
&mut self,
py: Python<'py>,
dict: &Bound<'py, PyDict>,
) -> JsonResult<()> {
let set_item = |key: Bound<'py, PyAny>, value: Bound<'py, PyAny>| {
let r = unsafe { ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr()) };
// AFAIK this shouldn't happen since the key will always be a string which is hashable
// we panic here rather than returning a result and using `?` below as it's up to 14% faster
// presumably because there are fewer branches
if r == -1 {
panic!("PyDict_SetItem failed")

Check warning on line 177 in src/python.rs

View check run for this annotation

Codecov / codecov/patch

src/python.rs#L177

Added line #L177 was not covered by tests
}
};
if let Some(first_key) = self.parser.object_first::<StringDecoder>(&mut self.tape)? {
let first_key = StringCache::get(py, first_key.as_str());
let peek = self.parser.peek()?;
let first_value = self._check_take_value::<StringCache>(py, peek)?;
set_item(first_key, first_value);
while let Some(key) = self.parser.object_step::<StringDecoder>(&mut self.tape)? {
let key = StringCache::get(py, key.as_str());
let peek = self.parser.peek()?;
let value = self._check_take_value::<StringCache>(py, peek)?;
set_item(key, value);
}
}

Check warning on line 191 in src/python.rs

View check run for this annotation

Codecov / codecov/patch

src/python.rs#L191

Added line #L191 was not covered by tests
Ok(())
}

fn _allow_partial_err(&self, e: &JsonError) -> bool {
if self.allow_partial {
matches!(

Check warning on line 197 in src/python.rs

View check run for this annotation

Codecov / codecov/patch

src/python.rs#L197

Added line #L197 was not covered by tests
e.error_type,
JsonErrorType::EofWhileParsingList
| JsonErrorType::EofWhileParsingObject
| JsonErrorType::EofWhileParsingString
| JsonErrorType::EofWhileParsingValue
| JsonErrorType::ExpectedListCommaOrEnd
| JsonErrorType::ExpectedObjectCommaOrEnd
)
} else {
false
}
}

fn _check_take_value<'py, StringCache: StringMaybeCache>(
&mut self,
py: Python<'py>,
Expand Down

0 comments on commit c3f6b6b

Please sign in to comment.