Skip to main content

nodedb_types/json_msgpack/
transcoder.rs

1//! Streaming msgpack → JSON string transcoder.
2//!
3//! Walks msgpack bytes and writes JSON text directly into a String.
4//! No intermediate `serde_json::Value` or `nodedb_types::Value`.
5//! Used ONLY at the outermost pgwire/HTTP layer for client compatibility.
6
7use std::fmt::Write as _;
8
9use super::reader::{Cursor, base64_encode};
10
11/// Transcode raw msgpack bytes to a JSON string without intermediate types.
12pub fn msgpack_to_json_string(bytes: &[u8]) -> zerompk::Result<String> {
13    if bytes.is_empty() {
14        return Ok(String::new());
15    }
16    let mut c = Cursor::new(bytes);
17    let mut out = String::with_capacity(bytes.len() * 2);
18    transcode_value(&mut c, &mut out)?;
19    Ok(out)
20}
21
22fn transcode_value(c: &mut Cursor<'_>, out: &mut String) -> zerompk::Result<()> {
23    if c.depth > 500 {
24        return Err(zerompk::Error::DepthLimitExceeded { max: 500 });
25    }
26
27    let marker = c.take()?;
28    match marker {
29        0xC0 => out.push_str("null"),
30        0xC2 => out.push_str("false"),
31        0xC3 => out.push_str("true"),
32
33        0x00..=0x7F => {
34            write_int(out, marker as i64);
35        }
36        0xE0..=0xFF => {
37            write_int(out, marker as i8 as i64);
38        }
39
40        0xCC => {
41            write_uint(out, c.take()? as u64);
42        }
43        0xCD => {
44            write_uint(out, c.read_u16_be()? as u64);
45        }
46        0xCE => {
47            write_uint(out, c.read_u32_be()? as u64);
48        }
49        0xCF => {
50            let b = c.take_n(8)?;
51            write_uint(
52                out,
53                u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
54            );
55        }
56
57        0xD0 => {
58            write_int(out, c.take()? as i8 as i64);
59        }
60        0xD1 => {
61            let b = c.take_n(2)?;
62            write_int(out, i16::from_be_bytes([b[0], b[1]]) as i64);
63        }
64        0xD2 => {
65            let b = c.take_n(4)?;
66            write_int(out, i32::from_be_bytes([b[0], b[1], b[2], b[3]]) as i64);
67        }
68        0xD3 => {
69            let b = c.take_n(8)?;
70            write_int(
71                out,
72                i64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
73            );
74        }
75
76        0xCA => {
77            let b = c.take_n(4)?;
78            write_float(out, f32::from_be_bytes([b[0], b[1], b[2], b[3]]) as f64);
79        }
80        0xCB => {
81            let b = c.take_n(8)?;
82            write_float(
83                out,
84                f64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
85            );
86        }
87
88        m @ 0xA0..=0xBF => transcode_str(c, out, (m & 0x1F) as usize)?,
89        0xD9 => {
90            let l = c.take()? as usize;
91            transcode_str(c, out, l)?;
92        }
93        0xDA => {
94            let l = c.read_u16_be()? as usize;
95            transcode_str(c, out, l)?;
96        }
97        0xDB => {
98            let l = c.read_u32_be()? as usize;
99            transcode_str(c, out, l)?;
100        }
101
102        0xC4 => {
103            let l = c.take()? as usize;
104            transcode_bin(c, out, l)?;
105        }
106        0xC5 => {
107            let l = c.read_u16_be()? as usize;
108            transcode_bin(c, out, l)?;
109        }
110        0xC6 => {
111            let l = c.read_u32_be()? as usize;
112            transcode_bin(c, out, l)?;
113        }
114
115        m @ 0x90..=0x9F => transcode_array(c, out, (m & 0x0F) as usize)?,
116        0xDC => {
117            let l = c.read_u16_be()? as usize;
118            transcode_array(c, out, l)?;
119        }
120        0xDD => {
121            let l = c.read_u32_be()? as usize;
122            transcode_array(c, out, l)?;
123        }
124
125        m @ 0x80..=0x8F => transcode_map(c, out, (m & 0x0F) as usize)?,
126        0xDE => {
127            let l = c.read_u16_be()? as usize;
128            transcode_map(c, out, l)?;
129        }
130        0xDF => {
131            let l = c.read_u32_be()? as usize;
132            transcode_map(c, out, l)?;
133        }
134
135        // ext types — render as null
136        0xD4 => {
137            c.take_n(2)?;
138            out.push_str("null");
139        }
140        0xD5 => {
141            c.take_n(3)?;
142            out.push_str("null");
143        }
144        0xD6 => {
145            c.take_n(5)?;
146            out.push_str("null");
147        }
148        0xD7 => {
149            c.take_n(9)?;
150            out.push_str("null");
151        }
152        0xD8 => {
153            c.take_n(17)?;
154            out.push_str("null");
155        }
156        0xC7 => {
157            let l = c.take()? as usize;
158            c.take_n(1 + l)?;
159            out.push_str("null");
160        }
161        0xC8 => {
162            let l = c.read_u16_be()? as usize;
163            c.take_n(1 + l)?;
164            out.push_str("null");
165        }
166        0xC9 => {
167            let l = c.read_u32_be()? as usize;
168            c.take_n(1 + l)?;
169            out.push_str("null");
170        }
171
172        _ => return Err(zerompk::Error::InvalidMarker(marker)),
173    }
174    Ok(())
175}
176
177fn write_int(out: &mut String, v: i64) {
178    let _ = write!(out, "{v}");
179}
180
181fn write_uint(out: &mut String, v: u64) {
182    let _ = write!(out, "{v}");
183}
184
185fn write_float(out: &mut String, v: f64) {
186    if v.is_nan() || v.is_infinite() {
187        out.push_str("null");
188    } else if v.fract() == 0.0 && v.abs() < (1i64 << 53) as f64 {
189        let _ = write!(out, "{v:.1}");
190    } else {
191        let _ = write!(out, "{v}");
192    }
193}
194
195fn transcode_str(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
196    let bytes = c.take_n(len)?;
197    let s = std::str::from_utf8(bytes).map_err(|_| zerompk::Error::InvalidMarker(0))?;
198    out.push('"');
199    for ch in s.chars() {
200        match ch {
201            '"' => out.push_str("\\\""),
202            '\\' => out.push_str("\\\\"),
203            '\n' => out.push_str("\\n"),
204            '\r' => out.push_str("\\r"),
205            '\t' => out.push_str("\\t"),
206            c if c < '\x20' => {
207                let _ = write!(out, "\\u{:04x}", c as u32);
208            }
209            c => out.push(c),
210        }
211    }
212    out.push('"');
213    Ok(())
214}
215
216fn transcode_bin(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
217    let bytes = c.take_n(len)?;
218    out.push('"');
219    out.push_str(&base64_encode(bytes));
220    out.push('"');
221    Ok(())
222}
223
224fn transcode_array(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
225    c.depth += 1;
226    out.push('[');
227    for i in 0..len {
228        if i > 0 {
229            out.push(',');
230        }
231        transcode_value(c, out)?;
232    }
233    out.push(']');
234    c.depth -= 1;
235    Ok(())
236}
237
238fn transcode_map(c: &mut Cursor<'_>, out: &mut String, len: usize) -> zerompk::Result<()> {
239    c.depth += 1;
240    out.push('{');
241    for i in 0..len {
242        if i > 0 {
243            out.push(',');
244        }
245        let key_marker = c.peek()?;
246        if (0xA0..=0xBF).contains(&key_marker)
247            || key_marker == 0xD9
248            || key_marker == 0xDA
249            || key_marker == 0xDB
250        {
251            transcode_value(c, out)?;
252        } else {
253            let mut tmp = String::new();
254            transcode_value(c, &mut tmp)?;
255            out.push('"');
256            out.push_str(&tmp);
257            out.push('"');
258        }
259        out.push(':');
260        transcode_value(c, out)?;
261    }
262    out.push('}');
263    c.depth -= 1;
264    Ok(())
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use crate::json_msgpack::writer::json_to_msgpack;
271
272    #[test]
273    fn basic_map() {
274        let val = serde_json::json!({"name": "alice", "age": 30, "active": true});
275        let mp = json_to_msgpack(&val).unwrap();
276        let json_str = msgpack_to_json_string(&mp).unwrap();
277        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
278        assert_eq!(parsed["name"], "alice");
279        assert_eq!(parsed["age"], 30);
280        assert_eq!(parsed["active"], true);
281    }
282
283    #[test]
284    fn array() {
285        let val = serde_json::json!([1, "two", null, false]);
286        let mp = json_to_msgpack(&val).unwrap();
287        let json_str = msgpack_to_json_string(&mp).unwrap();
288        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
289        assert_eq!(parsed, val);
290    }
291
292    #[test]
293    fn escaping() {
294        let val = serde_json::json!({"msg": "hello \"world\"\nnewline"});
295        let mp = json_to_msgpack(&val).unwrap();
296        let json_str = msgpack_to_json_string(&mp).unwrap();
297        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
298        assert_eq!(parsed["msg"], "hello \"world\"\nnewline");
299    }
300
301    #[test]
302    fn empty() {
303        assert_eq!(msgpack_to_json_string(&[]).unwrap(), "");
304    }
305
306    #[test]
307    fn nested() {
308        let val = serde_json::json!({"a": {"b": [1, 2, {"c": 3}]}});
309        let mp = json_to_msgpack(&val).unwrap();
310        let json_str = msgpack_to_json_string(&mp).unwrap();
311        let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
312        assert_eq!(parsed, val);
313    }
314}