nodedb_types/json_msgpack/
transcoder.rs1use std::fmt::Write as _;
13
14use super::error::MsgpackResult;
15use super::instant_ext::instant_from_ext;
16use super::reader::{Cursor, base64_encode};
17use crate::datetime::NdbDateTime;
18use crate::value::float_text::non_finite_float_text;
19
20pub fn msgpack_to_json_string(bytes: &[u8]) -> MsgpackResult<String> {
25 if bytes.is_empty() {
26 return Ok(String::new());
27 }
28 let mut c = Cursor::new(bytes);
29 let mut out = String::with_capacity(bytes.len() * 2);
30 transcode_value(&mut c, &mut out)?;
31 c.finish()?;
32 Ok(out)
33}
34
35fn transcode_value(c: &mut Cursor<'_>, out: &mut String) -> zerompk::Result<()> {
36 if c.depth > 500 {
37 return Err(zerompk::Error::DepthLimitExceeded { max: 500 });
38 }
39
40 let marker = c.take()?;
41 match marker {
42 0xC0 => out.push_str("null"),
43 0xC2 => out.push_str("false"),
44 0xC3 => out.push_str("true"),
45
46 0x00..=0x7F => {
47 write_int(out, marker as i64);
48 }
49 0xE0..=0xFF => {
50 write_int(out, marker as i8 as i64);
51 }
52
53 0xCC => {
54 write_uint(out, c.take()? as u64);
55 }
56 0xCD => {
57 write_uint(out, c.read_u16_be()? as u64);
58 }
59 0xCE => {
60 write_uint(out, c.read_u32_be()? as u64);
61 }
62 0xCF => {
63 let b = c.take_n(8)?;
64 write_uint(
65 out,
66 u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
67 );
68 }
69
70 0xD0 => {
71 write_int(out, c.take()? as i8 as i64);
72 }
73 0xD1 => {
74 let b = c.take_n(2)?;
75 write_int(out, i16::from_be_bytes([b[0], b[1]]) as i64);
76 }
77 0xD2 => {
78 let b = c.take_n(4)?;
79 write_int(out, i32::from_be_bytes([b[0], b[1], b[2], b[3]]) as i64);
80 }
81 0xD3 => {
82 let b = c.take_n(8)?;
83 write_int(
84 out,
85 i64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
86 );
87 }
88
89 0xCA => {
90 let b = c.take_n(4)?;
91 write_float(out, f32::from_be_bytes([b[0], b[1], b[2], b[3]]) as f64);
92 }
93 0xCB => {
94 let b = c.take_n(8)?;
95 write_float(
96 out,
97 f64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
98 );
99 }
100
101 m @ 0xA0..=0xBF => transcode_str(c, out, (m & 0x1F) as usize)?,
102 0xD9 => {
103 let l = c.take()? as usize;
104 transcode_str(c, out, l)?;
105 }
106 0xDA => {
107 let l = c.read_u16_be()? as usize;
108 transcode_str(c, out, l)?;
109 }
110 0xDB => {
111 let l = c.read_u32_be()? as usize;
112 transcode_str(c, out, l)?;
113 }
114
115 0xC4 => {
116 let l = c.take()? as usize;
117 transcode_bin(c, out, l)?;
118 }
119 0xC5 => {
120 let l = c.read_u16_be()? as usize;
121 transcode_bin(c, out, l)?;
122 }
123 0xC6 => {
124 let l = c.read_u32_be()? as usize;
125 transcode_bin(c, out, l)?;
126 }
127
128 m @ 0x90..=0x9F => transcode_array(c, out, (m & 0x0F) as usize)?,
129 0xDC => {
130 let l = c.read_u16_be()? as usize;
131 transcode_array(c, out, l)?;
132 }
133 0xDD => {
134 let l = c.read_u32_be()? as usize;
135 transcode_array(c, out, l)?;
136 }
137
138 m @ 0x80..=0x8F => transcode_map(c, out, (m & 0x0F) as usize)?,
139 0xDE => {
140 let l = c.read_u16_be()? as usize;
141 transcode_map(c, out, l)?;
142 }
143 0xDF => {
144 let l = c.read_u32_be()? as usize;
145 transcode_map(c, out, l)?;
146 }
147
148 0xD7 => {
150 let (ext_type, payload) = c.take_fixext8()?;
151 match instant_from_ext(ext_type, payload) {
152 Some((_, micros)) => {
153 out.push('"');
154 out.push_str(&NdbDateTime::from_micros(micros).to_iso8601());
155 out.push('"');
156 }
157 None => out.push_str("null"),
158 }
159 }
160
161 0xD4 => {
163 c.take_n(2)?;
164 out.push_str("null");
165 }
166 0xD5 => {
167 c.take_n(3)?;
168 out.push_str("null");
169 }
170 0xD6 => {
171 c.take_n(5)?;
172 out.push_str("null");
173 }
174 0xD8 => {
175 c.take_n(17)?;
176 out.push_str("null");
177 }
178 0xC7 => {
179 let l = c.take()? as usize;
180 c.take_n(1 + l)?;
181 out.push_str("null");
182 }
183 0xC8 => {
184 let l = c.read_u16_be()? as usize;
185 c.take_n(1 + l)?;
186 out.push_str("null");
187 }
188 0xC9 => {
189 let l = c.read_u32_be()? as usize;
190 c.take_n(1 + l)?;
191 out.push_str("null");
192 }
193
194 _ => return Err(zerompk::Error::InvalidMarker(marker)),
195 }
196 Ok(())
197}
198
199fn write_int(out: &mut String, v: i64) {
200 let _ = write!(out, "{v}");
201}
202
203fn write_uint(out: &mut String, v: u64) {
204 let _ = write!(out, "{v}");
205}
206
207fn write_float(out: &mut String, v: f64) {
210 if let Some(text) = non_finite_float_text(v) {
211 out.push('"');
212 out.push_str(text);
213 out.push('"');
214 } else if v.fract() == 0.0 && v.abs() < (1i64 << 53) as f64 {
215 let _ = write!(out, "{v:.1}");
216 } else {
217 let _ = write!(out, "{v}");
218 }
219}
220
221fn transcode_str(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
222 let bytes = c.take_n(len)?;
223 let s = std::str::from_utf8(bytes).map_err(|_| zerompk::Error::InvalidMarker(0))?;
224 out.push('"');
225 for ch in s.chars() {
226 match ch {
227 '"' => out.push_str("\\\""),
228 '\\' => out.push_str("\\\\"),
229 '\n' => out.push_str("\\n"),
230 '\r' => out.push_str("\\r"),
231 '\t' => out.push_str("\\t"),
232 c if c < '\x20' => {
233 let _ = write!(out, "\\u{:04x}", c as u32);
234 }
235 c => out.push(c),
236 }
237 }
238 out.push('"');
239 Ok(())
240}
241
242fn transcode_bin(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
243 let bytes = c.take_n(len)?;
244 out.push('"');
245 out.push_str(&base64_encode(bytes));
246 out.push('"');
247 Ok(())
248}
249
250fn transcode_array(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
251 c.depth += 1;
252 out.push('[');
253 for i in 0..len {
254 if i > 0 {
255 out.push(',');
256 }
257 transcode_value(c, out)?;
258 }
259 out.push(']');
260 c.depth -= 1;
261 Ok(())
262}
263
264fn transcode_map(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
265 c.depth += 1;
266 out.push('{');
267 for i in 0..len {
268 if i > 0 {
269 out.push(',');
270 }
271 let key_marker = c.peek()?;
272 if (0xA0..=0xBF).contains(&key_marker)
273 || key_marker == 0xD9
274 || key_marker == 0xDA
275 || key_marker == 0xDB
276 {
277 transcode_value(c, out)?;
278 } else {
279 let mut tmp = String::new();
280 transcode_value(c, &mut tmp)?;
281 out.push('"');
282 out.push_str(&tmp);
283 out.push('"');
284 }
285 out.push(':');
286 transcode_value(c, out)?;
287 }
288 out.push('}');
289 c.depth -= 1;
290 Ok(())
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use crate::json_msgpack::writer::json_to_msgpack;
297
298 #[test]
299 fn basic_map() {
300 let val = serde_json::json!({"name": "alice", "age": 30, "active": true});
301 let mp = json_to_msgpack(&val).unwrap();
302 let json_str = msgpack_to_json_string(&mp).unwrap();
303 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
304 assert_eq!(parsed["name"], "alice");
305 assert_eq!(parsed["age"], 30);
306 assert_eq!(parsed["active"], true);
307 }
308
309 #[test]
310 fn array() {
311 let val = serde_json::json!([1, "two", null, false]);
312 let mp = json_to_msgpack(&val).unwrap();
313 let json_str = msgpack_to_json_string(&mp).unwrap();
314 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
315 assert_eq!(parsed, val);
316 }
317
318 #[test]
319 fn escaping() {
320 let val = serde_json::json!({"msg": "hello \"world\"\nnewline"});
321 let mp = json_to_msgpack(&val).unwrap();
322 let json_str = msgpack_to_json_string(&mp).unwrap();
323 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
324 assert_eq!(parsed["msg"], "hello \"world\"\nnewline");
325 }
326
327 #[test]
328 fn empty() {
329 assert_eq!(msgpack_to_json_string(&[]).unwrap(), "");
330 }
331
332 #[test]
333 fn instant_ext_renders_iso8601() {
334 use crate::json_msgpack::instant_ext::{InstantKind, write_instant};
335 let mut mp = vec![0x81, 0xA2, b't', b's'];
336 write_instant(&mut mp, InstantKind::Naive, 1_710_498_600_000_000);
337 let json_str = msgpack_to_json_string(&mp).unwrap();
338 assert_eq!(json_str, "{\"ts\":\"2024-03-15T10:30:00.000000Z\"}");
339
340 let unknown = [0xD7, 0x09, 0, 0, 0, 0, 0, 0, 0, 1];
341 assert_eq!(msgpack_to_json_string(&unknown).unwrap(), "null");
342 }
343
344 #[test]
347 fn non_finite_floats_render_postgres_text() {
348 let mut mp = vec![0x94];
349 for f in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 2.5] {
350 mp.push(0xCB);
351 mp.extend_from_slice(&f.to_be_bytes());
352 }
353 assert_eq!(
354 msgpack_to_json_string(&mp).unwrap(),
355 "[\"NaN\",\"Infinity\",\"-Infinity\",2.5]"
356 );
357 }
358
359 #[test]
360 fn nested() {
361 let val = serde_json::json!({"a": {"b": [1, 2, {"c": 3}]}});
362 let mp = json_to_msgpack(&val).unwrap();
363 let json_str = msgpack_to_json_string(&mp).unwrap();
364 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
365 assert_eq!(parsed, val);
366 }
367}