pub(crate) mod map_as_pairs {
use serde::de::{Error as _, SeqAccess, Visitor};
use serde::{Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
pub fn serialize<K, V, S>(map: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
where
K: Serialize,
V: Serialize,
S: Serializer,
{
serializer.collect_seq(map.iter())
}
pub fn deserialize<'de, K, V, D>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
where
K: serde::Deserialize<'de> + Ord,
V: serde::Deserialize<'de>,
D: Deserializer<'de>,
{
struct PairMapVisitor<K, V>(PhantomData<(K, V)>);
impl<'de, K, V> Visitor<'de> for PairMapVisitor<K, V>
where
K: serde::Deserialize<'de> + Ord,
V: serde::Deserialize<'de>,
{
type Value = BTreeMap<K, V>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence of unique [key, value] pairs")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((key, value)) = seq.next_element::<(K, V)>()? {
if map.insert(key, value).is_some() {
return Err(A::Error::custom("duplicate key in pair-encoded map"));
}
}
Ok(map)
}
}
deserializer.deserialize_seq(PairMapVisitor(PhantomData))
}
}
pub(crate) mod strict_map {
use serde::de::{Error as _, MapAccess, Visitor};
use serde::{Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::marker::PhantomData;
pub fn serialize<K, V, S>(map: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
where
K: Serialize + Ord,
V: Serialize,
S: Serializer,
{
map.serialize(serializer)
}
pub fn deserialize<'de, K, V, D>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
where
K: serde::Deserialize<'de> + Ord,
V: serde::Deserialize<'de>,
D: Deserializer<'de>,
{
struct StrictMapVisitor<K, V>(PhantomData<(K, V)>);
impl<'de, K, V> Visitor<'de> for StrictMapVisitor<K, V>
where
K: serde::Deserialize<'de> + Ord,
V: serde::Deserialize<'de>,
{
type Value = BTreeMap<K, V>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a JSON object with unique keys")
}
fn visit_map<A>(self, mut entries: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((key, value)) = entries.next_entry::<K, V>()? {
if map.insert(key, value).is_some() {
return Err(A::Error::custom("duplicate map key"));
}
}
Ok(map)
}
}
deserializer.deserialize_map(StrictMapVisitor(PhantomData))
}
}
pub(crate) mod strict_set {
use serde::de::{Error as _, SeqAccess, Visitor};
use serde::{Deserializer, Serialize, Serializer};
use std::collections::BTreeSet;
use std::fmt;
use std::marker::PhantomData;
pub fn serialize<T, S>(set: &BTreeSet<T>, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize + Ord,
S: Serializer,
{
set.serialize(serializer)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<BTreeSet<T>, D::Error>
where
T: serde::Deserialize<'de> + Ord,
D: Deserializer<'de>,
{
struct StrictSetVisitor<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for StrictSetVisitor<T>
where
T: serde::Deserialize<'de> + Ord,
{
type Value = BTreeSet<T>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence with unique members")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut set = BTreeSet::new();
while let Some(value) = seq.next_element::<T>()? {
if !set.insert(value) {
return Err(A::Error::custom("duplicate set member"));
}
}
Ok(set)
}
}
deserializer.deserialize_seq(StrictSetVisitor(PhantomData))
}
}
use serde::ser::Error as _;
use serde::Serialize;
use serde_json::{Map, Number, Value};
const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
pub fn canonical_json<T: Serialize>(value: &T) -> Result<Vec<u8>, serde_json::Error> {
let v = serde_json::to_value(value)?;
let mut out = Vec::new();
write_jcs(&v, &mut out)?;
Ok(out)
}
pub fn canonical_json_string<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {
let bytes = canonical_json(value)?;
String::from_utf8(bytes)
.map_err(|e| serde_json::Error::custom(format!("JCS output was not UTF-8: {e}")))
}
fn write_jcs(value: &Value, out: &mut Vec<u8>) -> Result<(), serde_json::Error> {
match value {
Value::Null => out.extend_from_slice(b"null"),
Value::Bool(true) => out.extend_from_slice(b"true"),
Value::Bool(false) => out.extend_from_slice(b"false"),
Value::Number(n) => write_number(n, out)?,
Value::String(s) => write_string(s, out),
Value::Array(items) => {
out.push(b'[');
for (i, item) in items.iter().enumerate() {
if i > 0 {
out.push(b',');
}
write_jcs(item, out)?;
}
out.push(b']');
}
Value::Object(map) => write_object(map, out)?,
}
Ok(())
}
fn write_object(map: &Map<String, Value>, out: &mut Vec<u8>) -> Result<(), serde_json::Error> {
let mut keys: Vec<&String> = map.keys().collect();
keys.sort_by(|a, b| a.encode_utf16().cmp(b.encode_utf16()));
out.push(b'{');
for (i, key) in keys.iter().enumerate() {
if i > 0 {
out.push(b',');
}
write_string(key, out);
out.push(b':');
let value = map
.get(key.as_str())
.ok_or_else(|| serde_json::Error::custom("map key vanished during iteration"))?;
write_jcs(value, out)?;
}
out.push(b'}');
Ok(())
}
fn write_number(n: &Number, out: &mut Vec<u8>) -> Result<(), serde_json::Error> {
if let Some(u) = n.as_u64() {
if u > MAX_SAFE_INTEGER {
return Err(serde_json::Error::custom(format!(
"integer {u} exceeds the I-JSON safe range required by JCS"
)));
}
out.extend_from_slice(u.to_string().as_bytes());
} else if let Some(i) = n.as_i64() {
if i < -(MAX_SAFE_INTEGER as i64) {
return Err(serde_json::Error::custom(format!(
"integer {i} exceeds the I-JSON safe range required by JCS"
)));
}
out.extend_from_slice(i.to_string().as_bytes());
} else if let Some(f) = n.as_f64() {
if !f.is_finite() {
return Err(serde_json::Error::custom("non-finite number in JCS input"));
}
let mut buf = ryu_js::Buffer::new();
out.extend_from_slice(buf.format_finite(f).as_bytes());
} else {
return Err(serde_json::Error::custom("unrepresentable JSON number"));
}
Ok(())
}
fn write_string(s: &str, out: &mut Vec<u8>) {
out.push(b'"');
for c in s.chars() {
match c {
'"' => out.extend_from_slice(b"\\\""),
'\\' => out.extend_from_slice(b"\\\\"),
'\u{08}' => out.extend_from_slice(b"\\b"),
'\u{0C}' => out.extend_from_slice(b"\\f"),
'\n' => out.extend_from_slice(b"\\n"),
'\r' => out.extend_from_slice(b"\\r"),
'\t' => out.extend_from_slice(b"\\t"),
c if (c as u32) < 0x20 => {
out.extend_from_slice(format!("\\u{:04x}", c as u32).as_bytes());
}
c => {
let mut buf = [0u8; 4];
out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
}
}
}
out.push(b'"');
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::BTreeMap;
fn jcs_str(v: &Value) -> String {
canonical_json_string(v).unwrap()
}
#[test]
fn test_canonical_determinism() {
let mut map = BTreeMap::new();
map.insert("z", 1);
map.insert("a", 2);
let bytes1 = canonical_json(&map).unwrap();
let bytes2 = canonical_json(&map).unwrap();
assert_eq!(bytes1, bytes2);
let s = String::from_utf8(bytes1).unwrap();
assert_eq!(s, r#"{"a":2,"z":1}"#);
}
#[test]
#[allow(clippy::excessive_precision)]
fn test_rfc8785_example() {
let input = json!({
"numbers": [333333333.33333329f64, 1E30f64, 4.50f64, 2e-3f64,
0.000000000000000000000000001f64],
"string": "\u{20ac}$\u{000F}\u{000a}A'B\"\\\\\"/",
"literals": [null, true, false]
});
let expected = "{\"literals\":[null,true,false],\
\"numbers\":[333333333.3333333,1e+30,4.5,0.002,1e-27],\
\"string\":\"\u{20ac}$\\u000f\\nA'B\\\"\\\\\\\\\\\"/\"}";
assert_eq!(jcs_str(&input), expected);
}
#[test]
fn test_rfc8785_numbers() {
for (input, expected) in [
(0.0f64, "0"),
(-0.0f64, "0"),
(1.0, "1"),
(-1.0, "-1"),
(0.5, "0.5"),
(1e+21, "1e+21"),
(1e+20, "100000000000000000000"),
(5e-324, "5e-324"),
(9007199254740994.0, "9007199254740994"),
(999999999999999700000.0, "999999999999999700000"),
(0.000001, "0.000001"),
(0.0000001, "1e-7"),
] {
assert_eq!(jcs_str(&json!(input)), expected, "for {input}");
}
assert_eq!(jcs_str(&json!(9007199254740991u64)), "9007199254740991");
assert_eq!(jcs_str(&json!(-9007199254740991i64)), "-9007199254740991");
}
#[test]
fn test_unsafe_integer_rejected() {
assert!(canonical_json(&json!(9007199254740992u64)).is_err());
assert!(canonical_json(&json!(u64::MAX)).is_err());
assert!(canonical_json(&json!(-9007199254740992i64)).is_err());
}
#[test]
fn test_utf16_key_order() {
let input = json!({
"\u{e000}": 1,
"\u{1d306}": 2,
});
assert_eq!(jcs_str(&input), "{\"\u{1d306}\":2,\"\u{e000}\":1}");
}
#[test]
fn test_string_escapes() {
let input = json!("\u{0008}\u{0009}\u{000a}\u{000b}\u{000c}\u{000d}\u{001f}\u{007f}");
assert_eq!(jcs_str(&input), "\"\\b\\t\\n\\u000b\\f\\r\\u001f\u{007f}\"");
}
}