use crate::value::Value;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq)]
pub enum Json {
Null,
Bool(bool),
Int(i64, Option<String>),
Real(f64, Option<String>),
Str(String, Option<StrSrc>),
Array(Vec<Json>),
Object(Vec<(String, Option<StrSrc>, Json)>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum StrSrc {
TextJ(String),
Text5(String),
}
impl StrSrc {
fn body(&self) -> &str {
match self {
StrSrc::TextJ(b) | StrSrc::Text5(b) => b,
}
}
}
impl Json {
pub fn type_name(&self) -> &'static str {
match self {
Json::Null => "null",
Json::Bool(true) => "true",
Json::Bool(false) => "false",
Json::Int(..) => "integer",
Json::Real(..) => "real",
Json::Str(..) => "text",
Json::Array(_) => "array",
Json::Object(_) => "object",
}
}
pub fn to_sql(&self) -> Value {
match self {
Json::Null => Value::Null,
Json::Bool(b) => Value::Integer(*b as i64),
Json::Int(i, _) => Value::Integer(*i),
Json::Real(r, _) => Value::Real(*r),
Json::Str(s, _) => Value::Text(s.clone()),
Json::Array(_) | Json::Object(_) => Value::Text(self.serialize()),
}
}
pub fn serialize(&self) -> String {
let mut s = String::new();
self.write(&mut s);
s
}
pub fn quote(&self) -> String {
if let Json::Real(r, _) = self {
if r.is_infinite() {
return String::from(if *r < 0.0 { "-9.0e+999" } else { "9.0e+999" });
}
}
self.serialize()
}
pub fn to_jsonb(&self) -> Vec<u8> {
let mut out = Vec::new();
self.write_jsonb(&mut out);
out
}
fn write_jsonb(&self, out: &mut Vec<u8>) {
match self {
Json::Null => push_jsonb(out, JSONB_NULL, &[]),
Json::Bool(true) => push_jsonb(out, JSONB_TRUE, &[]),
Json::Bool(false) => push_jsonb(out, JSONB_FALSE, &[]),
Json::Int(_, Some(raw)) => push_jsonb(out, JSONB_INT5, raw.as_bytes()),
Json::Int(i, None) => push_jsonb(out, JSONB_INT, i.to_string().as_bytes()),
Json::Real(r, src) => {
match src {
Some(t) if is_strict_json_number(t) => {
push_jsonb(out, JSONB_FLOAT, t.as_bytes())
}
Some(t) => push_jsonb(out, JSONB_FLOAT5, t.as_bytes()),
None if r.is_infinite() => {
let s = if *r < 0.0 { "-9e999" } else { "9e999" };
push_jsonb(out, JSONB_FLOAT, s.as_bytes());
}
None => push_jsonb(
out,
JSONB_FLOAT,
crate::exec::eval::format_real(*r).as_bytes(),
),
}
}
Json::Str(s, raw) => write_str_jsonb(s, raw, out),
Json::Array(items) => {
let mut body = Vec::new();
for it in items {
it.write_jsonb(&mut body);
}
push_jsonb(out, JSONB_ARRAY, &body);
}
Json::Object(members) => {
let mut body = Vec::new();
for (k, kraw, v) in members {
write_str_jsonb(k, kraw, &mut body);
v.write_jsonb(&mut body);
}
push_jsonb(out, JSONB_OBJECT, &body);
}
}
}
pub(crate) fn jsonb_len(&self) -> usize {
let payload = self.jsonb_payload_len();
jsonb_header_len(payload) + payload
}
pub(crate) fn jsonb_header_bytes(&self) -> usize {
jsonb_header_len(self.jsonb_payload_len())
}
fn jsonb_payload_len(&self) -> usize {
match self {
Json::Null | Json::Bool(_) => 0,
Json::Int(_, Some(raw)) => raw.len(),
Json::Int(i, None) => i.to_string().len(),
Json::Real(r, src) => match src {
Some(t) => t.len(),
None if r.is_infinite() => {
if *r < 0.0 {
"-9e999".len()
} else {
"9e999".len()
}
}
None => crate::exec::eval::format_real(*r).len(),
},
Json::Str(s, raw) => str_prov_payload_len(s, raw),
Json::Array(items) => items.iter().map(Json::jsonb_len).sum(),
Json::Object(members) => members
.iter()
.map(|(k, kraw, v)| str_prov_jsonb_len(k, kraw) + v.jsonb_len())
.sum(),
}
}
pub fn from_jsonb(bytes: &[u8]) -> Option<Json> {
let (j, rest) = decode_jsonb(bytes)?;
rest.is_empty().then_some(j)
}
pub fn pretty(&self, indent: &str) -> String {
let mut s = String::new();
self.write_pretty(&mut s, indent, 0);
s
}
fn write_pretty(&self, out: &mut String, indent: &str, depth: usize) {
let pad = |out: &mut String, n: usize| {
for _ in 0..n {
out.push_str(indent);
}
};
match self {
Json::Array(items) if !items.is_empty() => {
out.push('[');
for (i, it) in items.iter().enumerate() {
if i > 0 {
out.push(',');
}
out.push('\n');
pad(out, depth + 1);
it.write_pretty(out, indent, depth + 1);
}
out.push('\n');
pad(out, depth);
out.push(']');
}
Json::Object(members) if !members.is_empty() => {
out.push('{');
for (i, (k, kraw, v)) in members.iter().enumerate() {
if i > 0 {
out.push(',');
}
out.push('\n');
pad(out, depth + 1);
write_str_text(k, kraw, out);
out.push_str(": ");
v.write_pretty(out, indent, depth + 1);
}
out.push('\n');
pad(out, depth);
out.push('}');
}
_ => self.write(out),
}
}
fn write(&self, out: &mut String) {
match self {
Json::Null => out.push_str("null"),
Json::Bool(true) => out.push_str("true"),
Json::Bool(false) => out.push_str("false"),
Json::Int(i, _) => out.push_str(&i.to_string()),
Json::Real(_, Some(src)) if is_strict_json_number(src) => out.push_str(src),
Json::Real(_, Some(src)) => out.push_str(&json5_fixup_number(src)),
Json::Real(r, _) if r.is_infinite() => {
out.push_str(if *r < 0.0 { "-9e999" } else { "9e999" });
}
Json::Real(r, _) => out.push_str(&crate::exec::eval::format_real(*r)),
Json::Str(s, raw) => write_str_text(s, raw, out),
Json::Array(items) => {
out.push('[');
for (i, it) in items.iter().enumerate() {
if i > 0 {
out.push(',');
}
it.write(out);
}
out.push(']');
}
Json::Object(members) => {
out.push('{');
for (i, (k, kraw, v)) in members.iter().enumerate() {
if i > 0 {
out.push(',');
}
write_str_text(k, kraw, out);
out.push(':');
v.write(out);
}
out.push('}');
}
}
}
}
const JSONB_NULL: u8 = 0;
const JSONB_TRUE: u8 = 1;
const JSONB_FALSE: u8 = 2;
const JSONB_INT: u8 = 3;
const JSONB_INT5: u8 = 4;
const JSONB_FLOAT: u8 = 5;
const JSONB_FLOAT5: u8 = 6;
const JSONB_TEXT: u8 = 7;
const JSONB_TEXTJ: u8 = 8;
const JSONB_TEXT5: u8 = 9;
const JSONB_TEXTRAW: u8 = 10;
const JSONB_ARRAY: u8 = 11;
const JSONB_OBJECT: u8 = 12;
fn jsonb_header_len(n: usize) -> usize {
if n <= 11 {
1
} else if n <= 0xFF {
2
} else if n <= 0xFFFF {
3
} else if n <= 0xFFFF_FFFF {
5
} else {
9
}
}
fn str_jsonb_payload_len(s: &str) -> usize {
if json_needs_escape(s) {
json_escape_body(s).len()
} else {
s.len()
}
}
fn str_prov_payload_len(s: &str, raw: &Option<StrSrc>) -> usize {
match raw {
Some(src) => src.body().len(),
None => str_jsonb_payload_len(s),
}
}
pub(crate) fn str_prov_jsonb_len(s: &str, raw: &Option<StrSrc>) -> usize {
let payload = str_prov_payload_len(s, raw);
jsonb_header_len(payload) + payload
}
fn write_str_jsonb(s: &str, raw: &Option<StrSrc>, out: &mut Vec<u8>) {
match raw {
Some(StrSrc::TextJ(body)) => push_jsonb(out, JSONB_TEXTJ, body.as_bytes()),
Some(StrSrc::Text5(body)) => push_jsonb(out, JSONB_TEXT5, body.as_bytes()),
None if json_needs_escape(s) => {
push_jsonb(out, JSONB_TEXTJ, json_escape_body(s).as_bytes());
}
None => push_jsonb(out, JSONB_TEXT, s.as_bytes()),
}
}
fn write_str_text(s: &str, raw: &Option<StrSrc>, out: &mut String) {
match raw {
Some(StrSrc::TextJ(body)) => {
out.push('"');
out.push_str(body);
out.push('"');
}
Some(StrSrc::Text5(body)) => {
out.push('"');
out.push_str(&json5_to_json_text(body));
out.push('"');
}
None => write_json_string(s, out),
}
}
fn push_jsonb(out: &mut Vec<u8>, ty: u8, payload: &[u8]) {
let n = payload.len();
if n <= 11 {
out.push(((n as u8) << 4) | ty);
} else if n <= 0xFF {
out.push((12 << 4) | ty);
out.push(n as u8);
} else if n <= 0xFFFF {
out.push((13 << 4) | ty);
out.extend_from_slice(&(n as u16).to_be_bytes());
} else if n <= 0xFFFF_FFFF {
out.push((14 << 4) | ty);
out.extend_from_slice(&(n as u32).to_be_bytes());
} else {
out.push((15 << 4) | ty);
out.extend_from_slice(&(n as u64).to_be_bytes());
}
out.extend_from_slice(payload);
}
fn decode_jsonb(b: &[u8]) -> Option<(Json, &[u8])> {
let header = *b.first()?;
let ty = header & 0x0F;
let mut rest = &b[1..];
let n = match header >> 4 {
d @ 0..=11 => d as usize,
12 => {
let v = *rest.first()? as usize;
rest = &rest[1..];
v
}
13 => {
let v = u16::from_be_bytes([*rest.first()?, *rest.get(1)?]) as usize;
rest = &rest[2..];
v
}
14 => {
let mut a = [0u8; 4];
a.copy_from_slice(rest.get(..4)?);
rest = &rest[4..];
u32::from_be_bytes(a) as usize
}
_ => {
let mut a = [0u8; 8];
a.copy_from_slice(rest.get(..8)?);
rest = &rest[8..];
u64::from_be_bytes(a) as usize
}
};
let payload = rest.get(..n)?;
let after = &rest[n..];
let text = || core::str::from_utf8(payload).ok().map(String::from);
let j = match ty {
JSONB_NULL => Json::Null,
JSONB_TRUE => Json::Bool(true),
JSONB_FALSE => Json::Bool(false),
JSONB_INT | JSONB_INT5 | JSONB_FLOAT | JSONB_FLOAT5 => {
let t = core::str::from_utf8(payload).ok()?;
match parse(t) {
Some(j @ (Json::Int(..) | Json::Real(..))) => j,
_ => return None,
}
}
JSONB_TEXT | JSONB_TEXTRAW => Json::Str(text()?, None),
JSONB_TEXTJ | JSONB_TEXT5 => {
let body = core::str::from_utf8(payload).ok()?;
let mut quoted = String::with_capacity(n + 2);
quoted.push('"');
quoted.push_str(body);
quoted.push('"');
match parse("ed)? {
Json::Str(s, _) => {
let raw = if ty == JSONB_TEXTJ {
Some(StrSrc::TextJ(String::from(body)))
} else {
Some(StrSrc::Text5(String::from(body)))
};
Json::Str(s, raw)
}
_ => return None,
}
}
JSONB_ARRAY => {
let mut items = Vec::new();
let mut p = payload;
while !p.is_empty() {
let (it, next) = decode_jsonb(p)?;
items.push(it);
p = next;
}
Json::Array(items)
}
JSONB_OBJECT => {
let mut members = Vec::new();
let mut p = payload;
while !p.is_empty() {
let (label, next) = decode_jsonb(p)?;
let (val, next2) = decode_jsonb(next)?;
let (key, kraw) = match label {
Json::Str(s, raw) => (s, raw),
_ => return None,
};
members.push((key, kraw, val));
p = next2;
}
Json::Object(members)
}
_ => return None,
};
Some((j, after))
}
fn json_needs_escape(s: &str) -> bool {
s.chars()
.any(|c| matches!(c, '"' | '\\') || (c as u32) < 0x20)
}
fn json_escape_body(s: &str) -> String {
let mut q = String::new();
write_json_string(s, &mut q);
q[1..q.len() - 1].to_string()
}
pub(crate) fn push_path_key(path: &str, k: &str) -> String {
let mut chars = k.chars();
let simple = match chars.next() {
Some(c) if c.is_ascii_alphabetic() => chars.all(|c| c.is_ascii_alphanumeric()),
_ => false,
};
if simple {
alloc::format!("{path}.{k}")
} else {
alloc::format!("{path}.\"{}\"", json_escape_body(k))
}
}
pub(crate) fn push_path_key_prov(path: &str, k: &str, raw: &Option<StrSrc>) -> String {
match raw {
Some(src) => alloc::format!("{path}.\"{}\"", src.body()),
None => push_path_key(path, k),
}
}
fn write_json_string(s: &str, out: &mut String) {
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\u{08}' => out.push_str("\\b"),
'\u{0c}' => out.push_str("\\f"),
c if (c as u32) < 0x20 => {
out.push_str("\\u");
for shift in [12, 8, 4, 0] {
let nib = (c as u32 >> shift) & 0xf;
out.push(char::from_digit(nib, 16).unwrap());
}
}
c => out.push(c),
}
}
out.push('"');
}
pub fn parse(text: &str) -> Option<Json> {
parse_with_error_position(text).ok()
}
pub fn parse_with_error_position(text: &str) -> Result<Json, usize> {
let mut p = Parser {
bytes: text.as_bytes(),
pos: 0,
};
p.skip_ws();
if p.peek().is_none() {
return Err(0);
}
let v = p.value()?;
p.skip_ws();
if p.pos == p.bytes.len() {
Ok(v)
} else {
Err(0)
}
}
pub fn is_strict_json(text: &str) -> bool {
let mut p = StrictParser {
b: text.as_bytes(),
i: 0,
};
p.ws();
p.value() && {
p.ws();
p.i == p.b.len()
}
}
struct StrictParser<'a> {
b: &'a [u8],
i: usize,
}
impl StrictParser<'_> {
fn ws(&mut self) {
while matches!(self.b.get(self.i), Some(b' ' | b'\t' | b'\n' | b'\r')) {
self.i += 1;
}
}
fn lit(&mut self, w: &str) -> bool {
if self.b[self.i..].starts_with(w.as_bytes()) {
self.i += w.len();
true
} else {
false
}
}
fn value(&mut self) -> bool {
self.ws();
match self.b.get(self.i) {
Some(b'{') => self.object(),
Some(b'[') => self.array(),
Some(b'"') => self.string(),
Some(b't') => self.lit("true"),
Some(b'f') => self.lit("false"),
Some(b'n') => self.lit("null"),
Some(b'-' | b'0'..=b'9') => self.number(),
_ => false,
}
}
fn object(&mut self) -> bool {
self.i += 1; self.ws();
if self.b.get(self.i) == Some(&b'}') {
self.i += 1;
return true;
}
loop {
self.ws();
if self.b.get(self.i) != Some(&b'"') || !self.string() {
return false;
}
self.ws();
if self.b.get(self.i) != Some(&b':') {
return false;
}
self.i += 1;
if !self.value() {
return false;
}
self.ws();
match self.b.get(self.i) {
Some(b',') => self.i += 1, Some(b'}') => {
self.i += 1;
return true;
}
_ => return false,
}
}
}
fn array(&mut self) -> bool {
self.i += 1; self.ws();
if self.b.get(self.i) == Some(&b']') {
self.i += 1;
return true;
}
loop {
if !self.value() {
return false;
}
self.ws();
match self.b.get(self.i) {
Some(b',') => self.i += 1, Some(b']') => {
self.i += 1;
return true;
}
_ => return false,
}
}
}
fn string(&mut self) -> bool {
self.i += 1; loop {
match self.b.get(self.i) {
None => return false,
Some(b'"') => {
self.i += 1;
return true;
}
Some(b'\\') => {
self.i += 1;
match self.b.get(self.i) {
Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => self.i += 1,
Some(b'u') => {
self.i += 1;
for _ in 0..4 {
match self.b.get(self.i) {
Some(c) if c.is_ascii_hexdigit() => self.i += 1,
_ => return false,
}
}
}
_ => return false,
}
}
Some(&c) if c < 0x20 => return false,
Some(_) => self.i += 1,
}
}
}
fn number(&mut self) -> bool {
let start = self.i;
if self.b.get(self.i) == Some(&b'-') {
self.i += 1;
}
match self.b.get(self.i) {
Some(b'0') => self.i += 1, Some(b'1'..=b'9') => {
while matches!(self.b.get(self.i), Some(b'0'..=b'9')) {
self.i += 1;
}
}
_ => return false,
}
if self.b.get(self.i) == Some(&b'.') {
self.i += 1;
if !matches!(self.b.get(self.i), Some(b'0'..=b'9')) {
return false;
}
while matches!(self.b.get(self.i), Some(b'0'..=b'9')) {
self.i += 1;
}
}
if matches!(self.b.get(self.i), Some(b'e' | b'E')) {
self.i += 1;
if matches!(self.b.get(self.i), Some(b'+' | b'-')) {
self.i += 1;
}
if !matches!(self.b.get(self.i), Some(b'0'..=b'9')) {
return false;
}
while matches!(self.b.get(self.i), Some(b'0'..=b'9')) {
self.i += 1;
}
}
self.i > start
}
}
struct Parser<'a> {
bytes: &'a [u8],
pos: usize,
}
impl Parser<'_> {
fn peek(&self) -> Option<u8> {
self.bytes.get(self.pos).copied()
}
fn err<T>(&self) -> Result<T, usize> {
Err(self.pos)
}
fn skip_ws(&mut self) {
loop {
match self.peek() {
Some(b' ' | b'\t' | b'\n' | b'\r') => self.pos += 1,
Some(b'/') => match self.bytes.get(self.pos + 1) {
Some(b'/') => {
self.pos += 2;
while let Some(c) = self.peek() {
self.pos += 1;
if c == b'\n' {
break;
}
}
}
Some(b'*') => {
self.pos += 2;
while self.pos < self.bytes.len() {
if self.bytes[self.pos] == b'*'
&& self.bytes.get(self.pos + 1) == Some(&b'/')
{
self.pos += 2;
break;
}
self.pos += 1;
}
}
_ => break,
},
_ => break,
}
}
}
fn value(&mut self) -> Result<Json, usize> {
self.skip_ws();
match self.peek() {
Some(b'{') => self.object(),
Some(b'[') => self.array(),
Some(b'"') => self.string(b'"').map(|(s, raw)| Json::Str(s, raw)),
Some(b'\'') => self.string(b'\'').map(|(s, raw)| Json::Str(s, raw)),
Some(b't') => self.literal("true", Json::Bool(true)),
Some(b'f') => self.literal("false", Json::Bool(false)),
Some(b'n') => self.literal("null", Json::Null),
Some(b'I') => self.literal("Infinity", Json::Real(f64::INFINITY, None)),
Some(b'N') => self.literal("NaN", Json::Null),
Some(b'-' | b'+' | b'.' | b'0'..=b'9') => self.number(),
_ => self.err(),
}
}
fn literal(&mut self, word: &str, val: Json) -> Result<Json, usize> {
if self.bytes[self.pos..].starts_with(word.as_bytes()) {
self.pos += word.len();
Ok(val)
} else {
self.err()
}
}
fn object(&mut self) -> Result<Json, usize> {
self.pos += 1; let mut members = Vec::new();
loop {
self.skip_ws();
if self.peek() == Some(b'}') {
self.pos += 1;
return Ok(Json::Object(members));
}
let (key, key_raw) = self.object_key()?;
self.skip_ws();
if self.peek() != Some(b':') {
return self.err();
}
self.pos += 1;
let val = self.value()?;
members.push((key, key_raw, val));
self.skip_ws();
match self.peek() {
Some(b',') => self.pos += 1,
Some(b'}') => {
self.pos += 1;
return Ok(Json::Object(members));
}
_ => return self.err(),
}
}
}
fn object_key(&mut self) -> Result<(String, Option<StrSrc>), usize> {
match self.peek() {
Some(b'"') => self.string(b'"'),
Some(b'\'') => self.string(b'\''),
Some(c) if c.is_ascii_alphabetic() || c == b'_' || c == b'$' => {
let start = self.pos;
while let Some(c) = self.peek() {
if c.is_ascii_alphanumeric() || c == b'_' || c == b'$' {
self.pos += 1;
} else {
break;
}
}
Ok((
core::str::from_utf8(&self.bytes[start..self.pos])
.unwrap_or("")
.to_string(),
None,
))
}
_ => {
while let Some(c) = self.peek() {
if c.is_ascii_alphanumeric() || c == b'_' || c == b'$' {
self.pos += 1;
} else {
break;
}
}
self.err()
}
}
}
fn array(&mut self) -> Result<Json, usize> {
self.pos += 1; let mut items = Vec::new();
loop {
self.skip_ws();
if self.peek() == Some(b']') {
self.pos += 1;
return Ok(Json::Array(items));
}
let val = self.value()?;
items.push(val);
self.skip_ws();
match self.peek() {
Some(b',') => self.pos += 1,
Some(b']') => {
self.pos += 1;
return Ok(Json::Array(items));
}
_ => return self.err(),
}
}
}
fn string(&mut self, quote: u8) -> Result<(String, Option<StrSrc>), usize> {
self.pos += 1; let body_start = self.pos;
let mut s = String::new();
let mut had_escape = false;
let mut json5 = false;
let mut block_raw = false;
loop {
let Some(c) = self.peek() else {
return self.err();
};
if c == quote {
let body_end = self.pos;
self.pos += 1;
let raw = if quote == b'"' && had_escape && !block_raw {
core::str::from_utf8(&self.bytes[body_start..body_end])
.ok()
.map(|body| {
let body = String::from(body);
if json5 {
StrSrc::Text5(body)
} else {
StrSrc::TextJ(body)
}
})
} else {
None
};
return Ok((s, raw));
}
self.pos += 1;
match c {
b'\\' => {
had_escape = true;
let esc_pos = self.pos;
let Some(esc) = self.peek() else {
return self.err();
};
self.pos += 1;
match esc {
b'"' => s.push('"'),
b'\'' => {
json5 = true;
s.push('\'');
}
b'\\' => s.push('\\'),
b'/' => s.push('/'),
b'n' => s.push('\n'),
b't' => s.push('\t'),
b'v' => {
block_raw = true;
s.push('\t');
}
b'r' => s.push('\r'),
b'b' => s.push('\u{08}'),
b'f' => s.push('\u{0c}'),
b'0' => {
json5 = true;
s.push('\0');
}
b'\n' => json5 = true,
b'\r' => {
json5 = true;
if self.peek() == Some(b'\n') {
self.pos += 1;
}
}
b'x' => {
json5 = true;
let hi = self.hex_digit()?;
let lo = self.hex_digit()?;
s.push((hi * 16 + lo) as u8 as char);
}
b'u' => {
let cp = self.hex4()?;
if (0xD800..=0xDBFF).contains(&cp) {
if self.peek() == Some(b'\\') {
self.pos += 1;
if self.peek() == Some(b'u') {
self.pos += 1;
let lo = self.hex4()?;
let c = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
match char::from_u32(c) {
Some(ch) => s.push(ch),
None => return self.err(),
}
} else {
return self.err();
}
} else {
return self.err();
}
} else {
match char::from_u32(cp) {
Some(ch) => s.push(ch),
None => return self.err(),
}
}
}
_ => return Err(esc_pos),
}
}
0x80..=0xFF => {
let start = self.pos - 1;
while self.peek().is_some_and(|b| b >= 0x80) {
self.pos += 1;
}
match core::str::from_utf8(&self.bytes[start..self.pos]) {
Ok(chunk) => s.push_str(chunk),
Err(_) => return self.err(),
}
}
c => {
if c < 0x20 {
block_raw = true;
}
s.push(c as char);
}
}
}
}
fn hex4(&mut self) -> Result<u32, usize> {
let mut v = 0u32;
for _ in 0..4 {
v = v * 16 + self.hex_digit()?;
}
Ok(v)
}
fn hex_digit(&mut self) -> Result<u32, usize> {
let Some(c) = self.peek() else {
return self.err();
};
let Some(d) = (c as char).to_digit(16) else {
return self.err();
};
self.pos += 1;
Ok(d)
}
fn number(&mut self) -> Result<Json, usize> {
let start = self.pos;
let neg = match self.peek() {
Some(b'-') => {
self.pos += 1;
true
}
Some(b'+') => {
self.pos += 1;
false
}
_ => false,
};
match self.peek() {
Some(b'I') => {
return self.literal(
"Infinity",
Json::Real(
if neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
},
None,
),
);
}
Some(b'N') => return self.literal("NaN", Json::Null),
_ => {}
}
if self.peek() == Some(b'0') && matches!(self.bytes.get(self.pos + 1), Some(b'x' | b'X')) {
self.pos += 2;
let digits_start = self.pos;
let mut v: u64 = 0;
while let Some(c) = self.peek() {
let Some(d) = (c as char).to_digit(16) else {
break;
};
v = v.wrapping_mul(16).wrapping_add(d as u64);
self.pos += 1;
}
if self.pos == digits_start {
return Err(start); }
let i = v as i64;
let raw = core::str::from_utf8(&self.bytes[start..self.pos])
.ok()
.map(String::from);
return Ok(Json::Int(if neg { i.wrapping_neg() } else { i }, raw));
}
if self.peek() == Some(b'0') && self.bytes.get(self.pos + 1).is_some_and(u8::is_ascii_digit)
{
return Err(start + 1);
}
let mut is_real = false;
while let Some(c) = self.peek() {
match c {
b'0'..=b'9' => self.pos += 1,
b'.' | b'e' | b'E' | b'+' | b'-' => {
is_real = true;
self.pos += 1;
}
_ => break,
}
}
if self.pos == start || (neg && self.pos == start + 1) {
return Err(start); }
let Ok(tok) = core::str::from_utf8(&self.bytes[start..self.pos]) else {
return Err(start);
};
if !is_real {
if let Ok(i) = tok.parse::<i64>() {
return Ok(Json::Int(i, None));
}
}
match parse_json5_real(tok) {
Some(r) => Ok(Json::Real(
r,
(!tok.starts_with('+')).then(|| String::from(tok)),
)),
None => Err(start),
}
}
}
fn is_strict_json_number(t: &str) -> bool {
let b = t.as_bytes();
let mut i = 0;
if b.get(i) == Some(&b'-') {
i += 1;
}
match b.get(i) {
Some(b'0') => i += 1, Some(c) if c.is_ascii_digit() => {
while b.get(i).is_some_and(u8::is_ascii_digit) {
i += 1;
}
}
_ => return false, }
if b.get(i) == Some(&b'.') {
i += 1;
if !b.get(i).is_some_and(u8::is_ascii_digit) {
return false; }
while b.get(i).is_some_and(u8::is_ascii_digit) {
i += 1;
}
}
if matches!(b.get(i), Some(b'e' | b'E')) {
i += 1;
if matches!(b.get(i), Some(b'+' | b'-')) {
i += 1;
}
if !b.get(i).is_some_and(u8::is_ascii_digit) {
return false;
}
while b.get(i).is_some_and(u8::is_ascii_digit) {
i += 1;
}
}
i == b.len()
}
fn json5_fixup_number(src: &str) -> String {
let b = src.as_bytes();
let Some(dot) = src.find('.') else {
return String::from(src);
};
let mut out = String::with_capacity(src.len() + 2);
out.push_str(&src[..dot]);
if !(dot > 0 && b[dot - 1].is_ascii_digit()) {
out.push('0'); }
out.push('.');
if !b.get(dot + 1).is_some_and(u8::is_ascii_digit) {
out.push('0'); }
out.push_str(&src[dot + 1..]);
out
}
fn json5_to_json_text(body: &str) -> String {
let b = body.as_bytes();
let mut out = String::with_capacity(body.len());
let mut i = 0;
let mut run = 0; while i < b.len() {
if b[i] == b'\\' && i + 1 < b.len() {
out.push_str(&body[run..i]); match b[i + 1] {
b'x' => {
out.push_str("\\u00");
out.push_str(&body[i + 2..i + 4]);
i += 4;
}
b'0' => {
out.push_str("\\u0000");
i += 2;
}
b'\'' => {
out.push('\'');
i += 2;
}
b'\n' => i += 2,
b'\r' => {
i += 2;
if b.get(i) == Some(&b'\n') {
i += 1;
}
}
_ => {
out.push_str(&body[i..i + 2]);
i += 2;
}
}
run = i;
} else {
i += 1;
}
}
out.push_str(&body[run..]);
out
}
fn parse_json5_real(tok: &str) -> Option<f64> {
let tok = tok.strip_prefix('+').unwrap_or(tok);
tok.parse::<f64>().ok()
}
pub fn navigate<'a>(root: &'a Json, path: &str) -> Option<&'a Json> {
let segs = parse_path(path)?;
let mut cur = root;
for seg in &segs {
match (cur, seg) {
(Json::Object(members), Seg::Key(k)) => {
cur = members
.iter()
.find(|(kk, _, _)| kk == k)
.map(|(_, _, v)| v)?;
}
(Json::Array(items), Seg::Index(idx)) => {
let n = idx.resolve_read(items.len())?;
cur = items.get(n)?;
}
_ => return None,
}
}
Some(cur)
}
pub(crate) fn navigate_with_offset<'a>(
root: &'a Json,
path: &str,
) -> Option<(&'a Json, usize, usize)> {
let segs = parse_path(path)?;
let mut cur = root;
let mut value_offset = 0usize;
let mut id = 0usize;
for seg in &segs {
let body = value_offset + jsonb_header_len(cur.jsonb_payload_len());
match (cur, seg) {
(Json::Object(members), Seg::Key(k)) => {
let mut at = body;
let mut found = None;
for (kk, kraw, vv) in members {
let klen = str_prov_jsonb_len(kk, kraw);
if kk == k {
found = Some((vv, at, at + klen));
break;
}
at += klen + vv.jsonb_len();
}
let (vv, key_off, val_off) = found?;
cur = vv;
value_offset = val_off;
id = key_off;
}
(Json::Array(items), Seg::Index(idx)) => {
let n = idx.resolve_read(items.len())?;
let mut at = body;
for vv in &items[..n] {
at += vv.jsonb_len();
}
cur = items.get(n)?;
value_offset = at;
id = at;
}
_ => return None,
}
}
Some((cur, value_offset, id))
}
pub fn value_to_json(v: &Value) -> Json {
match v {
Value::Null => Json::Null,
Value::Integer(i) => Json::Int(*i, None),
Value::Real(r) => Json::Real(*r, None),
Value::Text(s) => Json::Str(s.clone(), None),
Value::Blob(_) => Json::Str(String::new(), None),
}
}
pub fn arrow(doc: &Value, path_arg: &Value, as_text: bool) -> crate::Result<Value> {
if matches!(doc, Value::Null) {
return Ok(Value::Null);
}
let text = crate::exec::eval::to_text(doc);
let Some(root) = parse(&text) else {
if matches!(doc, Value::Blob(_)) {
return Ok(Value::Null);
}
return Err(crate::error::Error::Error("malformed JSON".into()));
};
let path = arrow_path(path_arg);
if matches!(path_arg, Value::Text(s) if s.starts_with('$')) && !path_is_valid(&path) {
return Err(crate::error::Error::Error(alloc::format!(
"bad JSON path: '{path}'"
)));
}
Ok(match navigate(&root, &path) {
None => Value::Null,
Some(node) => {
if as_text {
node.to_sql()
} else {
Value::Text(node.serialize())
}
}
})
}
fn arrow_path(v: &Value) -> String {
match v {
Value::Integer(i) if *i < 0 => alloc::format!("$[#-{}]", i.unsigned_abs()),
Value::Integer(i) => alloc::format!("$[{i}]"),
Value::Text(s) if s.starts_with('$') => s.clone(),
Value::Text(s) => alloc::format!("$.\"{s}\""),
other => alloc::format!("$.\"{}\"", crate::exec::eval::to_text(other)),
}
}
#[derive(Clone, Copy)]
enum Idx {
Abs(usize),
FromEnd(usize),
}
impl Idx {
fn resolve_read(self, len: usize) -> Option<usize> {
match self {
Idx::Abs(n) => Some(n),
Idx::FromEnd(0) => None,
Idx::FromEnd(k) => len.checked_sub(k),
}
}
}
enum Seg {
Key(String),
Index(Idx),
}
pub fn path_is_valid(path: &str) -> bool {
parse_path(path).is_some()
}
fn parse_path(path: &str) -> Option<Vec<Seg>> {
let bytes = path.as_bytes();
if bytes.first() != Some(&b'$') {
return None;
}
let mut segs = Vec::new();
let mut i = 1;
while i < bytes.len() {
match bytes[i] {
b'.' => {
let (key, next) = parse_key(bytes, i + 1)?;
segs.push(Seg::Key(key));
i = next;
}
b'[' => {
let start = i + 1;
let mut j = start;
while j < bytes.len() && bytes[j] != b']' {
j += 1;
}
if j >= bytes.len() {
return None; }
let inner = core::str::from_utf8(&bytes[start..j]).ok()?.trim();
segs.push(Seg::Index(parse_index(inner)?));
i = j + 1;
}
_ => return None,
}
}
Some(segs)
}
fn parse_index(inner: &str) -> Option<Idx> {
if let Some(rest) = inner.strip_prefix('#') {
let rest = rest.trim_start();
if rest.is_empty() {
return Some(Idx::FromEnd(0)); }
let k = rest.strip_prefix('-')?.trim_start();
Some(Idx::FromEnd(k.parse::<usize>().ok()?))
} else {
Some(Idx::Abs(inner.parse::<usize>().ok()?))
}
}
fn json_path_hex4(bytes: &[u8], at: usize) -> Option<u32> {
let slice = bytes.get(at..at + 4)?;
let mut v = 0u32;
for &b in slice {
v = v * 16 + (b as char).to_digit(16)?;
}
Some(v)
}
fn parse_key(bytes: &[u8], i: usize) -> Option<(String, usize)> {
if bytes.get(i) == Some(&b'"') {
let mut j = i + 1;
let mut s = String::new();
while let Some(&c) = bytes.get(j) {
match c {
b'"' => return Some((s, j + 1)),
b'\\' => {
let e = *bytes.get(j + 1)?;
j += 2;
match e {
b'"' => s.push('"'),
b'\\' => s.push('\\'),
b'/' => s.push('/'),
b'b' => s.push('\u{08}'),
b'f' => s.push('\u{0c}'),
b'n' => s.push('\n'),
b'r' => s.push('\r'),
b't' => s.push('\t'),
b'u' => {
let hi = json_path_hex4(bytes, j)?;
j += 4;
let ch = if (0xD800..=0xDBFF).contains(&hi) {
if bytes.get(j) == Some(&b'\\') && bytes.get(j + 1) == Some(&b'u') {
let lo = json_path_hex4(bytes, j + 2)?;
j += 6;
char::from_u32(0x1_0000 + ((hi - 0xD800) << 10) + (lo - 0xDC00))
.unwrap_or('\u{FFFD}')
} else {
'\u{FFFD}'
}
} else {
char::from_u32(hi).unwrap_or('\u{FFFD}')
};
s.push(ch);
}
other => s.push(other as char),
}
}
_ => {
s.push(c as char);
j += 1;
}
}
}
None
} else {
let start = i;
let mut j = i;
while j < bytes.len() && bytes[j] != b'.' && bytes[j] != b'[' {
j += 1;
}
if j == start {
return None;
}
let key = core::str::from_utf8(&bytes[start..j]).ok()?.to_string();
Some((key, j))
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum SetMode {
Set,
Insert,
Replace,
}
fn walk_to_parent<'a>(mut cur: &'a mut Json, segs: &[Seg]) -> Option<&'a mut Json> {
for seg in segs {
cur = match (cur, seg) {
(Json::Object(m), Seg::Key(k)) => {
m.iter_mut().find(|(kk, _, _)| kk == k).map(|(_, _, v)| v)?
}
(Json::Array(a), Seg::Index(idx)) => {
let n = idx.resolve_read(a.len())?;
a.get_mut(n)?
}
_ => return None,
};
}
Some(cur)
}
fn walk_to_parent_creating<'a>(mut cur: &'a mut Json, segs: &[Seg]) -> Option<&'a mut Json> {
if segs.is_empty() {
return Some(cur);
}
for i in 0..segs.len() - 1 {
let make = || match &segs[i + 1] {
Seg::Key(_) => Json::Object(Vec::new()),
Seg::Index(_) => Json::Array(Vec::new()),
};
cur = match (cur, &segs[i]) {
(Json::Object(m), Seg::Key(k)) => {
if !m.iter().any(|(kk, _, _)| kk == k) {
m.push((k.clone(), None, make()));
}
m.iter_mut().find(|(kk, _, _)| kk == k).map(|(_, _, v)| v)?
}
(Json::Array(a), Seg::Index(idx)) => match idx.resolve_read(a.len()) {
Some(n) if n < a.len() => a.get_mut(n)?,
_ if matches!(idx, Idx::FromEnd(0))
|| matches!(idx, Idx::Abs(n) if *n == a.len()) =>
{
a.push(make());
a.last_mut()?
}
_ => return None,
},
_ => return None,
};
}
Some(cur)
}
pub fn set_path(root: &mut Json, path: &str, value: Json, mode: SetMode) -> Option<()> {
let segs = parse_path(path)?;
if segs.is_empty() {
if mode != SetMode::Insert {
*root = value; }
return Some(());
}
let (last, parents) = segs.split_last()?;
if mode == SetMode::Replace {
if let Some(cur) = walk_to_parent(root, parents) {
write_leaf(cur, last, value, mode);
}
return Some(());
}
let mut work = root.clone();
if let Some(cur) = walk_to_parent_creating(&mut work, &segs) {
if write_leaf(cur, last, value, mode) {
*root = work;
}
}
Some(())
}
fn write_leaf(cur: &mut Json, last: &Seg, value: Json, mode: SetMode) -> bool {
match (cur, last) {
(Json::Object(m), Seg::Key(k)) => {
let slot = m.iter_mut().find(|(kk, _, _)| kk == k);
match (slot, mode) {
(Some(s), SetMode::Set) | (Some(s), SetMode::Replace) => {
s.2 = value;
true
}
(None, SetMode::Set) | (None, SetMode::Insert) => {
m.push((k.clone(), None, value));
true
}
_ => false,
}
}
(Json::Array(a), Seg::Index(idx)) => {
let len = a.len();
match idx.resolve_read(len) {
Some(n) if n < len => match mode {
SetMode::Set | SetMode::Replace => {
a[n] = value;
true
}
SetMode::Insert => false,
},
_ => {
let is_append =
matches!(idx, Idx::FromEnd(0)) || matches!(idx, Idx::Abs(n) if *n == len);
if is_append && matches!(mode, SetMode::Set | SetMode::Insert) {
a.push(value);
true
} else {
false
}
}
}
}
_ => false,
}
}
pub fn remove_path(root: &mut Json, path: &str) -> Option<()> {
let segs = parse_path(path)?;
let (last, parents) = segs.split_last()?; let Some(cur) = walk_to_parent(root, parents) else {
return Some(());
};
match (cur, last) {
(Json::Object(m), Seg::Key(k)) => m.retain(|(kk, _, _)| kk != k),
(Json::Array(a), Seg::Index(idx)) => {
if let Some(n) = idx.resolve_read(a.len()) {
if n < a.len() {
a.remove(n);
}
}
}
_ => {}
}
Some(())
}
pub fn merge_patch(target: &mut Json, patch: &Json) {
let Json::Object(pm) = patch else {
*target = patch.clone();
return;
};
if !matches!(target, Json::Object(_)) {
*target = Json::Object(Vec::new());
}
let Json::Object(tm) = target else {
return;
};
for (k, kraw, pv) in pm {
if matches!(pv, Json::Null) {
tm.retain(|(kk, _, _)| kk != k);
} else if let Some(slot) = tm.iter_mut().find(|(kk, _, _)| kk == k) {
merge_patch(&mut slot.2, pv);
} else {
tm.push((k.clone(), kraw.clone(), Json::Null));
let slot = tm.last_mut().unwrap();
merge_patch(&mut slot.2, pv);
}
}
}