ledgence_worker_api/
json.rs1use crate::{Error, ErrorKind, Result};
2use serde::de::DeserializeOwned;
3
4pub fn decode_json<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
11 let mut index = 0;
14 let mut quoted = false;
15 while index < bytes.len() {
16 match bytes[index] {
17 b'\\' if quoted => {
18 index += 2;
19 continue;
20 }
21 b'"' => quoted = !quoted,
22 b'-' | b'0'..=b'9' if !quoted => {
23 let start = index;
24 while index < bytes.len()
25 && matches!(bytes[index], b'0'..=b'9' | b'-' | b'+' | b'.' | b'e' | b'E')
26 {
27 index += 1;
28 }
29 let token = &bytes[start..index];
30 if !token.iter().any(|byte| matches!(byte, b'.' | b'e' | b'E')) {
31 let token = std::str::from_utf8(token).expect("numeric token is ASCII");
32 let representable = if token.starts_with('-') {
33 token.parse::<i64>().is_ok()
34 } else {
35 token.parse::<u64>().is_ok()
36 };
37 if !representable {
38 return Err(Error::new(
39 ErrorKind::InvalidInput,
40 "JSON integer exceeds the signed/unsigned 64-bit range; encode exact larger values as strings",
41 ));
42 }
43 }
44 continue;
45 }
46 _ => {}
47 }
48 index += 1;
49 }
50 serde_json::from_slice(bytes)
51 .map_err(|error| Error::new(ErrorKind::InvalidInput, format!("invalid JSON: {error}")))
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use serde_json::{Value, json};
58
59 #[test]
60 fn integer_limits_are_checked_before_lossy_value_construction() {
61 for (text, expected) in [
62 ("-9223372036854775808", json!(i64::MIN)),
63 ("18446744073709551615", json!(u64::MAX)),
64 ("9007199254740993", json!(9_007_199_254_740_993_u64)),
65 ] {
66 assert_eq!(decode_json::<Value>(text.as_bytes()).unwrap(), expected);
67 }
68 for text in [
69 "-9223372036854775809",
70 "18446744073709551616",
71 "1000000000000000000000000000000000000000",
72 ] {
73 assert!(decode_json::<Value>(text.as_bytes()).is_err(), "{text}");
74 assert!(decode_json::<Value>(format!("{{\"data\":[{text}]}}").as_bytes()).is_err());
75 }
76 }
77
78 #[test]
79 fn strings_and_application_keys_are_opaque_and_syntax_still_validates() {
80 let original = json!({"$serde_json::private::Number": "18446744073709551616", "18446744073709551616": "escaped \\\" quote and digits 18446744073709551616", "data":[null,true,2.5,1e100]});
81 assert_eq!(
82 decode_json::<Value>(&serde_json::to_vec(&original).unwrap()).unwrap(),
83 original
84 );
85 for invalid in ["[1 2]", "01", "--1", "1e", "NaN", "1e400", "\"unterminated"] {
86 assert!(
87 decode_json::<Value>(invalid.as_bytes()).is_err(),
88 "{invalid}"
89 );
90 }
91 }
92 #[test]
93 fn binary64_tokens_preserve_the_nearest_representable_value() {
94 let value =
95 decode_json::<Value>(b"[2.291712365432881e-09,-1.527077339613215e-236]").unwrap();
96 let numbers = value.as_array().unwrap();
97 assert_eq!(numbers.len(), 2);
98 for (actual, expected) in numbers
99 .iter()
100 .zip([2.291712365432881e-09_f64, -1.527077339613215e-236_f64])
101 {
102 assert_eq!(actual.as_f64().unwrap().to_bits(), expected.to_bits());
103 }
104 assert_eq!(
105 decode_json::<Value>(&serde_json::to_vec(&value).unwrap()).unwrap(),
106 value,
107 );
108 }
109}