blob: 9846cd000c10e6ef8d75f387531db152b6d449cf [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use serde::de::{self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
use serde_json_lenient::de::SliceRead;
use std::fmt;
use std::pin::Pin;
use std::result::Result;
// NOTE: It would be more efficient to stream chunks of the AST for
// conversion into a jsoncpp json::Value, to reduce the number of FFI calls.
#[cxx::bridge(namespace = "openscreen::json::serde")]
mod ffi {
unsafe extern "C++" {
include!("util/json/json_serialization_serde_internal.h");
type ValueBuilder;
type ValueVisitor;
fn set_null(self: Pin<&mut ValueBuilder>);
fn set_bool(self: Pin<&mut ValueBuilder>, val: bool);
fn set_i64(self: Pin<&mut ValueBuilder>, val: i64);
fn set_u64(self: Pin<&mut ValueBuilder>, val: u64);
fn set_f64(self: Pin<&mut ValueBuilder>, val: f64);
fn set_string(self: Pin<&mut ValueBuilder>, val: &str);
fn begin_array(self: Pin<&mut ValueBuilder>);
fn end_array(self: Pin<&mut ValueBuilder>);
fn begin_object(self: Pin<&mut ValueBuilder>);
fn set_object_key(self: Pin<&mut ValueBuilder>, key: &str);
fn end_object(self: Pin<&mut ValueBuilder>);
fn visit_node(self: Pin<&mut ValueVisitor>, writer: &mut SerdeJsonWriter) -> bool;
}
// NOTE #1: This would be more efficient by accepting an AST of the JSON, to
// reduce the number of FFI calls.
//
// NOTE #2: An alternative would be to roll our own serialization in C++,
// which becomes feasible once jsoncpp is eliminated as a third_party
// dependency.
extern "Rust" {
type SerdeJsonWriter;
fn write_null(writer: &mut SerdeJsonWriter);
fn write_bool(writer: &mut SerdeJsonWriter, val: bool);
fn write_i64(writer: &mut SerdeJsonWriter, val: i64);
fn write_u64(writer: &mut SerdeJsonWriter, val: u64);
fn write_f64(writer: &mut SerdeJsonWriter, val: f64);
fn write_str(writer: &mut SerdeJsonWriter, val: &str);
fn write_array_start(writer: &mut SerdeJsonWriter);
fn write_array_end(writer: &mut SerdeJsonWriter);
fn write_object_start(writer: &mut SerdeJsonWriter);
fn write_object_key(writer: &mut SerdeJsonWriter, key: &str);
fn write_object_end(writer: &mut SerdeJsonWriter);
fn decode_json_to_builder(
json: &[u8],
builder: Pin<&mut ValueBuilder>,
error_msg: &mut String,
) -> bool;
fn encode_json(
visitor: Pin<&mut ValueVisitor>,
pretty: bool,
output: &mut String,
error_msg: &mut String,
) -> bool;
}
}
pub struct SerdeJsonWriter {
buffer: String,
pretty: bool,
indent_level: usize,
needs_comma_stack: Vec<bool>,
is_object_key_pending: bool,
}
impl SerdeJsonWriter {
fn new(pretty: bool) -> Self {
Self {
buffer: String::new(),
pretty,
indent_level: 0,
needs_comma_stack: Vec::new(),
is_object_key_pending: false,
}
}
fn write_prefix_for_value(&mut self) {
if self.is_object_key_pending {
self.is_object_key_pending = false;
return;
}
if let Some(needs_comma) = self.needs_comma_stack.last_mut() {
if *needs_comma {
self.buffer.push(',');
if self.pretty {
self.buffer.push('\n');
self.indent();
}
} else {
*needs_comma = true;
if self.pretty {
self.buffer.push('\n');
self.indent();
}
}
}
}
fn indent(&mut self) {
for _ in 0..self.indent_level {
self.buffer.push_str(" ");
}
}
}
fn write_null(writer: &mut SerdeJsonWriter) {
writer.write_prefix_for_value();
writer.buffer.push_str("null");
}
fn write_bool(writer: &mut SerdeJsonWriter, val: bool) {
writer.write_prefix_for_value();
writer.buffer.push_str(if val { "true" } else { "false" });
}
fn write_i64(writer: &mut SerdeJsonWriter, val: i64) {
writer.write_prefix_for_value();
writer.buffer.push_str(&val.to_string());
}
fn write_u64(writer: &mut SerdeJsonWriter, val: u64) {
writer.write_prefix_for_value();
writer.buffer.push_str(&val.to_string());
}
fn write_f64(writer: &mut SerdeJsonWriter, val: f64) {
writer.write_prefix_for_value();
if val.is_nan() || val.is_infinite() {
writer.buffer.push_str("null");
} else {
let s = val.to_string();
writer.buffer.push_str(&s);
if !s.contains('.') && !s.contains('e') && !s.contains('E') {
writer.buffer.push_str(".0");
}
}
}
fn write_str(writer: &mut SerdeJsonWriter, val: &str) {
writer.write_prefix_for_value();
if let Ok(encoded) = serde_json_lenient::to_string(val) {
writer.buffer.push_str(&encoded);
} else {
writer.buffer.push_str("\"\"");
}
}
fn write_array_start(writer: &mut SerdeJsonWriter) {
writer.write_prefix_for_value();
writer.buffer.push('[');
writer.indent_level += 1;
writer.needs_comma_stack.push(false);
}
fn write_array_end(writer: &mut SerdeJsonWriter) {
let had_items = writer.needs_comma_stack.pop().unwrap_or(false);
writer.indent_level = writer.indent_level.saturating_sub(1);
if had_items && writer.pretty {
writer.buffer.push('\n');
writer.indent();
}
writer.buffer.push(']');
}
fn write_object_start(writer: &mut SerdeJsonWriter) {
writer.write_prefix_for_value();
writer.buffer.push('{');
writer.indent_level += 1;
writer.needs_comma_stack.push(false);
}
fn write_object_key(writer: &mut SerdeJsonWriter, key: &str) {
if let Some(needs_comma) = writer.needs_comma_stack.last_mut() {
if *needs_comma {
writer.buffer.push(',');
} else {
*needs_comma = true;
}
if writer.pretty {
writer.buffer.push('\n');
writer.indent();
}
}
if let Ok(encoded) = serde_json_lenient::to_string(key) {
writer.buffer.push_str(&encoded);
} else {
writer.buffer.push_str("\"\"");
}
if writer.pretty {
writer.buffer.push_str(": ");
} else {
writer.buffer.push(':');
}
writer.is_object_key_pending = true;
}
fn write_object_end(writer: &mut SerdeJsonWriter) {
let had_items = writer.needs_comma_stack.pop().unwrap_or(false);
writer.indent_level = writer.indent_level.saturating_sub(1);
if had_items && writer.pretty {
writer.buffer.push('\n');
writer.indent();
}
writer.buffer.push('}');
}
fn encode_json(
mut visitor: Pin<&mut ffi::ValueVisitor>,
pretty: bool,
output: &mut String,
error_msg: &mut String,
) -> bool {
let mut writer = SerdeJsonWriter::new(pretty);
if visitor.as_mut().visit_node(&mut writer) {
*output = writer.buffer;
true
} else {
*error_msg = "Serialization traversal failed".to_string();
false
}
}
struct JsonVisitor<'a, 'b> {
builder: &'a mut Pin<&'b mut ffi::ValueBuilder>,
}
impl<'de, 'a, 'b> Visitor<'de> for JsonVisitor<'a, 'b> {
type Value = ();
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("any valid JSON value")
}
fn visit_bool<E>(self, v: bool) -> Result<(), E>
where
E: de::Error,
{
self.builder.as_mut().set_bool(v);
Ok(())
}
fn visit_i64<E>(self, v: i64) -> Result<(), E>
where
E: de::Error,
{
self.builder.as_mut().set_i64(v);
Ok(())
}
fn visit_u64<E>(self, v: u64) -> Result<(), E>
where
E: de::Error,
{
if v <= i64::MAX as u64 {
self.builder.as_mut().set_i64(v as i64);
} else {
self.builder.as_mut().set_u64(v);
}
Ok(())
}
fn visit_f64<E>(self, v: f64) -> Result<(), E>
where
E: de::Error,
{
self.builder.as_mut().set_f64(v);
Ok(())
}
fn visit_str<E>(self, v: &str) -> Result<(), E>
where
E: de::Error,
{
self.builder.as_mut().set_string(v);
Ok(())
}
fn visit_unit<E>(self) -> Result<(), E>
where
E: de::Error,
{
self.builder.as_mut().set_null();
Ok(())
}
fn visit_seq<A>(self, mut seq: A) -> Result<(), A::Error>
where
A: SeqAccess<'de>,
{
self.builder.as_mut().begin_array();
while let Some(()) = seq.next_element_seed(JsonSeed { builder: self.builder })? {}
self.builder.as_mut().end_array();
Ok(())
}
fn visit_map<M>(self, mut access: M) -> Result<(), M::Error>
where
M: MapAccess<'de>,
{
self.builder.as_mut().begin_object();
while let Some(key) = access.next_key::<&str>()? {
self.builder.as_mut().set_object_key(key);
access.next_value_seed(JsonSeed { builder: self.builder })?;
}
self.builder.as_mut().end_object();
Ok(())
}
}
struct JsonSeed<'a, 'b> {
builder: &'a mut Pin<&'b mut ffi::ValueBuilder>,
}
impl<'de, 'a, 'b> DeserializeSeed<'de> for JsonSeed<'a, 'b> {
type Value = ();
fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(JsonVisitor { builder: self.builder })
}
}
fn decode_json_to_builder(
json: &[u8],
mut builder: Pin<&mut ffi::ValueBuilder>,
error_msg: &mut String,
) -> bool {
let mut deserializer = serde_json_lenient::Deserializer::new(SliceRead::new(
json, false, // replace_invalid_characters
false, // allow_newlines
false, // allow_control_chars
false, // allow_vert_tab
false, // allow_x_escapes
));
let result = deserializer.deserialize_any(JsonVisitor { builder: &mut builder });
match result.and(deserializer.end()) {
Ok(()) => true,
Err(err) => {
*error_msg = err.to_string();
false
}
}
}