1use simdjson_sys::{SIMDJSON_PADDING, dom_ffi};
9use snafu::prelude::*;
10
11use crate::{error::SimdJsonError, tape::TapeRef};
12
13#[derive(Debug, Snafu)]
15#[snafu(visibility(pub(crate)))]
16pub enum Error {
17 #[snafu(display("simdjson error: {source}"))]
18 SimdJson { source: SimdJsonError },
19
20 #[snafu(display(
21 "the JSON input buffer must have at least {} bytes of extra capacity",
22 SIMDJSON_PADDING
23 ))]
24 InsufficientPadding,
25
26 #[snafu(display("tape error: {source}"))]
27 Tape {
28 #[snafu(backtrace)]
29 source: crate::tape::Error,
30 },
31}
32
33pub type Result<T, E = Error> = std::result::Result<T, E>;
34
35impl From<crate::tape::Error> for Error {
36 fn from(source: crate::tape::Error) -> Self {
37 Error::Tape { source }
38 }
39}
40
41pub struct Parser {
56 inner: cxx::UniquePtr<dom_ffi::parser>,
57 padded: Vec<u8>,
59 valid: bool,
60}
61
62impl Default for Parser {
63 fn default() -> Self {
64 Self::new(simdjson_sys::SIMDJSON_MAXSIZE_BYTES)
65 }
66}
67
68impl Parser {
69 pub fn new(max_capacity: usize) -> Self {
71 Self {
72 inner: dom_ffi::parser_new(max_capacity),
73 padded: Vec::new(),
74 valid: false,
75 }
76 }
77
78 pub unsafe fn parse_bytes_with_padding(&mut self, json: &[u8]) -> Result<TapeRef<'_>> {
93 self.valid = false;
94 let code = unsafe { dom_ffi::parser_parse(self.inner.pin_mut(), json, false) };
96 SimdJsonError::check_code(code).context(SimdJsonSnafu)?;
97 self.valid = true;
98 Ok(self.tape_ref())
99 }
100
101 pub fn parse_str(&mut self, json: &str) -> Result<TapeRef<'_>> {
106 self.parse_bytes(json.as_bytes())
107 }
108
109 pub fn parse_bytes(&mut self, json: &[u8]) -> Result<TapeRef<'_>> {
111 self.valid = false;
117 let json_len = json.len();
118 self.padded.clear();
119 self.padded.reserve(json_len + SIMDJSON_PADDING);
120 self.padded.extend_from_slice(json);
121 self.padded
123 .extend(std::iter::repeat_n(0u8, SIMDJSON_PADDING));
124
125 let code =
128 unsafe { dom_ffi::parser_parse(self.inner.pin_mut(), &self.padded[..json_len], false) };
129 SimdJsonError::check_code(code).context(SimdJsonSnafu)?;
130 self.valid = true;
131 Ok(self.tape_ref())
132 }
133
134 pub fn parse_padded(&mut self, json: &mut String) -> Result<TapeRef<'_>> {
139 self.valid = false;
140 ensure!(
141 json.capacity() - json.len() >= SIMDJSON_PADDING,
142 InsufficientPaddingSnafu
143 );
144 let len = json.len();
145 json.extend(std::iter::repeat_n('\0', SIMDJSON_PADDING));
146 json.truncate(len);
147 let code = unsafe { dom_ffi::parser_parse(self.inner.pin_mut(), json.as_bytes(), false) };
149 SimdJsonError::check_code(code).context(SimdJsonSnafu)?;
150 self.valid = true;
151 Ok(self.tape_ref())
152 }
153
154 pub fn tape_ref(&self) -> TapeRef<'_> {
158 if !self.valid {
159 return TapeRef::new(&[], &[]);
160 }
161 let view = unsafe {
163 dom_ffi::parser_get_tape_view(self.inner.as_ref().expect("parser must not be null"))
164 };
165 TapeRef::new(view.tape, view.string_buf)
166 }
167
168 pub fn parse_to_value(&mut self, json: &str) -> Result<crate::tape::Value<'_>> {
170 let mut tape = self.parse_str(json)?;
171 Ok(tape.parse_value()?)
172 }
173
174 pub fn parse_bytes_to_value(&mut self, json: &[u8]) -> Result<crate::tape::Value<'_>> {
176 let mut tape = self.parse_bytes(json)?;
177 Ok(tape.parse_value()?)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn parse_null_tape() -> Result<()> {
187 let mut p = Parser::default();
188 let tape = p.parse_str("null")?;
189 assert_eq!(tape.tape_type(), Some(crate::tape::TapeType::Root));
191 Ok(())
192 }
193
194 #[test]
195 fn parse_object_tape() -> Result<()> {
196 let mut p = Parser::default();
197 let tape = p.parse_str(r#"{"answer":42}"#)?;
198 assert_eq!(tape.tape_type(), Some(crate::tape::TapeType::Root));
200 Ok(())
201 }
202
203 #[test]
204 fn parse_to_value_works() -> Result<()> {
205 let mut p = Parser::default();
206 let v = p.parse_to_value(r#"{"answer":42,"ok":true,"name":"rust"}"#)?;
207 assert_eq!(v.get("answer").unwrap().as_i64(), Some(42));
208 assert_eq!(v.get("ok").unwrap().as_bool(), Some(true));
209 assert_eq!(v.get("name").unwrap().as_str(), Some("rust"));
210 Ok(())
211 }
212}