1use std::fmt::Write;
12
13pub fn number_to_string(m: f64) -> String {
20 if m.is_nan() {
22 return "NaN".to_string();
23 }
24 if m == 0.0 {
26 return "0".to_string();
27 }
28 if m == f64::INFINITY {
30 return "Infinity".to_string();
31 }
32 if m == f64::NEG_INFINITY {
33 return "-Infinity".to_string();
34 }
35
36 let mut out = String::new();
37 if m < 0.0 {
39 out.push('-');
40 }
41
42 let sci = format!("{:e}", m.abs());
47 let (mantissa, exp_str) = sci.split_once('e').expect("`{:e}` always has 'e'");
48 let e: i32 = exp_str.parse().expect("valid exponent");
49 let digits: Vec<u8> = mantissa.bytes().filter(|&b| b != b'.').collect();
50 let k = digits.len() as i32;
51 let n = e + 1;
53
54 if (-5..=21).contains(&n) {
55 if n >= k {
56 for &d in &digits {
58 out.push(d as char);
59 }
60 for _ in 0..(n - k) {
61 out.push('0');
62 }
63 } else if n > 0 {
64 for i in 0..n {
66 out.push(digits[i as usize] as char);
67 }
68 out.push('.');
69 for i in n..k {
70 out.push(digits[i as usize] as char);
71 }
72 } else {
73 out.push('0');
75 out.push('.');
76 for _ in 0..(-n) {
77 out.push('0');
78 }
79 for &d in &digits {
80 out.push(d as char);
81 }
82 }
83 } else {
84 let exponent_sign = if n < 0 { '-' } else { '+' };
86 let exp_val = (n - 1).unsigned_abs();
87 out.push(digits[0] as char);
88 if k != 1 {
89 out.push('.');
90 for i in 1..k {
91 out.push(digits[i as usize] as char);
92 }
93 }
94 out.push('e');
95 out.push(exponent_sign);
96 out.push_str(&exp_val.to_string());
97 }
98 out
99}
100
101#[derive(Clone, Copy, PartialEq, Eq, Debug)]
104enum StateType {
105 Dict,
106 Array,
107}
108
109#[derive(Debug)]
110struct State {
111 ty: StateType,
113 needs_comma: bool,
115 needs_key: bool,
117 needs_value: bool,
119 is_empty: bool,
121}
122
123impl State {
124 fn new(ty: StateType) -> State {
125 State {
126 ty,
127 needs_comma: false,
128 needs_key: ty == StateType::Dict,
129 needs_value: false,
130 is_empty: true,
131 }
132 }
133}
134
135pub struct JSONEmitter<'w> {
142 out: &'w mut String,
143 pretty: bool,
144 indent: u32,
145 states: Vec<State>,
146}
147
148impl<'w> JSONEmitter<'w> {
149 pub fn new(out: &'w mut String, pretty: bool) -> JSONEmitter<'w> {
150 JSONEmitter { out, pretty, indent: 0, states: Vec::new() }
151 }
152
153 fn in_dict(&self) -> bool {
154 matches!(self.states.last(), Some(s) if s.ty == StateType::Dict)
155 }
156 fn in_array(&self) -> bool {
157 matches!(self.states.last(), Some(s) if s.ty == StateType::Array)
158 }
159
160 fn will_emit_value(&mut self) {
162 if self.states.is_empty() {
163 return;
164 }
165 let is_array;
166 {
167 let state = self.states.last_mut().unwrap();
168 debug_assert!(!state.needs_key, "Expected a key");
169 if state.needs_comma {
170 self.out.push(',');
171 }
172 state.needs_key = state.ty == StateType::Dict;
173 state.needs_comma = true;
174 state.needs_value = false;
175 state.is_empty = false;
176 is_array = state.ty == StateType::Array;
177 }
178 if is_array {
179 self.pretty_new_line();
180 }
181 }
182
183 pub fn emit_bool(&mut self, val: bool) {
184 self.will_emit_value();
185 self.out.push_str(if val { "true" } else { "false" });
186 }
187
188 pub fn emit_i64(&mut self, val: i64) {
190 self.will_emit_value();
191 let _ = write!(self.out, "{val}");
192 }
193 pub fn emit_u64(&mut self, val: u64) {
194 self.will_emit_value();
195 let _ = write!(self.out, "{val}");
196 }
197
198 pub fn emit_f64(&mut self, val: f64) {
200 self.will_emit_value();
201 if val.is_finite() {
202 self.out.push_str(&number_to_string(val));
203 } else {
204 self.out.push_str("null");
205 }
206 }
207
208 pub fn emit_str(&mut self, val: &str) {
210 self.will_emit_value();
211 self.primitive_emit_string(val);
212 }
213
214 pub fn emit_u16(&mut self, val: &[u16]) {
217 self.will_emit_value();
218 self.out.push('"');
219 for &curr in val {
220 self.emit_one_escaped_unit(curr);
221 }
222 self.out.push('"');
223 }
224
225 pub fn emit_null_value(&mut self) {
226 self.will_emit_value();
227 self.out.push_str("null");
228 }
229
230 pub fn emit_key_u16(&mut self, key: &[u16]) {
234 debug_assert!(self.in_dict(), "Not emitting a dictionary");
235 {
236 let state = self.states.last_mut().unwrap();
237 debug_assert!(state.needs_key, "Not expecting a key");
238 debug_assert!(!state.needs_value, "Missing a value for a key.");
239 if state.needs_comma {
240 self.out.push(',');
241 }
242 state.needs_comma = false;
243 state.needs_key = false;
244 state.needs_value = true;
245 }
246 self.pretty_new_line();
247 self.out.push('"');
248 for &unit in key {
249 self.emit_one_escaped_unit(unit);
250 }
251 self.out.push('"');
252 self.out.push(':');
253 if self.pretty {
254 self.out.push(' ');
255 }
256 }
257
258 pub fn emit_key(&mut self, key: &str) {
260 debug_assert!(self.in_dict(), "Not emitting a dictionary");
261 {
262 let state = self.states.last_mut().unwrap();
263 debug_assert!(state.needs_key, "Not expecting a key");
264 debug_assert!(!state.needs_value, "Missing a value for a key.");
265 if state.needs_comma {
266 self.out.push(',');
267 }
268 state.needs_comma = false;
269 state.needs_key = false;
270 state.needs_value = true;
271 }
272 self.pretty_new_line();
275 self.primitive_emit_string(key);
276 self.out.push(':');
277 if self.pretty {
278 self.out.push(' ');
279 }
280 }
281
282 pub fn open_dict(&mut self) {
283 self.will_emit_value();
284 self.out.push('{');
285 self.indent_more();
286 self.states.push(State::new(StateType::Dict));
287 }
288 pub fn close_dict(&mut self) {
289 debug_assert!(self.in_dict(), "Not currently emitting a dictionary");
290 debug_assert!(!self.states.last().unwrap().needs_value, "Missing a value for a key.");
291 self.indent_less();
292 if !self.states.last().unwrap().is_empty {
293 self.pretty_new_line();
294 }
295 self.out.push('}');
296 self.states.pop();
297 }
298 pub fn open_array(&mut self) {
299 self.will_emit_value();
300 self.indent_more();
301 self.out.push('[');
302 self.states.push(State::new(StateType::Array));
303 }
304 pub fn close_array(&mut self) {
305 debug_assert!(self.in_array(), "Not currently emitting an array");
306 self.indent_less();
307 if !self.states.last().unwrap().is_empty {
308 self.pretty_new_line();
309 }
310 self.out.push(']');
311 self.states.pop();
312 }
313
314 pub fn end_jsonl(&mut self) {
316 debug_assert!(self.states.is_empty(), "Previous object was not terminated.");
317 self.out.push('\n');
318 }
319
320 fn primitive_emit_string(&mut self, s: &str) {
322 self.out.push('"');
323 for ch in s.chars() {
324 let cp = ch as u32;
325 if cp > 0x7F {
326 if cp <= 0xFFFF {
328 self.write_u_escape(cp as u16);
329 } else {
330 let c = cp - 0x10000;
331 self.write_u_escape(0xD800 + (c >> 10) as u16);
332 self.write_u_escape(0xDC00 + (c & 0x3FF) as u16);
333 }
334 continue;
335 }
336 if cp == 0x22 || cp == 0x5C || cp == 0x2F {
337 self.out.push('\\');
339 }
340 if cp >= 0x20 {
341 self.out.push(cp as u8 as char);
342 continue;
343 }
344 match cp {
345 0x08 => self.out.push_str("\\b"),
346 0x0C => self.out.push_str("\\f"),
347 0x0A => self.out.push_str("\\n"),
348 0x0D => self.out.push_str("\\r"),
349 0x09 => self.out.push_str("\\t"),
350 _ => self.write_u_escape(cp as u16),
351 }
352 }
353 self.out.push('"');
354 }
355
356 fn emit_one_escaped_unit(&mut self, curr: u16) {
358 let c = curr as u32;
359 if c > 0x7F {
360 self.write_u_escape(curr);
361 return;
362 }
363 if c >= 0x20 {
364 if c == 0x22 || c == 0x5C || c == 0x2F {
365 self.out.push('\\');
366 }
367 self.out.push(c as u8 as char);
368 return;
369 }
370 match c {
371 0x08 => self.out.push_str("\\b"),
372 0x0C => self.out.push_str("\\f"),
373 0x0A => self.out.push_str("\\n"),
374 0x0D => self.out.push_str("\\r"),
375 0x09 => self.out.push_str("\\t"),
376 _ => self.write_u_escape(curr),
377 }
378 }
379
380 fn write_u_escape(&mut self, u: u16) {
381 let _ = write!(self.out, "\\u{u:04x}");
382 }
383
384 fn pretty_new_line(&mut self) {
385 if !self.pretty {
386 return;
387 }
388 self.out.push('\n');
389 for _ in 0..self.indent {
390 self.out.push(' ');
391 }
392 }
393 fn indent_more(&mut self) {
394 if self.pretty {
395 self.indent += 2;
396 }
397 }
398 fn indent_less(&mut self) {
399 if self.pretty {
400 debug_assert!(self.indent >= 2, "Unbalanced indentation.");
401 self.indent -= 2;
402 }
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409
410 fn emit<F: FnOnce(&mut JSONEmitter)>(f: F) -> String {
411 let mut s = String::new();
412 {
413 let mut j = JSONEmitter::new(&mut s, false);
414 f(&mut j);
415 }
416 s
417 }
418
419 #[test]
420 fn empty_array() {
421 assert_eq!(emit(|j| { j.open_array(); j.close_array(); }), "[]");
422 }
423
424 #[test]
425 fn empty_dict() {
426 assert_eq!(emit(|j| { j.open_dict(); j.close_dict(); }), "{}");
427 }
428
429 #[test]
430 fn sample() {
431 let s = emit(|j| {
433 j.open_dict();
434 j.emit_key("name"); j.emit_str("hermes");
435 j.emit_key("age"); j.emit_i64(2);
436 j.emit_key("hot"); j.emit_bool(true);
437 j.emit_key("cold"); j.emit_bool(false);
438 j.emit_key("tags");
439 j.open_array();
440 j.emit_str("small"); j.emit_str("light");
441 j.close_array();
442 j.close_dict();
443 });
444 assert_eq!(s, r#"{"name":"hermes","age":2,"hot":true,"cold":false,"tags":["small","light"]}"#);
445 }
446
447 #[test]
448 fn smoke_with_double_and_escapes() {
449 let s = emit(|j| {
451 j.open_dict();
452 j.emit_key("a"); j.emit_i64(123);
453 j.emit_key("b"); j.emit_f64(456.7);
454 j.emit_key("dict1");
455 j.open_dict();
456 j.emit_key("dict1_arr1");
457 j.open_array();
458 j.emit_str("val1"); j.emit_str("val2"); j.emit_str("val3");
459 j.close_array();
460 j.emit_key("dict1_empty"); j.open_dict(); j.close_dict();
461 j.emit_key("dict1_empty2"); j.open_array(); j.close_array();
462 j.emit_key("str1"); j.emit_str("\"ABC\u{8}DEF\\");
463 j.close_dict();
464 j.close_dict();
465 });
466 assert_eq!(s, r#"{"a":123,"b":456.7,"dict1":{"dict1_arr1":["val1","val2","val3"],"dict1_empty":{},"dict1_empty2":[],"str1":"\"ABC\bDEF\\"}}"#);
467 }
468
469 #[test]
470 fn escapes() {
471 let s = emit(|j| j.emit_str("x\"\\/\u{8}\u{c}\n\r\tx"));
473 assert_eq!(s, r#""x\"\\\/\b\f\n\r\tx""#);
474 }
475
476 #[test]
477 fn forward_slashes() {
478 let s = emit(|j| {
480 j.open_dict();
481 j.emit_key("url"); j.emit_str("http://www.example.com");
482 j.close_dict();
483 });
484 assert_eq!(s, r#"{"url":"http:\/\/www.example.com"}"#);
485 }
486
487 #[test]
488 fn non_ascii_and_astral() {
489 let s = emit(|j| {
491 j.open_dict();
492 j.emit_key("ha"); j.emit_str("\u{54C8}");
493 j.emit_key("gClef"); j.emit_str("\u{1D11E}");
494 j.emit_key("wave"); j.emit_str("hi\u{1F44B}");
495 j.close_dict();
496 });
497 assert_eq!(s, r#"{"ha":"\u54c8","gClef":"\ud834\udd1e","wave":"hi\ud83d\udc4b"}"#);
498 }
499
500 #[test]
501 fn non_finite_is_null() {
502 let s = emit(|j| {
504 j.open_array();
505 j.emit_f64(f64::INFINITY); j.emit_f64(f64::NEG_INFINITY); j.emit_f64(f64::NAN);
506 j.close_array();
507 });
508 assert_eq!(s, "[null,null,null]");
509 }
510
511 #[test]
512 fn null_value() {
513 assert_eq!(emit(|j| j.emit_null_value()), "null");
514 }
515
516 #[test]
517 fn jsonl() {
518 let mut s = String::new();
519 {
520 let mut j = JSONEmitter::new(&mut s, false);
521 j.open_dict(); j.close_dict(); j.end_jsonl();
522 j.open_dict(); j.close_dict(); j.end_jsonl();
523 }
524 assert_eq!(s, "{}\n{}\n");
525 }
526
527 #[test]
528 fn emit_utf16() {
529 let units: Vec<u16> = vec![b'h' as u16, b'i' as u16, 0xd83d, 0xdc4b];
531 let mut s = String::new();
532 {
533 let mut j = JSONEmitter::new(&mut s, false);
534 j.open_dict();
535 j.emit_key("str"); j.emit_u16(&units);
536 j.close_dict();
537 }
538 assert_eq!(s, r#"{"str":"hi\ud83d\udc4b"}"#);
539 }
540
541 #[test]
542 fn pretty_print() {
543 let mut s = String::new();
545 {
546 let mut j = JSONEmitter::new(&mut s, true);
547 j.open_dict();
548 j.emit_key("artist"); j.emit_str("prince");
549 j.emit_key("instruments");
550 j.open_array();
551 j.emit_str("piano");
552 j.open_dict();
553 j.emit_key("guitars");
554 j.open_array();
555 j.emit_str("cloud"); j.emit_str("love symbol"); j.emit_str("telecaster");
556 j.close_array();
557 j.close_dict();
558 j.emit_str("drums");
559 j.close_array();
560 j.emit_key("songs");
561 j.open_dict();
562 j.emit_key("purple rain"); j.emit_i64(1984);
563 j.emit_key("1999"); j.emit_i64(1982);
564 j.close_dict();
565 j.emit_key("color"); j.emit_str("purple");
566 j.emit_key("emptyDict"); j.open_dict(); j.close_dict();
567 j.emit_key("emptyArray"); j.open_array(); j.close_array();
568 j.close_dict();
569 }
570 let expected = "{\n \"artist\": \"prince\",\n \"instruments\": [\n \"piano\",\n {\n \"guitars\": [\n \"cloud\",\n \"love symbol\",\n \"telecaster\"\n ]\n },\n \"drums\"\n ],\n \"songs\": {\n \"purple rain\": 1984,\n \"1999\": 1982\n },\n \"color\": \"purple\",\n \"emptyDict\": {},\n \"emptyArray\": []\n}";
571 assert_eq!(s, expected);
572 }
573
574 #[test]
575 fn emit_u16_astral_and_lone_surrogate() {
576 let mut s = String::new();
580 {
581 let mut j = JSONEmitter::new(&mut s, false);
582 j.open_dict();
583 j.emit_key_u16(&[0xD800, 0xDC00]); j.emit_u16(&[0xD800]); j.close_dict();
586 }
587 assert_eq!(s, "{\"\\ud800\\udc00\":\"\\ud800\"}");
588 }
589
590 #[test]
591 fn number_to_string_matches_ecmascript() {
592 let cases: &[(f64, &str)] = &[
594 (0.0, "0"),
595 (-0.0, "0"),
596 (1.0, "1"),
597 (-1.0, "-1"),
598 (456.7, "456.7"),
599 (100.0, "100"),
600 (0.1, "0.1"),
601 (0.0001, "0.0001"), (1e-6, "0.000001"), (1e-7, "1e-7"), (1e20, "100000000000000000000"), (1e21, "1e+21"), (123.45, "123.45"),
607 (5e-324, "5e-324"), (f64::NAN, "NaN"),
609 (f64::INFINITY, "Infinity"),
610 (f64::NEG_INFINITY, "-Infinity"),
611 ];
612 for &(v, expected) in cases {
613 assert_eq!(number_to_string(v), expected, "for {v:?}");
614 }
615 }
616}