lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

v1.rs (22243B)


      1 #![forbid(unsafe_code)]
      2 
      3 //! Frozen NIP-01 wire-v1 parsing and canonical-identifier semantics.
      4 
      5 #[cfg(all(not(feature = "std"), not(test)))]
      6 use alloc::{
      7     collections::BTreeMap,
      8     string::{String, ToString},
      9     vec::Vec,
     10 };
     11 
     12 #[cfg(any(feature = "std", test))]
     13 use std::{collections::BTreeMap, string::String, vec::Vec};
     14 
     15 use crate::envelope::{EventEnvelope, EventEnvelopeError, EventEnvelopeParts};
     16 use crate::id::{EventId, EventSignature, ParseError, parse_public_key};
     17 use core::fmt;
     18 use serde_json::{Map, Value};
     19 use sha2::{Digest, Sha256};
     20 
     21 pub const DEFAULT_RAW_JSON_MAX_BYTES: usize = 256 * 1024;
     22 pub const DEFAULT_CONTENT_MAX_BYTES: usize = 128 * 1024;
     23 pub const DEFAULT_TAG_MAX_COUNT: usize = 1024;
     24 pub const DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT: usize = 4096;
     25 pub const DEFAULT_TAG_ELEMENT_MAX_BYTES: usize = 4 * 1024;
     26 pub const DEFAULT_TAG_TOTAL_MAX_BYTES: usize = 128 * 1024;
     27 pub const DEFAULT_EXTRA_MAX_FIELDS: usize = 64;
     28 pub const DEFAULT_EXTRA_TOTAL_JSON_MAX_BYTES: usize = 64 * 1024;
     29 
     30 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     31 pub struct EventWireLimits {
     32     pub max_raw_json_bytes: usize,
     33     pub max_content_bytes: usize,
     34     pub max_tag_count: usize,
     35     pub max_total_tag_elements: usize,
     36     pub max_tag_element_bytes: usize,
     37     pub max_total_tag_bytes: usize,
     38     pub max_extra_fields: usize,
     39     pub max_total_extra_json_bytes: usize,
     40 }
     41 
     42 impl Default for EventWireLimits {
     43     fn default() -> Self {
     44         Self {
     45             max_raw_json_bytes: DEFAULT_RAW_JSON_MAX_BYTES,
     46             max_content_bytes: DEFAULT_CONTENT_MAX_BYTES,
     47             max_tag_count: DEFAULT_TAG_MAX_COUNT,
     48             max_total_tag_elements: DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT,
     49             max_tag_element_bytes: DEFAULT_TAG_ELEMENT_MAX_BYTES,
     50             max_total_tag_bytes: DEFAULT_TAG_TOTAL_MAX_BYTES,
     51             max_extra_fields: DEFAULT_EXTRA_MAX_FIELDS,
     52             max_total_extra_json_bytes: DEFAULT_EXTRA_TOTAL_JSON_MAX_BYTES,
     53         }
     54     }
     55 }
     56 
     57 #[derive(Clone, Debug, PartialEq, Eq)]
     58 pub enum CanonicalEventIdError {
     59     InvalidPubkey(ParseError),
     60     InvalidComputedEventId(ParseError),
     61 }
     62 
     63 impl fmt::Display for CanonicalEventIdError {
     64     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     65         match self {
     66             Self::InvalidPubkey(error) => {
     67                 write!(f, "canonical event id pubkey is invalid: {error}")
     68             }
     69             Self::InvalidComputedEventId(error) => {
     70                 write!(f, "canonical event id digest is invalid: {error}")
     71             }
     72         }
     73     }
     74 }
     75 
     76 #[cfg(feature = "std")]
     77 impl std::error::Error for CanonicalEventIdError {}
     78 
     79 #[derive(Clone, Debug, PartialEq, Eq)]
     80 pub enum EventWireError {
     81     Json(String),
     82     RootNotObject,
     83     MissingField(&'static str),
     84     InvalidField(&'static str),
     85     InvalidIdentifier {
     86         field: &'static str,
     87         error: ParseError,
     88     },
     89     NonCanonicalIdentifier {
     90         field: &'static str,
     91     },
     92     RawJsonTooLarge {
     93         max: usize,
     94         actual: usize,
     95     },
     96     ContentTooLarge {
     97         max: usize,
     98         actual: usize,
     99     },
    100     TooManyTags {
    101         max: usize,
    102         actual: usize,
    103     },
    104     TooManyTagElements {
    105         max: usize,
    106         actual: usize,
    107     },
    108     EmptyTag {
    109         index: usize,
    110     },
    111     EmptyTagKey {
    112         index: usize,
    113     },
    114     ControlCharacterTagKey {
    115         index: usize,
    116     },
    117     TagElementTooLarge {
    118         tag_index: usize,
    119         element_index: usize,
    120         max: usize,
    121         actual: usize,
    122     },
    123     TagsTooLarge {
    124         max: usize,
    125         actual: usize,
    126     },
    127     TooManyExtraFields {
    128         max: usize,
    129         actual: usize,
    130     },
    131     ExtraJsonTooLarge {
    132         max: usize,
    133         actual: usize,
    134     },
    135     CanonicalEventId(CanonicalEventIdError),
    136     Envelope(EventEnvelopeError),
    137     EventIdMismatch {
    138         declared: String,
    139         computed: String,
    140     },
    141 }
    142 
    143 impl fmt::Display for EventWireError {
    144     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    145         match self {
    146             Self::Json(error) => write!(f, "event wire json is invalid: {error}"),
    147             Self::RootNotObject => write!(f, "event wire root must be a JSON object"),
    148             Self::MissingField(field) => write!(f, "event wire missing required field {field}"),
    149             Self::InvalidField(field) => write!(f, "event wire field {field} is invalid"),
    150             Self::InvalidIdentifier { field, error } => {
    151                 write!(f, "event wire field {field} is invalid: {error}")
    152             }
    153             Self::NonCanonicalIdentifier { field } => {
    154                 write!(
    155                     f,
    156                     "event wire field {field} must be canonical lowercase hex"
    157                 )
    158             }
    159             Self::RawJsonTooLarge { max, actual } => {
    160                 write!(f, "event wire raw JSON size {actual} exceeds {max} bytes")
    161             }
    162             Self::ContentTooLarge { max, actual } => {
    163                 write!(f, "event wire content size {actual} exceeds {max} bytes")
    164             }
    165             Self::TooManyTags { max, actual } => {
    166                 write!(f, "event wire tag count {actual} exceeds {max}")
    167             }
    168             Self::TooManyTagElements { max, actual } => {
    169                 write!(f, "event wire tag element count {actual} exceeds {max}")
    170             }
    171             Self::EmptyTag { index } => write!(f, "event wire tag {index} is empty"),
    172             Self::EmptyTagKey { index } => write!(f, "event wire tag {index} key is empty"),
    173             Self::ControlCharacterTagKey { index } => {
    174                 write!(f, "event wire tag {index} key contains a control character")
    175             }
    176             Self::TagElementTooLarge {
    177                 tag_index,
    178                 element_index,
    179                 max,
    180                 actual,
    181             } => write!(
    182                 f,
    183                 "event wire tag {tag_index} element {element_index} size {actual} exceeds {max} bytes"
    184             ),
    185             Self::TagsTooLarge { max, actual } => {
    186                 write!(f, "event wire tag bytes {actual} exceed {max}")
    187             }
    188             Self::TooManyExtraFields { max, actual } => {
    189                 write!(f, "event wire extra field count {actual} exceeds {max}")
    190             }
    191             Self::ExtraJsonTooLarge { max, actual } => {
    192                 write!(f, "event wire extra JSON bytes {actual} exceed {max}")
    193             }
    194             Self::CanonicalEventId(error) => write!(f, "{error}"),
    195             Self::Envelope(error) => write!(f, "{error}"),
    196             Self::EventIdMismatch { declared, computed } => write!(
    197                 f,
    198                 "event wire id mismatch: declared {declared}, computed {computed}"
    199             ),
    200         }
    201     }
    202 }
    203 
    204 #[cfg(feature = "std")]
    205 impl std::error::Error for EventWireError {}
    206 
    207 impl From<CanonicalEventIdError> for EventWireError {
    208     fn from(value: CanonicalEventIdError) -> Self {
    209         Self::CanonicalEventId(value)
    210     }
    211 }
    212 
    213 impl From<EventEnvelopeError> for EventWireError {
    214     fn from(value: EventEnvelopeError) -> Self {
    215         Self::Envelope(value)
    216     }
    217 }
    218 
    219 #[cfg_attr(
    220     any(feature = "serde", test),
    221     derive(serde::Serialize, serde::Deserialize)
    222 )]
    223 #[derive(Clone, Debug, PartialEq, Eq)]
    224 pub struct Nip01EventWireParts {
    225     pub kind: u32,
    226     pub content: String,
    227     pub tags: Vec<Vec<String>>,
    228 }
    229 
    230 #[cfg_attr(
    231     any(feature = "serde", test),
    232     derive(serde::Serialize, serde::Deserialize)
    233 )]
    234 #[derive(Clone, Debug, PartialEq, Eq)]
    235 pub struct Nip01EventWire {
    236     pub id: String,
    237     pub pubkey: String,
    238     pub created_at: u64,
    239     pub kind: u32,
    240     pub tags: Vec<Vec<String>>,
    241     pub content: String,
    242     pub sig: String,
    243     #[cfg_attr(any(feature = "serde", test), serde(flatten))]
    244     pub extra: BTreeMap<String, Value>,
    245 }
    246 
    247 impl Nip01EventWire {
    248     pub fn parse_json(raw_json: &str) -> Result<Self, EventWireError> {
    249         Self::parse_json_with_limits(raw_json, EventWireLimits::default())
    250     }
    251 
    252     /// Parses and structurally validates an event without verifying its ID.
    253     ///
    254     /// This boundary enforces every [`EventWireLimits`] budget. Callers must
    255     /// invoke [`Self::verify_id`] before treating the result as ID-verified.
    256     pub fn parse_json_unverified(raw_json: &str) -> Result<Self, EventWireError> {
    257         Self::parse_json_unverified_with_limits(raw_json, EventWireLimits::default())
    258     }
    259 
    260     pub fn parse_json_with_limits(
    261         raw_json: &str,
    262         limits: EventWireLimits,
    263     ) -> Result<Self, EventWireError> {
    264         let wire = Self::parse_json_unverified_with_limits(raw_json, limits)?;
    265         wire.verify_id()?;
    266         Ok(wire)
    267     }
    268 
    269     /// Parses and structurally validates an event under explicit limits
    270     /// without verifying its ID.
    271     pub fn parse_json_unverified_with_limits(
    272         raw_json: &str,
    273         limits: EventWireLimits,
    274     ) -> Result<Self, EventWireError> {
    275         let raw_len = raw_json.len();
    276         if raw_len > limits.max_raw_json_bytes {
    277             return Err(EventWireError::RawJsonTooLarge {
    278                 max: limits.max_raw_json_bytes,
    279                 actual: raw_len,
    280             });
    281         }
    282         let value = serde_json::from_str::<Value>(raw_json)
    283             .map_err(|error| EventWireError::Json(error.to_string()))?;
    284         Self::from_json_value(value, limits)
    285     }
    286 
    287     pub fn canonical_id_preimage(&self) -> Result<String, CanonicalEventIdError> {
    288         canonical_nip01_event_id_preimage(
    289             self.pubkey.as_str(),
    290             self.created_at,
    291             self.kind,
    292             &self.tags,
    293             self.content.as_str(),
    294         )
    295     }
    296 
    297     pub fn computed_event_id(&self) -> Result<EventId, CanonicalEventIdError> {
    298         compute_canonical_nip01_event_id(
    299             self.pubkey.as_str(),
    300             self.created_at,
    301             self.kind,
    302             &self.tags,
    303             self.content.as_str(),
    304         )
    305     }
    306 
    307     pub fn verify_id(&self) -> Result<(), EventWireError> {
    308         let computed = self.computed_event_id()?.into_string();
    309         if computed.as_str() != self.id.as_str() {
    310             return Err(EventWireError::EventIdMismatch {
    311                 declared: self.id.clone(),
    312                 computed,
    313             });
    314         }
    315         Ok(())
    316     }
    317 
    318     pub fn into_envelope(self) -> Result<EventEnvelope, EventWireError> {
    319         self.verify_id()?;
    320         self.into_unverified_envelope()
    321             .map_err(EventWireError::Envelope)
    322     }
    323 
    324     /// Converts structurally validated wire data without verifying its ID.
    325     ///
    326     /// The returned envelope remains untrusted until an admission typestate
    327     /// transition verifies its canonical ID and signature.
    328     pub fn into_unverified_envelope(self) -> Result<EventEnvelope, EventEnvelopeError> {
    329         EventEnvelope::new(EventEnvelopeParts {
    330             id: self.id,
    331             author: self.pubkey,
    332             created_at: self.created_at,
    333             kind: self.kind,
    334             tags: self.tags,
    335             content: self.content,
    336             sig: self.sig,
    337         })
    338     }
    339 
    340     fn from_json_value(value: Value, limits: EventWireLimits) -> Result<Self, EventWireError> {
    341         let mut object = match value {
    342             Value::Object(object) => object,
    343             _ => return Err(EventWireError::RootNotObject),
    344         };
    345         let id = take_canonical_event_id(&mut object)?;
    346         let pubkey = take_canonical_pubkey(&mut object)?;
    347         let created_at = take_u64(&mut object, "created_at")?;
    348         let kind = take_u32(&mut object, "kind")?;
    349         let tags = take_tags(&mut object, limits)?;
    350         let content = take_string(&mut object, "content")?;
    351         let content_len = content.len();
    352         if content_len > limits.max_content_bytes {
    353             return Err(EventWireError::ContentTooLarge {
    354                 max: limits.max_content_bytes,
    355                 actual: content_len,
    356             });
    357         }
    358         let sig = take_canonical_signature(&mut object)?;
    359         let extra = validate_extra(object, limits)?;
    360         let wire = Self {
    361             id,
    362             pubkey,
    363             created_at,
    364             kind,
    365             tags,
    366             content,
    367             sig,
    368             extra,
    369         };
    370         Ok(wire)
    371     }
    372 }
    373 
    374 pub fn canonical_nip01_event_id_preimage(
    375     pubkey: &str,
    376     created_at: u64,
    377     kind: u32,
    378     tags: &[Vec<String>],
    379     content: &str,
    380 ) -> Result<String, CanonicalEventIdError> {
    381     canonical_nip01_event_id_preimage_v1(pubkey, created_at, kind, tags, content)
    382 }
    383 
    384 /// Serializes the canonical NIP-01 event-id preimage with wire-v1 semantics.
    385 pub fn canonical_nip01_event_id_preimage_v1(
    386     pubkey: &str,
    387     created_at: u64,
    388     kind: u32,
    389     tags: &[Vec<String>],
    390     content: &str,
    391 ) -> Result<String, CanonicalEventIdError> {
    392     let pubkey = parse_public_key(pubkey).map_err(CanonicalEventIdError::InvalidPubkey)?;
    393     let pubkey = pubkey.to_hex();
    394     let mut preimage = String::new();
    395     preimage.push_str("[0,");
    396     push_canonical_json_string(&mut preimage, pubkey.as_str());
    397     preimage.push(',');
    398     preimage.push_str(created_at.to_string().as_str());
    399     preimage.push(',');
    400     preimage.push_str(kind.to_string().as_str());
    401     preimage.push_str(",[");
    402     for (tag_index, tag) in tags.iter().enumerate() {
    403         if tag_index > 0 {
    404             preimage.push(',');
    405         }
    406         preimage.push('[');
    407         for (value_index, value) in tag.iter().enumerate() {
    408             if value_index > 0 {
    409                 preimage.push(',');
    410             }
    411             push_canonical_json_string(&mut preimage, value);
    412         }
    413         preimage.push(']');
    414     }
    415     preimage.push_str("],");
    416     push_canonical_json_string(&mut preimage, content);
    417     preimage.push(']');
    418     Ok(preimage)
    419 }
    420 
    421 pub fn compute_canonical_nip01_event_id(
    422     pubkey: &str,
    423     created_at: u64,
    424     kind: u32,
    425     tags: &[Vec<String>],
    426     content: &str,
    427 ) -> Result<EventId, CanonicalEventIdError> {
    428     compute_canonical_nip01_event_id_v1(pubkey, created_at, kind, tags, content)
    429 }
    430 
    431 /// Computes the canonical NIP-01 event identifier with wire-v1 semantics.
    432 pub fn compute_canonical_nip01_event_id_v1(
    433     pubkey: &str,
    434     created_at: u64,
    435     kind: u32,
    436     tags: &[Vec<String>],
    437     content: &str,
    438 ) -> Result<EventId, CanonicalEventIdError> {
    439     let preimage = canonical_nip01_event_id_preimage_v1(pubkey, created_at, kind, tags, content)?;
    440     let digest = Sha256::digest(preimage.as_bytes());
    441     let event_id = hex::encode(digest);
    442     EventId::parse(event_id).map_err(CanonicalEventIdError::InvalidComputedEventId)
    443 }
    444 
    445 fn take_string(
    446     object: &mut Map<String, Value>,
    447     field: &'static str,
    448 ) -> Result<String, EventWireError> {
    449     match object.remove(field) {
    450         Some(Value::String(value)) => Ok(value),
    451         Some(_) => Err(EventWireError::InvalidField(field)),
    452         None => Err(EventWireError::MissingField(field)),
    453     }
    454 }
    455 
    456 fn take_canonical_event_id(object: &mut Map<String, Value>) -> Result<String, EventWireError> {
    457     let raw = take_string(object, "id")?;
    458     let parsed = EventId::parse(raw.as_str())
    459         .map_err(|error| EventWireError::InvalidIdentifier { field: "id", error })?;
    460     canonical_identifier_string("id", raw, parsed.into_string())
    461 }
    462 
    463 fn take_canonical_pubkey(object: &mut Map<String, Value>) -> Result<String, EventWireError> {
    464     let raw = take_string(object, "pubkey")?;
    465     let parsed =
    466         parse_public_key(raw.as_str()).map_err(|error| EventWireError::InvalidIdentifier {
    467             field: "pubkey",
    468             error,
    469         })?;
    470     canonical_identifier_string("pubkey", raw, parsed.to_hex())
    471 }
    472 
    473 fn take_canonical_signature(object: &mut Map<String, Value>) -> Result<String, EventWireError> {
    474     let raw = take_string(object, "sig")?;
    475     let parsed =
    476         EventSignature::parse(raw.as_str()).map_err(|error| EventWireError::InvalidIdentifier {
    477             field: "sig",
    478             error,
    479         })?;
    480     canonical_identifier_string("sig", raw, parsed.into_string())
    481 }
    482 
    483 fn canonical_identifier_string(
    484     field: &'static str,
    485     raw: String,
    486     canonical: String,
    487 ) -> Result<String, EventWireError> {
    488     if canonical.as_str() != raw.as_str() {
    489         return Err(EventWireError::NonCanonicalIdentifier { field });
    490     }
    491     Ok(canonical)
    492 }
    493 
    494 fn take_u64(object: &mut Map<String, Value>, field: &'static str) -> Result<u64, EventWireError> {
    495     match object.remove(field) {
    496         Some(Value::Number(value)) => value.as_u64().ok_or(EventWireError::InvalidField(field)),
    497         Some(_) => Err(EventWireError::InvalidField(field)),
    498         None => Err(EventWireError::MissingField(field)),
    499     }
    500 }
    501 
    502 fn take_u32(object: &mut Map<String, Value>, field: &'static str) -> Result<u32, EventWireError> {
    503     let value = take_u64(object, field)?;
    504     u32::try_from(value).map_err(|_| EventWireError::InvalidField(field))
    505 }
    506 
    507 fn take_tags(
    508     object: &mut Map<String, Value>,
    509     limits: EventWireLimits,
    510 ) -> Result<Vec<Vec<String>>, EventWireError> {
    511     let raw_tags = match object.remove("tags") {
    512         Some(Value::Array(raw_tags)) => raw_tags,
    513         Some(_) => return Err(EventWireError::InvalidField("tags")),
    514         None => return Err(EventWireError::MissingField("tags")),
    515     };
    516     let tag_count = raw_tags.len();
    517     if tag_count > limits.max_tag_count {
    518         return Err(EventWireError::TooManyTags {
    519             max: limits.max_tag_count,
    520             actual: tag_count,
    521         });
    522     }
    523     let total_tag_elements = raw_tags.iter().try_fold(0usize, |total, raw_tag| {
    524         let Value::Array(values) = raw_tag else {
    525             return Err(EventWireError::InvalidField("tags"));
    526         };
    527         Ok(total.saturating_add(values.len()))
    528     })?;
    529     if total_tag_elements > limits.max_total_tag_elements {
    530         return Err(EventWireError::TooManyTagElements {
    531             max: limits.max_total_tag_elements,
    532             actual: total_tag_elements,
    533         });
    534     }
    535     let mut total_tag_bytes = 0usize;
    536     let mut tags = Vec::with_capacity(tag_count);
    537     for (tag_index, raw_tag) in raw_tags.into_iter().enumerate() {
    538         let raw_values = match raw_tag {
    539             Value::Array(values) => values,
    540             _ => return Err(EventWireError::InvalidField("tags")),
    541         };
    542         if raw_values.is_empty() {
    543             return Err(EventWireError::EmptyTag { index: tag_index });
    544         }
    545         let mut tag = Vec::with_capacity(raw_values.len());
    546         for (element_index, raw_value) in raw_values.into_iter().enumerate() {
    547             let value = match raw_value {
    548                 Value::String(value) => value,
    549                 _ => return Err(EventWireError::InvalidField("tags")),
    550             };
    551             let value_len = value.len();
    552             if value_len > limits.max_tag_element_bytes {
    553                 return Err(EventWireError::TagElementTooLarge {
    554                     tag_index,
    555                     element_index,
    556                     max: limits.max_tag_element_bytes,
    557                     actual: value_len,
    558                 });
    559             }
    560             if element_index == 0 {
    561                 validate_tag_key(tag_index, value.as_str())?;
    562             }
    563             total_tag_bytes = total_tag_bytes.saturating_add(value_len);
    564             if total_tag_bytes > limits.max_total_tag_bytes {
    565                 return Err(EventWireError::TagsTooLarge {
    566                     max: limits.max_total_tag_bytes,
    567                     actual: total_tag_bytes,
    568                 });
    569             }
    570             tag.push(value);
    571         }
    572         tags.push(tag);
    573     }
    574     Ok(tags)
    575 }
    576 
    577 fn validate_tag_key(index: usize, value: &str) -> Result<(), EventWireError> {
    578     if value.is_empty() {
    579         return Err(EventWireError::EmptyTagKey { index });
    580     }
    581     if value.chars().any(char::is_control) {
    582         return Err(EventWireError::ControlCharacterTagKey { index });
    583     }
    584     Ok(())
    585 }
    586 
    587 fn validate_extra(
    588     object: Map<String, Value>,
    589     limits: EventWireLimits,
    590 ) -> Result<BTreeMap<String, Value>, EventWireError> {
    591     let extra_count = object.len();
    592     if extra_count > limits.max_extra_fields {
    593         return Err(EventWireError::TooManyExtraFields {
    594             max: limits.max_extra_fields,
    595             actual: extra_count,
    596         });
    597     }
    598     let mut total_json_bytes = 0usize;
    599     let mut extra = BTreeMap::new();
    600     for (key, value) in object {
    601         let key_json_len = serialized_json_string_len(&key);
    602         let value_json_len = serialized_json_value_len(&value);
    603         total_json_bytes = total_json_bytes
    604             .saturating_add(key_json_len)
    605             .saturating_add(1)
    606             .saturating_add(value_json_len);
    607         if total_json_bytes > limits.max_total_extra_json_bytes {
    608             return Err(EventWireError::ExtraJsonTooLarge {
    609                 max: limits.max_total_extra_json_bytes,
    610                 actual: total_json_bytes,
    611             });
    612         }
    613         extra.insert(key, value);
    614     }
    615     Ok(extra)
    616 }
    617 
    618 #[cfg_attr(coverage_nightly, coverage(off))]
    619 fn serialized_json_string_len(value: &String) -> usize {
    620     serde_json::to_vec(value)
    621         .expect("JSON strings always serialize")
    622         .len()
    623 }
    624 
    625 #[cfg_attr(coverage_nightly, coverage(off))]
    626 fn serialized_json_value_len(value: &Value) -> usize {
    627     serde_json::to_vec(value)
    628         .expect("JSON values always serialize")
    629         .len()
    630 }
    631 
    632 fn push_canonical_json_string(target: &mut String, value: &str) {
    633     target.push('"');
    634     for character in value.chars() {
    635         match character {
    636             '"' => target.push_str("\\\""),
    637             '\\' => target.push_str("\\\\"),
    638             '\n' => target.push_str("\\n"),
    639             '\r' => target.push_str("\\r"),
    640             '\t' => target.push_str("\\t"),
    641             '\u{08}' => target.push_str("\\b"),
    642             '\u{0c}' => target.push_str("\\f"),
    643             '\u{00}'..='\u{1f}' => push_unicode_escape(target, character),
    644             _ => target.push(character),
    645         }
    646     }
    647     target.push('"');
    648 }
    649 
    650 fn push_unicode_escape(target: &mut String, character: char) {
    651     const HEX: &[u8; 16] = b"0123456789abcdef";
    652     let value = character as u32;
    653     target.push_str("\\u00");
    654     target.push(HEX[((value >> 4) & 0x0f) as usize] as char);
    655     target.push(HEX[(value & 0x0f) as usize] as char);
    656 }
    657 
    658 #[cfg(test)]
    659 #[cfg_attr(coverage_nightly, coverage(off))]
    660 mod tests;