lance_jsonb/parser.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3// SPDX-FileCopyrightText: Copyright 2023 Datafuse Labs
4// Adapted from databendlabs/jsonb at commit fba895c5ebe77ce2539e187f9c652f51cbf195c3.
5
6use std::borrow::Cow;
7
8use super::constants::*;
9use super::error::Error;
10use super::error::ParseErrorCode;
11use super::error::Result;
12use super::number::Number;
13use super::util::parse_string;
14use super::value::Object;
15use super::value::Value;
16
17use crate::constants::MAX_DECIMAL128_PRECISION;
18use crate::constants::MAX_DECIMAL256_PRECISION;
19
20use crate::constants::INT64_MAX;
21use crate::constants::INT64_MIN;
22use crate::constants::UINT64_MAX;
23use crate::constants::UINT64_MIN;
24
25// JSON literal constants
26const NULL_LOWERCASE: [u8; 4] = *b"null";
27const NULL_UPPERCASE: [u8; 4] = *b"NULL";
28const TRUE_LOWERCASE: [u8; 4] = *b"true";
29const TRUE_UPPERCASE: [u8; 4] = *b"TRUE";
30const FALSE_LOWERCASE: [u8; 5] = *b"false";
31const FALSE_UPPERCASE: [u8; 5] = *b"FALSE";
32
33const NAN_LOWERCASE: [u8; 3] = *b"nan";
34const NAN_UPPERCASE: [u8; 3] = *b"NAN";
35const INFINITY_LOWERCASE: [u8; 8] = *b"infinity";
36const INFINITY_UPPERCASE: [u8; 8] = *b"INFINITY";
37
38/// Intermediate Abstract Syntax Tree representation of JSON values optimized for parsing performance.
39///
40/// `JsonAst` serves as an efficient intermediate representation during the JSON parsing process,
41/// providing several performance optimizations:
42///
43/// 1. **Zero-copy string handling**: Uses `Cow<'a, str>` to avoid unnecessary string allocations
44/// when the input can be directly borrowed, for both string values and object keys.
45///
46/// 2. **Efficient object representation**: Stores object entries as a vector of tuples with
47/// keys using the same zero-copy `Cow<'a, str>` approach, reducing memory overhead and
48/// avoiding unnecessary allocations during parsing.
49///
50/// 3. **Immediate validation**: Performs object key uniqueness validation and sorting during
51/// the parsing process, eliminating the need for a separate validation pass and reducing
52/// overall processing time.
53///
54/// 4. **Lifetime preservation**: Maintains the lifetime of the original input buffer throughout
55/// the parsing process, minimizing unnecessary copying of data.
56///
57/// 5. **Direct conversion path**: Provides an optimized conversion to the final `OwnedJsonb` type
58/// through the `into_owned_jsonb` method.
59///
60/// This approach separates the parsing concerns from the final representation concerns,
61/// allowing each to be optimized independently.
62#[derive(Clone, PartialEq, Default, Eq, Debug)]
63pub enum JsonAst<'a> {
64 #[default]
65 Null,
66 Bool(bool),
67 String(Cow<'a, str>),
68 Number(Number),
69 Array(Vec<Self>),
70 Object(Vec<(Cow<'a, str>, Self, usize)>),
71}
72
73impl<'a> JsonAst<'a> {
74 /// Converts the intermediate `JsonAst` representation to the final `Value` type.
75 fn into_value(self) -> Result<Value<'a>> {
76 let value = match self {
77 JsonAst::Null => Value::Null,
78 JsonAst::Bool(v) => Value::Bool(v),
79 JsonAst::String(v) => Value::String(v),
80 JsonAst::Number(v) => Value::Number(v),
81 JsonAst::Array(vals) => {
82 let mut values = Vec::with_capacity(vals.len());
83 for val in vals.into_iter() {
84 let value = val.into_value()?;
85 values.push(value);
86 }
87 Value::Array(values)
88 }
89 JsonAst::Object(kvs) => {
90 let mut object = Object::new();
91 for (key, val, _) in kvs.into_iter() {
92 let key_str = key.to_string();
93 let value = val.into_value()?;
94 object.insert(key_str, value);
95 }
96 Value::Object(object)
97 }
98 };
99 Ok(value)
100 }
101}
102
103/// Parse JSON text to JSONB Value with extended mode.
104/// The parser will follow extended JSON syntax rules like leading plus signs,
105/// multiple leading zeros, decimal points without digits, and empty array elements.
106/// Numeric values are preferentially parsed as decimal values to ensure that precision is not lost.
107///
108/// Inspired by `https://github.com/jorgecarleitao/json-deserializer`
109/// Thanks Jorge Leitao.
110pub fn parse_value(buf: &[u8]) -> Result<Value<'_>> {
111 let mut parser = Parser::new(buf);
112 let json_ast = parser.parse()?;
113 json_ast.into_value()
114}
115
116/// JSON parser with optimized parsing strategies.
117///
118/// This parser implements both standard JSON parsing and an extended syntax with additional features.
119/// It uses a single-pass approach for better performance and provides detailed error reporting.
120struct Parser<'a> {
121 /// Input buffer containing the JSON text to parse
122 buf: &'a [u8],
123 /// Current position in the buffer
124 idx: usize,
125 /// Function pointer for parsing json value based on the mode
126 parse_value_fn: fn(&mut Self) -> Result<JsonAst<'a>>,
127 /// Function pointer for parsing array value based on the mode
128 parse_array_value_fn: fn(&mut Self) -> Result<JsonAst<'a>>,
129 /// Function pointer for parsing object_key based on the mode
130 parse_object_key_fn: fn(&mut Self) -> Result<Cow<'a, str>>,
131}
132
133impl<'a> Parser<'a> {
134 fn new(buf: &'a [u8]) -> Self {
135 Self {
136 buf,
137 idx: 0,
138 parse_value_fn: Self::parse_json_value,
139 parse_array_value_fn: Self::parse_array_value,
140 parse_object_key_fn: Self::parse_object_key,
141 }
142 }
143
144 /// Parse a complete JSON document from the input buffer.
145 fn parse(&mut self) -> Result<JsonAst<'a>> {
146 let value = (self.parse_value_fn)(self)?;
147
148 self.skip_unused();
149 if self.idx < self.buf.len() {
150 self.step();
151 return Err(self.error(ParseErrorCode::UnexpectedTrailingCharacters));
152 }
153 Ok(value)
154 }
155
156 /// Parse a JSON value in extended mode with more lenient syntax rules
157 #[inline]
158 fn parse_json_value(&mut self) -> Result<JsonAst<'a>> {
159 self.skip_unused();
160 // Parse empty string to Null value
161 let Ok(c) = self.next() else {
162 return Ok(JsonAst::Null);
163 };
164 match c {
165 b'n' | b'N' => self.parse_json_null_or_nan(),
166 b't' | b'T' => self.parse_json_true(),
167 b'f' | b'F' => self.parse_json_false(),
168 b'i' | b'I' => self.parse_json_infinity(),
169 b'0'..=b'9' | b'-' | b'+' | b'.' => self.parse_json_number(),
170 b'"' | b'\'' => self.parse_json_string(),
171 b'[' => self.parse_json_array(),
172 b'{' => self.parse_json_object(),
173 _ => {
174 self.step();
175 Err(self.error(ParseErrorCode::ExpectedSomeValue))
176 }
177 }
178 }
179
180 #[inline]
181 fn next(&mut self) -> Result<u8> {
182 match self.buf.get(self.idx) {
183 Some(c) => Ok(*c),
184 None => Err(self.error(ParseErrorCode::InvalidEOF)),
185 }
186 }
187
188 #[inline]
189 fn must_is(&mut self, c: &u8) -> Result<()> {
190 match self.buf.get(self.idx) {
191 Some(v) => {
192 self.step();
193 if v == c {
194 Ok(())
195 } else {
196 Err(self.error(ParseErrorCode::ExpectedSomeIdent))
197 }
198 }
199 None => Err(self.error(ParseErrorCode::InvalidEOF)),
200 }
201 }
202
203 #[inline]
204 fn must_either(&mut self, c1: &u8, c2: &u8) -> Result<u8> {
205 match self.buf.get(self.idx) {
206 Some(v) => {
207 self.step();
208 if v == c1 || v == c2 {
209 Ok(*v)
210 } else {
211 Err(self.error(ParseErrorCode::ExpectedSomeIdent))
212 }
213 }
214 None => Err(self.error(ParseErrorCode::InvalidEOF)),
215 }
216 }
217
218 #[inline]
219 fn check_next(&mut self, c: &u8) -> bool {
220 if let Some(v) = self.buf.get(self.idx)
221 && v == c
222 {
223 return true;
224 }
225 false
226 }
227
228 #[inline]
229 fn check_next_either(&mut self, c1: &u8, c2: &u8) -> Option<u8> {
230 if let Some(v) = self.buf.get(self.idx)
231 && (v == c1 || v == c2)
232 {
233 return Some(*v);
234 }
235 None
236 }
237
238 #[inline]
239 fn check_digit(&mut self) -> Option<u8> {
240 if let Some(v) = self.buf.get(self.idx)
241 && v.is_ascii_digit()
242 {
243 let digit = v - b'0';
244 return Some(digit);
245 }
246 None
247 }
248
249 #[inline]
250 fn step_digits(&mut self) -> usize {
251 let mut len = 0;
252 while let Some(v) = self.buf.get(self.idx) {
253 if !v.is_ascii_digit() {
254 break;
255 }
256 len += 1;
257 self.step();
258 }
259 len
260 }
261
262 #[inline]
263 fn step_hexdigits(&mut self) -> usize {
264 let mut len = 0;
265 while let Some(v) = self.buf.get(self.idx) {
266 if !v.is_ascii_hexdigit() {
267 break;
268 }
269 len += 1;
270 self.step();
271 }
272 len
273 }
274
275 #[inline]
276 fn step(&mut self) {
277 self.idx += 1;
278 }
279
280 #[inline]
281 fn step_by(&mut self, n: usize) {
282 self.idx += n;
283 }
284
285 fn error(&self, code: ParseErrorCode) -> Error {
286 let pos = self.idx;
287 Error::Syntax(code, pos)
288 }
289
290 #[inline]
291 fn skip_unused(&mut self) {
292 while self.idx < self.buf.len() {
293 let c = self.buf[self.idx];
294
295 // Fast path: handle common whitespace characters
296 if c.is_ascii_whitespace() {
297 self.idx += 1;
298 continue;
299 }
300
301 // Slow path: handle escape sequences
302 if c == b'\\' && self.idx + 1 < self.buf.len() {
303 let next_c = self.buf[self.idx + 1];
304
305 // Handle simple escapes \n, \r, \t
306 let simple_escape = matches!(next_c, b'n' | b'r' | b't');
307 if simple_escape {
308 self.idx += 2;
309 continue;
310 }
311
312 // Handle \x0C escape
313 let hex_escape = self.idx + 3 < self.buf.len()
314 && next_c == b'x'
315 && self.buf[self.idx + 2] == b'0'
316 && self.buf[self.idx + 3] == b'C';
317 if hex_escape {
318 self.idx += 4;
319 continue;
320 }
321 }
322
323 break;
324 }
325 }
326
327 /// Parse a JSON null or NaN literal in extended mode with case-insensitivity
328 #[inline]
329 fn parse_json_null_or_nan(&mut self) -> Result<JsonAst<'a>> {
330 let idx = self.idx;
331 if let Ok(null) = self.parse_json_null() {
332 Ok(null)
333 } else {
334 // fallback idx to check if it is NaN
335 self.idx = idx;
336 self.parse_json_nan()
337 }
338 }
339
340 /// Parse a JSON null literal in extended mode with case-insensitivity
341 /// Accepts any case variation of "null" (e.g., "Null", "NULL", "nUlL").
342 #[inline]
343 fn parse_json_null(&mut self) -> Result<JsonAst<'a>> {
344 for (v1, v2) in NULL_LOWERCASE.iter().zip(NULL_UPPERCASE.iter()) {
345 self.must_either(v1, v2)?;
346 }
347 Ok(JsonAst::Null)
348 }
349
350 /// Parse a JSON true literal in extended mode with case-insensitivity
351 /// Accepts any case variation of "true" (e.g., "True", "TRUE", "tRuE").
352 #[inline]
353 fn parse_json_true(&mut self) -> Result<JsonAst<'a>> {
354 for (v1, v2) in TRUE_LOWERCASE.iter().zip(TRUE_UPPERCASE.iter()) {
355 self.must_either(v1, v2)?;
356 }
357 Ok(JsonAst::Bool(true))
358 }
359
360 /// Parse a JSON false literal in extended mode with case-insensitivity
361 /// Accepts any case variation of "false" (e.g., "False", "FALSE", "fAlSe").
362 #[inline]
363 fn parse_json_false(&mut self) -> Result<JsonAst<'a>> {
364 for (v1, v2) in FALSE_LOWERCASE.iter().zip(FALSE_UPPERCASE.iter()) {
365 self.must_either(v1, v2)?;
366 }
367 Ok(JsonAst::Bool(false))
368 }
369
370 /// Parse a JSON infinity literal in extended mode with case-insensitivity
371 /// Accepts any case variation of "infinity" (e.g., "Infinity", "INFINITY").
372 #[inline]
373 fn parse_json_infinity(&mut self) -> Result<JsonAst<'a>> {
374 for (v1, v2) in INFINITY_LOWERCASE.iter().zip(INFINITY_UPPERCASE.iter()) {
375 self.must_either(v1, v2)?;
376 }
377 Ok(JsonAst::Number(Number::Float64(f64::INFINITY)))
378 }
379
380 /// Parse a JSON NaN literal in extended mode with case-insensitivity
381 /// Accepts any case variation of "NaN" (e.g., "nan", "NAN").
382 #[inline]
383 fn parse_json_nan(&mut self) -> Result<JsonAst<'a>> {
384 for (v1, v2) in NAN_LOWERCASE.iter().zip(NAN_UPPERCASE.iter()) {
385 self.must_either(v1, v2)?;
386 }
387 Ok(JsonAst::Number(Number::Float64(f64::NAN)))
388 }
389
390 /// Parse extended JSON numbers (supporting non-standard syntax)
391 ///
392 /// This function implements a high-performance JSON number parsing algorithm with extended syntax:
393 /// 1. Support for leading plus sign (e.g., `+123`)
394 /// 2. Support for multiple leading zeros (e.g., `000123`)
395 /// 3. Support for decimal point without digits on either side (e.g., `.123` or `123.`)
396 /// 4. Support for special values like `NaN`, `Infinity`, and `-Infinity` (case-insensitive)
397 /// 5. Support for hexadecimal notation with optional fractional part (e.g., `0xFF`, `0x1A.B`)
398 ///
399 /// Zero-allocation parsing strategy:
400 /// 1. Uses direct digit accumulation without intermediate string conversions
401 /// 2. For standard numeric types (Int64/UInt64), directly builds the value during parsing
402 /// 3. For hexadecimal numbers, uses specialized parsing with support for fractional parts
403 /// 4. Falls back to Float64 parsing for fractions, exponents, and integers outside the i64/u64 range
404 ///
405 /// This implementation prioritizes performance through:
406 /// - Single-pass approach with minimal branching
407 /// - Avoiding heap allocations and string conversions
408 /// - Optimized handling of common number formats
409 /// - Specialized handling for extended syntax elements
410 fn parse_json_number(&mut self) -> Result<JsonAst<'a>> {
411 // Store the starting position for potential fallback parsing
412 let start_idx = self.idx;
413
414 let mut negative = false;
415 let mut leading_zeros = 0;
416
417 // Handle sign prefix (+ or -), extending JSON to support leading plus sign
418 let c = self.next()?;
419 if c == b'-' {
420 negative = true;
421 self.step();
422 } else if c == b'+' {
423 // Extended syntax: Support for leading plus sign
424 self.step();
425 }
426
427 // Extended syntax: Support for multiple leading zeros (e.g., 000123)
428 loop {
429 if self.check_next(&b'0') {
430 leading_zeros += 1;
431 self.step();
432 } else {
433 break;
434 }
435 }
436
437 // Initialize parsing state
438 let mut hi_value = 0_i128; // Stores high digits (for large values)
439 let mut lo_value = 0_i128; // Stores low digits (for very large values)
440 let mut scale = 0_u32; // Tracks decimal places
441 let mut precision = 0; // Count of significant digits
442 let mut has_fraction = false; // Whether the number has an fraction part
443 let mut has_exponent = false; // Whether the number has an exponent part
444
445 // Parse digits, supporting up to MAX_DECIMAL256_PRECISION digits
446 while precision < MAX_DECIMAL256_PRECISION {
447 if let Some(digit) = self.check_digit() {
448 // Parse digit and accumulate value
449 // Store in hi_value or lo_value based on precision
450 if precision < MAX_DECIMAL128_PRECISION {
451 hi_value = unsafe { hi_value.unchecked_mul(10_i128) };
452 hi_value = unsafe { hi_value.unchecked_add(digit as i128) };
453 } else {
454 lo_value = unsafe { lo_value.unchecked_mul(10_i128) };
455 lo_value = unsafe { lo_value.unchecked_add(digit as i128) };
456 }
457 self.step();
458 } else if self.check_next(&b'.') {
459 // Handle decimal point - can only appear once
460 if has_fraction {
461 return Err(self.error(ParseErrorCode::InvalidNumberValue));
462 }
463 has_fraction = true;
464 self.step();
465 // Continue to next iteration without incrementing precision
466 continue;
467 } else {
468 // Not a digit or decimal point, exit the parsing loop
469 break;
470 }
471 precision += 1;
472 // Track scale (number of digits after decimal point)
473 if has_fraction {
474 scale += 1;
475 }
476 }
477
478 // Handle numbers that exceed MAX_DECIMAL256_PRECISION
479 if precision == MAX_DECIMAL256_PRECISION {
480 // If we haven't seen a decimal point yet, continue parsing integer part
481 if !has_fraction {
482 let len = self.step_digits();
483 precision += len;
484 if self.check_next(&b'.') {
485 has_fraction = true;
486 self.step();
487 }
488 }
489 // Parse fractional part if decimal point exists
490 if has_fraction {
491 let len = self.step_digits();
492 precision += len;
493 scale += len as u32;
494 }
495 }
496
497 if leading_zeros == 0 && precision == 0 {
498 // Handle special values
499 if !has_fraction && let Ok(c) = self.next() {
500 match c {
501 b'i' | b'I' => {
502 let val = self.parse_json_infinity()?;
503 if negative {
504 return Ok(JsonAst::Number(Number::Float64(f64::NEG_INFINITY)));
505 } else {
506 return Ok(val);
507 }
508 }
509 b'n' | b'N' => {
510 let val = self.parse_json_nan()?;
511 if negative {
512 // `-Nan` is not allowed
513 return Err(self.error(ParseErrorCode::InvalidNumberValue));
514 } else {
515 return Ok(val);
516 }
517 }
518 _ => {}
519 }
520 }
521 return Err(self.error(ParseErrorCode::InvalidNumberValue));
522 } else if leading_zeros == 1 && precision == 0 && !has_fraction {
523 // Handle hexadecimal number (0x...)
524 if self.check_next_either(&b'x', &b'X').is_some() {
525 self.step();
526
527 // Mark the start position of hex digits
528 let hex_start = self.idx;
529 let int_len = self.step_hexdigits();
530 if int_len == 0 {
531 return Err(self.error(ParseErrorCode::InvalidNumberValue));
532 }
533
534 // Check if we have a fractional part
535 if self.check_next(&b'.') {
536 // Skip the decimal point
537 self.step();
538
539 // Mark the start of fractional digits
540 let frac_start = self.idx;
541 let frac_len = self.step_hexdigits();
542 if frac_len == 0 {
543 return Err(self.error(ParseErrorCode::InvalidNumberValue));
544 }
545
546 let int_str = std::str::from_utf8(&self.buf[hex_start..hex_start + int_len])
547 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
548 let frac_str =
549 std::str::from_utf8(&self.buf[frac_start..frac_start + frac_len])
550 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
551
552 // Parse integer part
553 let int_val = u128::from_str_radix(int_str, 16)
554 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
555
556 // Parse fractional part and calculate its value
557 let frac_val = u128::from_str_radix(frac_str, 16)
558 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
559 let frac_divisor = 16.0_f64.powi(frac_len as i32);
560
561 // Combine integer and fractional parts
562 let mut final_val = int_val as f64 + (frac_val as f64 / frac_divisor);
563 if negative {
564 final_val = -final_val;
565 }
566 return Ok(JsonAst::Number(Number::Float64(final_val)));
567 } else {
568 // Integer-only hex value
569 let int_str = std::str::from_utf8(&self.buf[hex_start..self.idx])
570 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
571
572 // Parse the hex value
573 let value = u128::from_str_radix(int_str, 16)
574 .map_err(|_| self.error(ParseErrorCode::InvalidNumberValue))?;
575
576 // Convert to appropriate number type based on size
577 if negative {
578 // Handle negative values
579 if value <= (i64::MAX as u128 + 1) {
580 let i_val = -(value as i64);
581 return Ok(JsonAst::Number(Number::Int64(i_val)));
582 }
583 {
584 return Ok(JsonAst::Number(Number::Float64(-(value as f64))));
585 }
586 } else {
587 // Handle positive values
588 if value <= u64::MAX as u128 {
589 return Ok(JsonAst::Number(Number::UInt64(value as u64)));
590 }
591 {
592 return Ok(JsonAst::Number(Number::Float64(value as f64)));
593 }
594 }
595 }
596 }
597 }
598
599 // Handle exponent notation (e.g., 1e10, 1.5E-7)
600 if self.check_next_either(&b'E', &b'e').is_some() {
601 has_exponent = true;
602 self.step();
603 // Handle exponent sign
604 if self.check_next_either(&b'+', &b'-').is_some() {
605 self.step();
606 }
607 // Parse exponent digits
608 let len = self.step_digits();
609 if len == 0 {
610 return Err(self.error(ParseErrorCode::InvalidNumberValue));
611 }
612 }
613
614 // If no exponent and precision is within limits, try to return the most appropriate numeric type
615 if !has_exponent && precision <= MAX_DECIMAL128_PRECISION {
616 let value = if negative { -hi_value } else { hi_value };
617
618 // Try to fit the value into the most appropriate numeric type
619 if scale == 0 && (UINT64_MIN..=UINT64_MAX).contains(&value) {
620 return Ok(JsonAst::Number(Number::UInt64(
621 u64::try_from(value).unwrap(),
622 )));
623 } else if scale == 0 && (INT64_MIN..=INT64_MAX).contains(&value) {
624 return Ok(JsonAst::Number(Number::Int64(
625 i64::try_from(value).unwrap(),
626 )));
627 }
628 }
629
630 // Second parsing strategy: Try to parse as i256 for very large numbers
631
632 // Final fallback strategy: Parse as Float64 using fast_float2 library
633 // This handles scientific notation and very large/small numbers
634 let s = unsafe { std::str::from_utf8_unchecked(&self.buf[start_idx..self.idx]) };
635 match fast_float2::parse(s) {
636 Ok(v) => Ok(JsonAst::Number(Number::Float64(v))),
637 Err(_) => Err(self.error(ParseErrorCode::InvalidNumberValue)),
638 }
639 }
640
641 /// Parse a JSON string with extended syntax support
642 ///
643 /// Extended syntax allows both double quotes (") and single quotes (')
644 /// as string delimiters, which is not allowed in standard JSON.
645 #[inline]
646 fn parse_json_string(&mut self) -> Result<JsonAst<'a>> {
647 let end_quote = self.must_either(&b'"', &b'\'')?;
648 let val = self.parse_quoted_string(end_quote)?;
649 Ok(JsonAst::String(val))
650 }
651
652 /// Parse a quoted string with support for escape sequences
653 ///
654 /// Handles both standard and extended Unicode escape sequences:
655 /// - Standard: \uXXXX (4 hex digits)
656 /// - Extended: \u{XXXX} (variable number of hex digits in braces)
657 ///
658 /// Uses a two-pass approach for efficiency:
659 /// 1. First pass: Find string boundaries and count escapes
660 /// 2. Second pass: Process escapes only when necessary
661 fn parse_quoted_string(&mut self, end_quote: u8) -> Result<Cow<'a, str>> {
662 // Mark the starting position (after the opening quote)
663 let start_idx = self.idx;
664 let mut escapes = 0;
665
666 // First pass: Scan the string to find the end and count escape sequences
667 loop {
668 let c = self.next()?;
669 match c {
670 b'\\' => {
671 // Handle escape sequence
672 self.step();
673 escapes += 1;
674 let next_c = self.next()?;
675 if next_c == b'u' {
676 // Handle Unicode escape sequence
677 self.step();
678 let next_c = self.next()?;
679 if next_c == b'{' {
680 // Extended Unicode format: \u{XXXX}
681 self.step_by(UNICODE_LEN + 2);
682 } else {
683 // Standard Unicode format: \uXXXX
684 self.step_by(UNICODE_LEN);
685 }
686 } else {
687 // Simple escape sequence like \n, \t, etc.
688 self.step();
689 }
690 }
691 _ => {
692 self.step();
693 if c == end_quote {
694 break;
695 }
696 }
697 }
698 }
699
700 // Get the string data (excluding quotes)
701 let data = &self.buf[start_idx..self.idx - 1];
702
703 // Second pass: Process the string based on whether it contains escape sequences
704 let val = if escapes > 0 {
705 // String contains escape sequences, need to process them
706 let len = self.idx - 1 - start_idx - escapes;
707 let mut idx = start_idx + 1;
708 let s = parse_string(data, len, &mut idx)?;
709 Cow::Owned(s)
710 } else {
711 // Fast path: No escape sequences, can use the string as-is
712 std::str::from_utf8(data)
713 .map(Cow::Borrowed)
714 .map_err(|_| self.error(ParseErrorCode::InvalidStringValue))?
715 };
716 Ok(val)
717 }
718
719 /// Parse an unquoted string literal for object keys
720 ///
721 /// Extended syntax feature that allows object keys without quotes.
722 ///
723 /// Restrictions:
724 /// - Only letters, numbers, underscore, dollar and UTF-8 multi-byte characters are allowed
725 /// - First character cannot be a number
726 /// - Must contain at least one character
727 fn parse_unquoted_string(&mut self) -> Result<Cow<'a, str>> {
728 let start_idx = self.idx;
729
730 let c = self.next()?;
731 if c.is_ascii_digit() {
732 self.step();
733 return Err(self.error(ParseErrorCode::ObjectKeyInvalidNumber));
734 }
735
736 loop {
737 let c = self.next()?;
738 if c.is_ascii_alphanumeric() || matches!(c, b'_' | b'$') {
739 self.step();
740 } else if c >= 0x80 {
741 // Handle UTF-8 multi-byte characters (including Chinese)
742 // UTF-8 continuation bytes start with binary 10xxxxxx (0x80-0xBF)
743 // Determine how many continuation bytes to expect based on the first byte
744 let continuation_bytes = if c >= 0xF0 {
745 4 // 4-byte sequence (U+10000 to U+10FFFF)
746 } else if c >= 0xE0 {
747 3 // 3-byte sequence (U+0800 to U+FFFF) - includes most Chinese characters
748 } else if c >= 0xC0 {
749 2 // 2-byte sequence (U+0080 to U+07FF)
750 } else {
751 // Invalid UTF-8 start byte
752 return Err(self.error(ParseErrorCode::ObjectKeyInvalidCharacter));
753 };
754
755 // Consume the expected continuation bytes
756 self.step_by(continuation_bytes);
757 } else {
758 break;
759 }
760 }
761 if self.idx == start_idx {
762 return Err(self.error(ParseErrorCode::ObjectKeyInvalidCharacter));
763 }
764
765 // Get the string data
766 let data = &self.buf[start_idx..self.idx];
767 let val = std::str::from_utf8(data)
768 .map(Cow::Borrowed)
769 .map_err(|_| self.error(ParseErrorCode::InvalidStringValue))?;
770 Ok(val)
771 }
772
773 /// Parse an array value with support for empty elements
774 ///
775 /// Extended syntax feature that treats empty elements as null:
776 /// - [1,,3] is parsed as [1,null,3]
777 /// - [1,2,] is parsed as [1,2,null]
778 ///
779 /// This is not allowed in standard JSON but supported in extended mode.
780 #[inline]
781 fn parse_array_value(&mut self) -> Result<JsonAst<'a>> {
782 if self.check_next_either(&b',', &b']').is_some() {
783 Ok(JsonAst::Null)
784 } else {
785 self.parse_json_value()
786 }
787 }
788
789 /// Parse a JSON array with support for extended syntax
790 ///
791 /// - Parses arrays enclosed in square brackets [...]
792 /// - Handles comma-separated values
793 /// - Validates proper syntax for separators and closing brackets
794 /// - Allows empty elements, which are treated as null
795 ///
796 /// Examples of valid arrays:
797 /// - [1,2,3] (standard JSON)
798 /// - [1,,3] (empty element treated as null)
799 /// - [1,2,] (trailing comma treated as null element)
800 fn parse_json_array(&mut self) -> Result<JsonAst<'a>> {
801 // Ensure the array starts with an opening bracket
802 self.must_is(&b'[')?;
803
804 let mut first = true;
805 let mut values = Vec::with_capacity(8);
806
807 // Parse array elements until closing bracket is found
808 loop {
809 self.skip_unused();
810 let c = self.next()?;
811
812 // Check for end of array
813 if c == b']' {
814 self.step();
815 break;
816 }
817
818 // Handle comma separator between elements (not for the first element)
819 if !first {
820 if c != b',' {
821 return Err(self.error(ParseErrorCode::ExpectedArrayCommaOrEnd));
822 }
823 self.step();
824 }
825 first = false;
826
827 self.skip_unused();
828 // Parse a regular array element
829 let value = (self.parse_array_value_fn)(self)?;
830 values.push(value);
831 }
832 Ok(JsonAst::Array(values))
833 }
834
835 /// Parse an object key with extended syntax support
836 ///
837 /// Extended syntax allows:
838 /// 1. Double-quoted strings (")
839 /// 2. Single-quoted strings (')
840 /// 3. Unquoted identifiers (letters, numbers, underscore, dollar and UTF-8 characters)
841 /// with the restriction that they cannot start with a number
842 #[inline]
843 fn parse_object_key(&mut self) -> Result<Cow<'a, str>> {
844 if let Some(end_quote) = self.check_next_either(&b'"', &b'\'') {
845 self.step();
846 self.parse_quoted_string(end_quote)
847 } else {
848 self.parse_unquoted_string()
849 }
850 }
851
852 /// Parse a JSON object with support for both standard and extended syntax
853 ///
854 /// - Parses objects enclosed in curly braces {...}
855 /// - Handles key-value pairs separated by colons
856 /// - Validates proper syntax for separators and closing braces
857 /// - Detects and reports duplicate keys
858 /// - Accepts quoted (double/single) and unquoted keys, and extended syntax for values
859 ///
860 /// Examples of valid objects:
861 /// - {"key": "value"} (standard JSON)
862 /// - {'key': 'value'} (single quotes)
863 /// - {key: "value"} (unquoted key)
864 /// - {_user123: 'value'} (unquoted key with underscore)
865 fn parse_json_object(&mut self) -> Result<JsonAst<'a>> {
866 // Ensure the object starts with an opening brace
867 self.must_is(&b'{')?;
868
869 let mut first = true;
870 let mut obj = Vec::with_capacity(16);
871
872 // Parse key-value pairs until closing brace is found
873 loop {
874 self.skip_unused();
875 let c = self.next()?;
876
877 // Check for end of object
878 if c == b'}' {
879 self.step();
880 break;
881 }
882
883 // Handle comma separator between key-value pairs (not for the first pair)
884 if !first {
885 if c != b',' {
886 return Err(self.error(ParseErrorCode::ExpectedObjectCommaOrEnd));
887 }
888 self.step();
889 }
890 first = false;
891
892 self.skip_unused();
893 let key_str = (self.parse_object_key_fn)(self)?;
894 let pos = self.idx;
895
896 self.skip_unused();
897
898 // Ensure key and value are separated by a colon
899 let c = self.next()?;
900 if c != b':' {
901 return Err(self.error(ParseErrorCode::ExpectedColon));
902 }
903 self.step();
904
905 // Parse the value
906 let value = (self.parse_value_fn)(self)?;
907
908 // Add the key-value pair to the object
909 obj.push((key_str, value, pos));
910 }
911
912 // Sort the Object fields by key and check for duplicate keys.
913 // Returns an error if duplicate keys are found.
914 obj.sort_by(|a, b| a.0.cmp(&b.0));
915 for i in 1..obj.len() {
916 if obj[i - 1].0 == obj[i].0 {
917 let key_str = obj[i].0.clone().to_string();
918 let pos = obj[i].2;
919 let code = ParseErrorCode::ObjectDuplicateKey(key_str);
920 return Err(Error::Syntax(code, pos));
921 }
922 }
923 Ok(JsonAst::Object(obj))
924 }
925}
926
927#[cfg(test)]
928mod tests {
929 use super::*;
930 use crate::OwnedJsonb;
931 use proptest::prelude::*;
932 use std::collections::BTreeMap;
933 use std::fmt::Display;
934 use std::fmt::Formatter;
935
936 /// Json5Value represents the extended [JSON5 syntax](https://json5.org/) for testing purposes
937 ///
938 /// This enum is used to generate test data that conforms to the JSON5 specification,
939 /// including features like hexadecimal numbers, single-quoted strings, and unquoted object keys.
940 #[derive(Clone, PartialEq, Default, Eq, Debug)]
941 pub enum Json5Value {
942 #[default]
943 Null,
944 Bool(bool),
945 Number(Number),
946 HexNumber(String),
947 DoubleQuotedString(String),
948 SingleQuotedString(String),
949 Array(Vec<Self>),
950 DoubleQuotedKeyObject(BTreeMap<String, Self>),
951 SingleQuotedKeyObject(BTreeMap<String, Self>),
952 UnquotedKeyObject(BTreeMap<String, Self>),
953 }
954
955 /// Display implementation for Json5Value that formats values according to JSON5 syntax
956 ///
957 /// This implementation handles proper escaping of special characters in strings
958 /// and ensures the output conforms to the JSON5 specification.
959 impl Display for Json5Value {
960 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
961 match self {
962 Self::Null => write!(f, "null"),
963 Self::Bool(v) => {
964 if *v {
965 write!(f, "true")
966 } else {
967 write!(f, "false")
968 }
969 }
970 Self::Number(v) => write!(f, "{}", v),
971 Self::HexNumber(v) => write!(f, "{}", v),
972 Self::DoubleQuotedString(v) => {
973 write!(f, "\"")?;
974 for c in v.chars() {
975 match c {
976 '"' => write!(f, "\\\"")?,
977 '\\' => write!(f, "\\\\")?,
978 c => write!(f, "{}", c)?,
979 }
980 }
981 write!(f, "\"")
982 }
983 Self::SingleQuotedString(v) => {
984 write!(f, "'")?;
985 for c in v.chars() {
986 match c {
987 '\'' => write!(f, "\\\'")?,
988 '\\' => write!(f, "\\\\")?,
989 c => write!(f, "{}", c)?,
990 }
991 }
992 write!(f, "'")
993 }
994 Self::Array(vs) => {
995 write!(f, "[")?;
996 for (i, v) in vs.iter().enumerate() {
997 if i > 0 {
998 write!(f, ",")?;
999 }
1000 write!(f, "{v}")?;
1001 }
1002 write!(f, "]")
1003 }
1004 Self::DoubleQuotedKeyObject(vs) => {
1005 write!(f, "{{")?;
1006 for (i, (k, v)) in vs.iter().enumerate() {
1007 if i > 0 {
1008 write!(f, ",")?;
1009 }
1010 write!(f, "\"")?;
1011 for c in k.chars() {
1012 match c {
1013 '"' => write!(f, "\\\"")?,
1014 '\\' => write!(f, "\\\\")?,
1015 c => write!(f, "{}", c)?,
1016 }
1017 }
1018 write!(f, "\"")?;
1019 write!(f, ":{v}")?;
1020 }
1021 write!(f, "}}")
1022 }
1023 Self::SingleQuotedKeyObject(vs) => {
1024 write!(f, "{{")?;
1025 for (i, (k, v)) in vs.iter().enumerate() {
1026 if i > 0 {
1027 write!(f, ",")?;
1028 }
1029 write!(f, "'")?;
1030 for c in k.chars() {
1031 match c {
1032 '\'' => write!(f, "\\\'")?,
1033 '\\' => write!(f, "\\\\")?,
1034 c => write!(f, "{}", c)?,
1035 }
1036 }
1037 write!(f, "'")?;
1038 write!(f, ":{v}")?;
1039 }
1040 write!(f, "}}")
1041 }
1042 Self::UnquotedKeyObject(vs) => {
1043 write!(f, "{{")?;
1044 for (i, (k, v)) in vs.iter().enumerate() {
1045 if i > 0 {
1046 write!(f, ",")?;
1047 }
1048 write!(f, "{k}:{v}")?;
1049 }
1050 write!(f, "}}")
1051 }
1052 }
1053 }
1054 }
1055
1056 /// Strategy to generate standard strings for testing
1057 ///
1058 /// Generates strings containing ASCII characters and CJK Unicode characters
1059 /// for testing standard JSON string handling
1060 fn string_strategy() -> impl Strategy<Value = String> {
1061 let ascii = '!'..='~';
1062 // CJK Unified Ideographs
1063 let cjk = '\u{4E00}'..='\u{9FFF}';
1064
1065 let chars: Vec<char> = ascii.chain(cjk).collect();
1066 prop::collection::vec(prop::sample::select(chars), 1..50)
1067 .prop_map(|v| v.into_iter().collect())
1068 }
1069
1070 /// Strategy to generate strings suitable for quoted keys and values in JSON5
1071 ///
1072 /// Excludes quote characters (single and double) and backslashes to simplify testing
1073 /// while still providing a diverse set of characters including CJK Unicode
1074 fn quoted_string_strategy() -> impl Strategy<Value = String> {
1075 // ignore ' " \
1076 let ascii1 = '('..='[';
1077 let ascii2 = ']'..='~';
1078 // CJK Unified Ideographs
1079 let cjk = '\u{4E00}'..='\u{9FFF}';
1080
1081 let chars: Vec<char> = ascii1.chain(ascii2).chain(cjk).collect();
1082 prop::collection::vec(prop::sample::select(chars), 1..50)
1083 .prop_map(|v| v.into_iter().collect())
1084 }
1085
1086 /// Strategy to generate strings suitable for unquoted object keys in JSON5
1087 ///
1088 /// Generates strings containing alphanumeric characters, underscores, dollar signs,
1089 /// and CJK Unicode characters that are valid as unquoted keys in JSON5.
1090 /// This tests the parser's ability to handle extended syntax for object keys.
1091 fn unquoted_string_strategy() -> impl Strategy<Value = String> {
1092 let number = '0'..='9';
1093 let lowercase = 'a'..='f';
1094 let uppercase = 'A'..='F';
1095 let underline = '_';
1096 let dollar = '$';
1097 // CJK Unified Ideographs
1098 let cjk = '\u{4E00}'..='\u{9FFF}';
1099
1100 let mut chars: Vec<char> = number
1101 .chain(lowercase)
1102 .chain(uppercase)
1103 .chain(cjk)
1104 .collect();
1105 chars.push(underline);
1106 chars.push(dollar);
1107 prop::collection::vec(prop::sample::select(chars), 1..50)
1108 .prop_map(|v| v.into_iter().collect())
1109 }
1110
1111 /// Strategy to generate standard JSON number values
1112 ///
1113 /// Generates integers (signed and unsigned) and floating-point numbers
1114 /// while excluding special cases like -0.0 that might cause comparison issues
1115 fn standard_number_strategy() -> impl Strategy<Value = Number> {
1116 prop_oneof![
1117 any::<u64>().prop_map(Number::UInt64),
1118 any::<i64>().prop_map(Number::Int64),
1119 any::<f64>()
1120 .prop_filter("Exclude -0.0", |x| *x != -0.0)
1121 .prop_map(Number::Float64),
1122 ]
1123 }
1124
1125 /// Strategy to generate random hexadecimal numbers for testing
1126 ///
1127 /// Generates hexadecimal numbers with 0x/0X prefix (e.g., "0xFF", "0X1A3")
1128 /// to test the parser's ability to handle extended JSON5 hex number syntax
1129 fn hex_number_strategy() -> impl Strategy<Value = String> {
1130 let number = '0'..='9';
1131 let lowercase = 'a'..='f';
1132 let uppercase = 'A'..='F';
1133
1134 let hex_digit =
1135 prop::sample::select(number.chain(lowercase).chain(uppercase).collect::<Vec<_>>());
1136 let hex_prefix = prop::sample::select(vec!['x', 'X']);
1137 let int_part =
1138 prop::collection::vec(hex_digit, 1..16).prop_map(|v| v.into_iter().collect::<String>());
1139
1140 (hex_prefix, int_part).prop_map(|(x, i)| format!("0{}{}", x, i))
1141 }
1142
1143 /// Strategy to generate JSON5 values for testing the extended JSON parser
1144 ///
1145 /// Creates a comprehensive set of JSON5 values including all extended syntax features:
1146 /// - Standard JSON literals (null, true, false)
1147 /// - Numbers (standard format)
1148 /// - Hexadecimal numbers (with 0x/0X prefix)
1149 /// - Double-quoted strings
1150 /// - Single-quoted strings
1151 /// - Arrays
1152 /// - Objects with different key styles (double-quoted, single-quoted, and unquoted)
1153 ///
1154 /// This strategy is used to verify that our parser correctly handles all JSON5 extensions
1155 fn json5_strategy() -> impl Strategy<Value = Json5Value> {
1156 let leaf = prop_oneof![
1157 Just(Json5Value::Null),
1158 any::<bool>().prop_map(Json5Value::Bool),
1159 standard_number_strategy().prop_map(Json5Value::Number),
1160 hex_number_strategy().prop_map(Json5Value::HexNumber),
1161 quoted_string_strategy().prop_map(Json5Value::DoubleQuotedString),
1162 quoted_string_strategy().prop_map(Json5Value::SingleQuotedString),
1163 ];
1164
1165 leaf.prop_recursive(8, 256, 30, |inner| {
1166 prop_oneof![
1167 prop::collection::vec(inner.clone(), 0..10).prop_map(Json5Value::Array),
1168 prop::collection::btree_map(quoted_string_strategy(), inner.clone(), 0..20)
1169 .prop_map(Json5Value::DoubleQuotedKeyObject),
1170 prop::collection::btree_map(quoted_string_strategy(), inner.clone(), 0..20)
1171 .prop_map(Json5Value::SingleQuotedKeyObject),
1172 prop::collection::btree_map(unquoted_string_strategy(), inner, 0..20)
1173 .prop_map(Json5Value::UnquotedKeyObject),
1174 ]
1175 })
1176 }
1177
1178 /// Strategy to generate standard JSON values
1179 fn standard_json_strategy() -> impl Strategy<Value = Value<'static>> {
1180 let leaf = prop_oneof![
1181 Just(Value::Null),
1182 any::<bool>().prop_map(Value::Bool),
1183 standard_number_strategy().prop_map(Value::Number),
1184 string_strategy().prop_map(|v| Value::String(Cow::Owned(v))),
1185 ];
1186
1187 leaf.prop_recursive(8, 256, 30, |inner| {
1188 prop_oneof![
1189 prop::collection::vec(inner.clone(), 0..10).prop_map(Value::Array),
1190 prop::collection::btree_map(string_strategy(), inner, 0..20)
1191 .prop_map(Value::Object),
1192 ]
1193 })
1194 }
1195
1196 proptest! {
1197 /// Tests the parser's ability to handle JSON5 syntax
1198 ///
1199 /// Generates JSON5 values and verifies that our parser produces
1200 /// the same results as the json_five crate
1201 #[test]
1202 fn test_json5_parser(json in json5_strategy()) {
1203 let source = format!("{}", json);
1204
1205 let res1 = json_five::from_str::<serde_json::Value>(&source);
1206 let res2 = parse_value(source.as_bytes());
1207 let res3 = source.parse::<OwnedJsonb>();
1208 assert_eq!(res1.is_ok(), res2.is_ok());
1209 assert_eq!(res1.is_ok(), res3.is_ok());
1210 if res1.is_ok() {
1211 let res1 = format!("{}", res1.unwrap());
1212 let res2 = format!("{}", res2.unwrap());
1213 let res3 = format!("{}", res3.unwrap());
1214 assert_eq!(res1, res2);
1215 assert_eq!(res1, res3);
1216 }
1217 }
1218 }
1219
1220 proptest! {
1221 /// Tests that standard JSON input round-trips through the parser
1222 #[test]
1223 fn test_standard_json_parser(json in standard_json_strategy()) {
1224 let source = format!("{}", json);
1225 let res1 = serde_json::from_slice::<serde_json::Value>(source.as_bytes());
1226 let res2 = parse_value(source.as_bytes());
1227 let res3 = source.parse::<OwnedJsonb>();
1228 assert_eq!(res1.is_ok(), res2.is_ok());
1229 assert_eq!(res1.is_ok(), res3.is_ok());
1230 if res1.is_ok() {
1231 let res2 = format!("{}", res2.unwrap());
1232 let res3 = format!("{}", res3.unwrap());
1233 assert_eq!(source, res2);
1234 assert_eq!(source, res3);
1235 }
1236 }
1237 }
1238}