Skip to main content

json_bourne/
ser.rs

1//! Type-driven serialization.
2//!
3//! Mirror image of [`crate::de`]. Each type knows how to write itself to a
4//! [`JsonWrite`] sink; the top-level entry point [`to_string`] (and
5//! [`to_vec`]) drives a `StringSink` and returns the resulting buffer.
6//!
7//! The sink is not [`core::fmt::Write`] for two reasons: (1) `fmt::Write`
8//! returns an opaque `fmt::Error` and forces every numeric write through
9//! the `Formatter` machinery, which is ~3× slower than direct push for
10//! integers; (2) we need a typed error path for non-finite floats
11//! ([`ErrorKind::NonFiniteFloat`]). The sink trait exposes per-primitive
12//! writers so impls can bypass formatting entirely.
13//!
14//! Float impls are not included here — they live in a follow-up that ports
15//! the ryu shortest-round-trip algorithm. Non-float primitives, composites,
16//! and the std/alloc adapters are all in scope.
17
18use crate::{Error, ErrorKind, Position};
19
20#[cfg(feature = "alloc")]
21extern crate alloc;
22
23#[cfg(feature = "alloc")]
24use alloc::string::String;
25
26// ---------------------------------------------------------------------------
27// Sink trait + StringSink
28// ---------------------------------------------------------------------------
29
30/// Output sink for [`ToJson`] impls.
31///
32/// Implementors expose per-primitive writers so the trait impls below can
33/// avoid the [`core::fmt`] machinery. The default integer/float methods
34/// land on `write_str_raw` after formatting into a small stack buffer, but
35/// implementations that can write directly (e.g. `StringSink`'s integer
36/// LUT) override them.
37pub trait JsonWrite {
38    /// Sink-specific error type. `StringSink` uses [`core::convert::Infallible`]
39    /// for the byte/string writes; the typed-level [`to_string`] entry point
40    /// widens to [`crate::Error`] so non-finite floats can surface.
41    type Error;
42
43    /// Hint that at least `additional` more bytes will be written. Sinks
44    /// backed by a growable buffer (like [`ByteSink`]) can amortize
45    /// capacity growth across a known-size sequence; sinks without a
46    /// reservation concept treat this as a no-op.
47    ///
48    /// Hot path: the array writer in this module calls this once at the
49    /// start of a slice/Vec serialization so per-element `reserve` calls
50    /// become predictable no-ops.
51    #[inline]
52    fn reserve_hint(&mut self, _additional: usize) {}
53
54    /// Append a single ASCII byte. Used for structural punctuation
55    /// (`{`, `}`, `[`, `]`, `,`, `:`, `"`).
56    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error>;
57
58    /// Append a single ASCII byte WITHOUT a per-write capacity check.
59    ///
60    /// # Safety
61    ///
62    /// The caller MUST have previously called [`Self::reserve_hint`] with
63    /// at least enough bytes to cover this write plus all subsequent
64    /// writes up to the next `reserve_hint` (or the end of writing).
65    /// Sinks that don't track a tail capacity (e.g. `fmt::Write`-backed)
66    /// fall back to the safe `write_byte`.
67    ///
68    /// Why this exists: under random `f64` inputs the per-element
69    /// capacity-check branch in `Vec::push` at the comma site mispredicts
70    /// at ~13% in `perf record -e branch-misses` — the single largest
71    /// mispredict source in the array hot loop. Hoisting the check via
72    /// `reserve_hint` and emitting the byte unchecked removes that
73    /// branch entirely from the inner loop.
74    ///
75    /// Default impl forwards to `write_byte` (safe but checked) so
76    /// non-`ByteSink` sinks aren't required to opt in.
77    ///
78    /// # Safety contract example (matches what `write_array` does)
79    /// ```ignore
80    /// w.reserve_hint(n * MAX + brackets_and_commas);
81    /// for _ in 0..count {
82    ///     // SAFETY: reserve_hint covered all of these.
83    ///     unsafe { w.write_byte_unchecked(b','); }
84    ///     w.write_int_u64(...);  // also covered by reserve_hint
85    /// }
86    /// ```
87    #[inline]
88    #[allow(unsafe_code)]
89    unsafe fn write_byte_unchecked(&mut self, b: u8) -> Result<(), Self::Error> {
90        self.write_byte(b)
91    }
92
93    /// Append a `&str` verbatim. Caller is responsible for any escaping
94    /// — this is the structural / pre-escaped path. For user string
95    /// payloads, call [`Self::write_escaped_str`] instead.
96    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error>;
97
98    /// Append a JSON-quoted, escaped string (including the surrounding
99    /// `"` characters). The default impl escapes one byte at a time
100    /// through `write_byte`; sinks with bulk-write capability (like
101    /// [`StringSink`]) override with a literal-run fast path.
102    #[inline]
103    fn write_escaped_str(&mut self, s: &str) -> Result<(), Self::Error> {
104        self.write_byte(b'"')?;
105        for &b in s.as_bytes() {
106            write_escape_byte(self, b)?;
107        }
108        self.write_byte(b'"')
109    }
110
111    /// Append a byte slice whose contents are known-valid UTF-8.
112    ///
113    /// Used by macro-generated code to fuse adjacent structural literals
114    /// (e.g. `b"{\"id\":"`) into a single write. The default routes
115    /// through [`Self::write_str_raw`]; byte-oriented sinks like
116    /// [`ByteSink`] override to avoid the `&str` conversion.
117    //
118    // The `expect` is unreachable: callers feed compile-time byte-string
119    // literals from `concat!` / `b"..."`, which are ASCII by construction.
120    #[inline]
121    fn write_raw_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error> {
122        let s = core::str::from_utf8(b).expect("write_raw_bytes input must be valid UTF-8");
123        self.write_str_raw(s)
124    }
125
126    /// Write a signed 64-bit integer as a JSON number.
127    #[inline]
128    fn write_int_i64(&mut self, n: i64) -> Result<(), Self::Error> {
129        let mut buf = [0u8; 20];
130        let s = format_i64(n, &mut buf);
131        self.write_str_raw(s)
132    }
133
134    /// Write an unsigned 64-bit integer as a JSON number.
135    #[inline]
136    fn write_int_u64(&mut self, n: u64) -> Result<(), Self::Error> {
137        let mut buf = [0u8; 20];
138        let s = format_u64(n, &mut buf);
139        self.write_str_raw(s)
140    }
141
142    /// Write a signed 128-bit integer as a JSON number.
143    #[inline]
144    fn write_int_i128(&mut self, n: i128) -> Result<(), Self::Error> {
145        let mut buf = [0u8; 40];
146        let s = format_i128(n, &mut buf);
147        self.write_str_raw(s)
148    }
149
150    /// Write an unsigned 128-bit integer as a JSON number.
151    #[inline]
152    fn write_int_u128(&mut self, n: u128) -> Result<(), Self::Error> {
153        let mut buf = [0u8; 40];
154        let s = format_u128(n, &mut buf);
155        self.write_str_raw(s)
156    }
157
158    /// Write a finite `f64` as a JSON number. Non-finite inputs (`inf`,
159    /// `-inf`, `NaN`) have no JSON representation; sinks reject them
160    /// via their `Self::Error` type.
161    ///
162    /// The default routes through `core::fmt::Write` after stack-buffer
163    /// formatting via `format!` — this is the "good enough" path that
164    /// works for any sink. Sinks that want shortest-round-trip output
165    /// without fmt overhead override with a direct ryu call.
166    #[cfg(feature = "alloc")]
167    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error>;
168
169    /// Write a finite `f64` ASSUMING the caller has reserved at least
170    /// `f64::MAX_SERIALIZED_LEN` (32) bytes of sink capacity. Skips the
171    /// per-call cap check in `Vec::extend_from_slice` — the second-largest
172    /// mispredict source after the comma `Vec::push`.
173    ///
174    /// Default impl forwards to `write_float_f64`. `ByteSink` overrides
175    /// with a raw-pointer write directly into the Vec's reserved tail.
176    ///
177    /// # Safety
178    /// `f64::MAX_SERIALIZED_LEN` (32) bytes of sink capacity must be
179    /// guaranteed available before this call. Used by `[f64]::
180    /// write_json_in_reserved` after `[T]::write_json`'s `reserve_hint`.
181    #[cfg(feature = "alloc")]
182    #[inline]
183    #[allow(unsafe_code)]
184    unsafe fn write_float_f64_unchecked(&mut self, f: f64) -> Result<(), Self::Error> {
185        self.write_float_f64(f)
186    }
187
188    /// Like `write_float_f64_unchecked`, but additionally requires the
189    /// caller to have validated that `f` is finite. The per-element
190    /// finiteness branch in the bench's hot loop was the dominant
191    /// remaining mispredict source after the cap-check elimination;
192    /// hoisting validation to a one-shot pre-scan over the whole slice
193    /// removes that branch from the inner loop entirely.
194    ///
195    /// Default impl forwards to `write_float_f64_unchecked`. `ByteSink`
196    /// overrides with the finite-known float path.
197    ///
198    /// # Safety
199    /// All preconditions of `write_float_f64_unchecked`, *plus* `f`
200    /// must be finite. Used after a successful `[f64]::pre_validate_slice`.
201    #[cfg(feature = "alloc")]
202    #[inline]
203    #[allow(unsafe_code)]
204    unsafe fn write_float_f64_unchecked_finite(&mut self, f: f64) -> Result<(), Self::Error> {
205        // SAFETY: weaker contract subsumes ours.
206        unsafe { self.write_float_f64_unchecked(f) }
207    }
208
209    /// Per-element float write that branchlessly tolerates non-finite
210    /// input. Tainted bytes (junk ASCII for non-finite values) land in
211    /// the sink; the caller is expected to query the sink's
212    /// `take_nonfinite_taint()` once at the end of the slice and turn a
213    /// non-zero result into a typed error.
214    ///
215    /// This avoids both the per-element finiteness branch AND the
216    /// pre-scan pass that hoisted it out of the loop in the previous
217    /// design (the SIMD-vectorised pand/pcmpeqd scan was costing ~20%
218    /// of total cycles per `perf record -c cycles`).
219    ///
220    /// Default impl forwards to `write_float_f64_unchecked` and emits a
221    /// dummy taint of 0 — sinks without taint tracking should never be
222    /// driven through this path.
223    ///
224    /// # Safety
225    /// `MAX_SERIALIZED_LEN` (32) bytes of sink capacity must be available.
226    #[cfg(feature = "alloc")]
227    #[inline]
228    #[allow(unsafe_code)]
229    unsafe fn write_float_f64_taint(&mut self, f: f64) -> Result<(), Self::Error> {
230        // SAFETY: weaker contract subsumes ours.
231        unsafe { self.write_float_f64_unchecked(f) }
232    }
233
234    /// Read and clear the accumulated non-finite taint from the sink.
235    /// Returns 0 for sinks without taint tracking. The array writer
236    /// queries this once after a batch of `write_float_f64_taint`
237    /// calls; a non-zero result means at least one input was inf/NaN
238    /// and the call must return `Err(NonFiniteFloat)`.
239    #[inline]
240    fn take_nonfinite_taint(&mut self) -> u64 {
241        0
242    }
243}
244
245const HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
246
247/// Escape sequences for bytes 0x00–0x1F plus `"` and `\`. `0` means
248/// the byte passes through verbatim; otherwise the value is the ASCII
249/// char after `\` (e.g. `b'n'` for `\n`).
250const ESCAPE_TABLE: [u8; 256] = {
251    let mut t = [0u8; 256];
252    t[b'"' as usize] = b'"';
253    t[b'\\' as usize] = b'\\';
254    t[b'\n' as usize] = b'n';
255    t[b'\r' as usize] = b'r';
256    t[b'\t' as usize] = b't';
257    t[0x08] = b'b';
258    t[0x0C] = b'f';
259    t
260};
261
262/// Write one byte of a string body, applying JSON escape rules.
263fn write_escape_byte<W: JsonWrite + ?Sized>(w: &mut W, b: u8) -> Result<(), W::Error> {
264    let esc = ESCAPE_TABLE[b as usize];
265    if esc != 0 {
266        w.write_byte(b'\\')?;
267        return w.write_byte(esc);
268    }
269    if b < 0x20 {
270        w.write_byte(b'\\')?;
271        w.write_byte(b'u')?;
272        w.write_byte(b'0')?;
273        w.write_byte(b'0')?;
274        w.write_byte(HEX_LOWER[(b >> 4) as usize])?;
275        return w.write_byte(HEX_LOWER[(b & 0x0F) as usize]);
276    }
277    w.write_byte(b)
278}
279
280/// Returns true for bytes that need an escape sequence inside a JSON string
281/// body (quote, backslash, or any control byte `< 0x20`). Everything else —
282/// including high-bit UTF-8 continuation bytes — is safe to write verbatim.
283///
284/// Only used by the escape-writing sinks (`StringSink`, `ByteSink`,
285/// `PrettyStringSink`), all of which are alloc-gated.
286#[cfg(feature = "alloc")]
287#[inline]
288const fn needs_escape(b: u8) -> bool {
289    b == b'"' || b == b'\\' || b < 0x20
290}
291
292/// `JsonWrite` sink that appends to a `String`. Infallible.
293#[cfg(feature = "alloc")]
294#[derive(Debug)]
295pub struct StringSink<'a> {
296    out: &'a mut String,
297}
298
299#[cfg(feature = "alloc")]
300impl<'a> StringSink<'a> {
301    #[must_use]
302    pub const fn new(out: &'a mut String) -> Self {
303        Self { out }
304    }
305}
306
307#[cfg(feature = "alloc")]
308impl JsonWrite for StringSink<'_> {
309    /// `StringSink` widens its error to [`Error`] (rather than
310    /// [`core::convert::Infallible`]) so the float path has a typed
311    /// variant for non-finite inputs. The byte/string/integer writes
312    /// never fail in practice; only `write_float_f64` produces an `Err`.
313    type Error = Error;
314
315    #[inline]
316    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
317        // SAFETY would be required for `as_mut_vec`; instead push as a char.
318        // Single-byte ASCII pushes go through `String::push` which is
319        // already an inlined `Vec::push` — no formatting overhead.
320        self.out.push(b as char);
321        Ok(())
322    }
323
324    #[inline]
325    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error> {
326        self.out.push_str(s);
327        Ok(())
328    }
329
330    /// Literal-run fast path: scan for the next byte that needs escaping,
331    /// bulk-append the safe stretch, then emit one escape and resume.
332    /// Mirrors the `decode_escapes` strategy in `de.rs` in reverse.
333    fn write_escaped_str(&mut self, s: &str) -> Result<(), Self::Error> {
334        self.out.push('"');
335        let bytes = s.as_bytes();
336        let mut i = 0;
337        let mut start = 0;
338        while i < bytes.len() {
339            let b = bytes[i];
340            if needs_escape(b) {
341                if start < i {
342                    // Safe stretch: every byte in [start..i] is either ASCII
343                    // non-special or part of a valid UTF-8 multi-byte run
344                    // (since `s` is `&str`, the input is valid UTF-8 and
345                    // the only single-byte stop conditions are the ones
346                    // `needs_escape` flags).
347                    self.out.push_str(&s[start..i]);
348                }
349                write_escape_byte(self, b)?;
350                start = i + 1;
351            }
352            i += 1;
353        }
354        if start < bytes.len() {
355            self.out.push_str(&s[start..]);
356        }
357        self.out.push('"');
358        Ok(())
359    }
360
361    /// Production float path. Currently dispatches to the `write!`-based
362    /// formatter — see the `float` module below for the alternate ryu
363    /// path and the bench that picks between them. Both reject non-finite
364    /// inputs with `ErrorKind::NonFiniteFloat`.
365    #[inline]
366    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error> {
367        float::format_f64_write(f, self.out)
368    }
369}
370
371// ---------------------------------------------------------------------------
372// ByteSink — fast path for to_string / to_vec
373// ---------------------------------------------------------------------------
374
375/// `JsonWrite` sink that appends to a `Vec<u8>`.
376///
377/// Bypasses `String`'s UTF-8 invariant maintenance — every byte
378/// written is known-valid by construction, so the caller can convert
379/// to `String` via `from_utf8_unchecked` after serialization completes.
380#[cfg(feature = "alloc")]
381#[derive(Debug)]
382pub struct ByteSink<'a> {
383    out: &'a mut alloc::vec::Vec<u8>,
384    /// Set whenever any float write encounters a non-finite input.
385    /// Read once at the end of the array path; if non-zero, the call
386    /// returns `Err(NonFiniteFloat)`. Threading this side-channel
387    /// avoids a per-element branch on the finiteness check (the
388    /// dominant remaining cliff source under random input).
389    nonfinite_taint: u64,
390}
391
392#[cfg(feature = "alloc")]
393impl<'a> ByteSink<'a> {
394    #[must_use]
395    pub const fn new(out: &'a mut alloc::vec::Vec<u8>) -> Self {
396        Self {
397            out,
398            nonfinite_taint: 0,
399        }
400    }
401}
402
403#[cfg(feature = "alloc")]
404impl JsonWrite for ByteSink<'_> {
405    type Error = Error;
406
407    #[inline]
408    fn reserve_hint(&mut self, additional: usize) {
409        self.out.reserve(additional);
410    }
411
412    #[inline]
413    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
414        self.out.push(b);
415        Ok(())
416    }
417
418    /// Branchless byte append. Skips `Vec::push`'s capacity check; relies
419    /// on the caller having reserved enough via `reserve_hint`.
420    ///
421    /// # Safety
422    /// `self.out.len() < self.out.capacity()` must hold. The caller's
423    /// `reserve_hint` is what makes this safe in `write_array` for
424    /// primitive slices.
425    #[inline]
426    #[allow(unsafe_code)]
427    unsafe fn write_byte_unchecked(&mut self, b: u8) -> Result<(), Self::Error> {
428        // SAFETY: caller-asserted len < capacity. Equivalent to
429        // `Vec::push` minus the cap check + grow path. Writing directly
430        // to the tail and bumping len keeps the per-call cost to two
431        // memory ops with no branches.
432        unsafe {
433            let len = self.out.len();
434            let ptr = self.out.as_mut_ptr().add(len);
435            core::ptr::write(ptr, b);
436            self.out.set_len(len + 1);
437        }
438        Ok(())
439    }
440
441    #[inline]
442    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error> {
443        self.out.extend_from_slice(s.as_bytes());
444        Ok(())
445    }
446
447    #[inline]
448    fn write_raw_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error> {
449        self.out.extend_from_slice(b);
450        Ok(())
451    }
452
453    fn write_escaped_str(&mut self, s: &str) -> Result<(), Self::Error> {
454        self.out.push(b'"');
455        let bytes = s.as_bytes();
456        let mut i = 0;
457        let mut start = 0;
458        while i < bytes.len() {
459            let b = bytes[i];
460            if needs_escape(b) {
461                if start < i {
462                    self.out.extend_from_slice(&bytes[start..i]);
463                }
464                write_escape_byte(self, b)?;
465                start = i + 1;
466            }
467            i += 1;
468        }
469        if start < bytes.len() {
470            self.out.extend_from_slice(&bytes[start..]);
471        }
472        self.out.push(b'"');
473        Ok(())
474    }
475
476    #[inline]
477    fn write_int_i64(&mut self, n: i64) -> Result<(), Self::Error> {
478        if n >= 0 {
479            #[allow(clippy::cast_sign_loss)]
480            return self.write_int_u64(n as u64);
481        }
482        self.out.push(b'-');
483        #[allow(clippy::cast_possible_truncation)]
484        let mag = i128::from(n).unsigned_abs() as u64;
485        self.write_int_u64(mag)
486    }
487
488    #[inline]
489    fn write_int_u64(&mut self, n: u64) -> Result<(), Self::Error> {
490        format_u64_direct(n, self.out);
491        Ok(())
492    }
493
494    #[inline]
495    fn write_int_i128(&mut self, n: i128) -> Result<(), Self::Error> {
496        let mut buf = [0u8; 40];
497        let s = format_i128(n, &mut buf);
498        self.out.extend_from_slice(s.as_bytes());
499        Ok(())
500    }
501
502    #[inline]
503    fn write_int_u128(&mut self, n: u128) -> Result<(), Self::Error> {
504        let mut buf = [0u8; 40];
505        let s = format_u128(n, &mut buf);
506        self.out.extend_from_slice(s.as_bytes());
507        Ok(())
508    }
509
510    #[inline]
511    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error> {
512        // `format_finite_to_vec` returns false for non-finite inputs (after
513        // doing its own bit-pattern check); folding the check in there keeps
514        // `f` in `xmm0` across the call and avoids the spill/reload LLVM
515        // produced when the check sat at this call site.
516        if crate::float::format_finite_to_vec(f, self.out) {
517            Ok(())
518        } else {
519            Err(Error::new(ErrorKind::NonFiniteFloat, Position::START))
520        }
521    }
522
523    /// SAFETY-relying override: caller must have reserved ≥ 32 bytes via
524    /// `reserve_hint`. Skips `Vec::extend_from_slice`'s per-call cap
525    /// check that the per-element float loop otherwise pays.
526    ///
527    /// The non-finite arm is outlined via `#[cold]` so the branch
528    /// predictor gets a strong static hint that the error path is rare.
529    /// On `perf record -e branch-misses` the inlined `Err::new(...)`
530    /// path inflated the finiteness `jg`'s mispredict rate; outlining
531    /// it pushed the cold-arm PCs out of the hot loop's history.
532    #[inline]
533    #[allow(unsafe_code)]
534    unsafe fn write_float_f64_unchecked(&mut self, f: f64) -> Result<(), Self::Error> {
535        // SAFETY: caller-guaranteed capacity (≥ 32) is exactly what
536        // `format_finite_to_vec_unchecked` requires.
537        if unsafe { crate::float::format_finite_to_vec_unchecked(f, self.out) } {
538            Ok(())
539        } else {
540            cold_nonfinite_byte_sink()
541        }
542    }
543
544    /// Finite-known override: skip both the cap check AND the finiteness
545    /// check. The slice writer guarantees both preconditions via a
546    /// one-shot pre-scan + `reserve_hint`. This removes the last
547    /// data-dependent branch from the bench's per-element loop.
548    ///
549    /// # Safety
550    /// `cap - len ≥ 32` AND `f.is_finite()`.
551    #[inline]
552    #[allow(unsafe_code)]
553    unsafe fn write_float_f64_unchecked_finite(&mut self, f: f64) -> Result<(), Self::Error> {
554        // SAFETY: contract forwarded.
555        unsafe { crate::float::format_finite_to_vec_unchecked_finite(f, self.out) };
556        Ok(())
557    }
558
559    /// Taint-tracking float write: accumulates non-finite-ness into the
560    /// sink's `nonfinite_taint` field for end-of-slice query. No
561    /// per-element branch on finiteness.
562    #[inline]
563    #[allow(unsafe_code)]
564    unsafe fn write_float_f64_taint(&mut self, f: f64) -> Result<(), Self::Error> {
565        // SAFETY: caller-reserved ≥ 32 bytes.
566        self.nonfinite_taint |= unsafe { crate::float::format_finite_to_vec_taint(f, self.out) };
567        Ok(())
568    }
569
570    #[inline]
571    fn take_nonfinite_taint(&mut self) -> u64 {
572        core::mem::take(&mut self.nonfinite_taint)
573    }
574}
575
576/// Outlined non-finite error path for `ByteSink`. `#[cold]` biases the
577/// branch predictor's static hint toward not-taken (the always-correct
578/// guess for well-formed input) and keeps the error-construction code
579/// out of the hot icache footprint.
580#[cfg(feature = "alloc")]
581#[cold]
582#[inline(never)]
583const fn cold_nonfinite_byte_sink() -> Result<(), Error> {
584    Err(Error::new(ErrorKind::NonFiniteFloat, Position::START))
585}
586
587// ---------------------------------------------------------------------------
588// Integer formatters — forward-write with digit-count precomputation.
589// ---------------------------------------------------------------------------
590//
591// `format!("{n}")` routes through `core::fmt::Formatter`, which on profile
592// is the dominant cost when a payload is integer-heavy. Hand-rolled
593// two-digit-at-a-time formatting using a 200-byte LUT runs ~4× faster on
594// a `Vec<i64>` workload and produces byte-for-byte identical output.
595//
596// The digit count is computed upfront via `leading_zeros()` + a lookup
597// table (Champagne-Gareau & Lemire, SPE 2026), then digits are written
598// forward from the end of the buffer toward the front. This eliminates
599// the backward-fill + `ptr::copy` shift the previous implementation paid.
600
601const DIGIT_LUT: &[u8; 200] = b"\
6020001020304050607080910111213141516171819\
6032021222324252627282930313233343536373839\
6044041424344454647484950515253545556575859\
6056061626364656667686970717273747576777879\
6068081828384858687888990919293949596979899";
607
608/// Branchless digit count for a `u64`.
609///
610/// Uses `leading_zeros()` to index into a table of candidate digit counts,
611/// then one comparison resolves the off-by-one boundary. The table stores
612/// `(candidate_digit_count, threshold)` pairs: if `n > threshold` the true
613/// count is `candidate + 1`, otherwise it's `candidate`. This runs in ~3
614/// cycles on x86-64 (`lzcnt` + table load + compare).
615#[inline]
616#[allow(clippy::cast_possible_truncation)]
617fn fast_digit_count(n: u64) -> usize {
618    // Table indexed by `leading_zeros(n | 1)`. Each entry is the largest
619    // value with `digit_count` digits; if `n` exceeds it, the true count
620    // is one more. Entry 63 covers n=0 and n=1 (lzcnt=63).
621    //
622    // Built from: for each lzcnt value z, the candidate digit count is
623    // floor(log10(2^(63-z))) + 1 when the range is unambiguous, with the
624    // threshold being 10^candidate - 1.
625    // Each entry is `(candidate, threshold)` where `candidate` is the
626    // digit count when `n <= threshold`, and `candidate + 1` when
627    // `n > threshold`. For lzcnt buckets where all values share the
628    // same digit count, threshold is set to the bucket's max so the
629    // `+1` never fires.
630    static TABLE: [(u8, u64); 64] = [
631        (19, 9_999_999_999_999_999_999), // lzcnt  0: 19 or 20 digits
632        (19, 9_223_372_036_854_775_807), // lzcnt  1: always 19
633        (19, 4_611_686_018_427_387_903), // lzcnt  2: always 19
634        (19, 2_305_843_009_213_693_951), // lzcnt  3: always 19
635        (18, 999_999_999_999_999_999),   // lzcnt  4: 18 or 19
636        (18, 576_460_752_303_423_487),   // lzcnt  5: always 18
637        (18, 288_230_376_151_711_743),   // lzcnt  6: always 18
638        (17, 99_999_999_999_999_999),    // lzcnt  7: 17 or 18
639        (17, 72_057_594_037_927_935),    // lzcnt  8: always 17
640        (17, 36_028_797_018_963_967),    // lzcnt  9: always 17
641        (16, 9_999_999_999_999_999),     // lzcnt 10: 16 or 17
642        (16, 9_007_199_254_740_991),     // lzcnt 11: always 16
643        (16, 4_503_599_627_370_495),     // lzcnt 12: always 16
644        (16, 2_251_799_813_685_247),     // lzcnt 13: always 16
645        (15, 999_999_999_999_999),       // lzcnt 14: 15 or 16
646        (15, 562_949_953_421_311),       // lzcnt 15: always 15
647        (15, 281_474_976_710_655),       // lzcnt 16: always 15
648        (14, 99_999_999_999_999),        // lzcnt 17: 14 or 15
649        (14, 70_368_744_177_663),        // lzcnt 18: always 14
650        (14, 35_184_372_088_831),        // lzcnt 19: always 14
651        (13, 9_999_999_999_999),         // lzcnt 20: 13 or 14
652        (13, 8_796_093_022_207),         // lzcnt 21: always 13
653        (13, 4_398_046_511_103),         // lzcnt 22: always 13
654        (13, 2_199_023_255_551),         // lzcnt 23: always 13
655        (12, 999_999_999_999),           // lzcnt 24: 12 or 13
656        (12, 549_755_813_887),           // lzcnt 25: always 12
657        (12, 274_877_906_943),           // lzcnt 26: always 12
658        (11, 99_999_999_999),            // lzcnt 27: 11 or 12
659        (11, 68_719_476_735),            // lzcnt 28: always 11
660        (11, 34_359_738_367),            // lzcnt 29: always 11
661        (10, 9_999_999_999),             // lzcnt 30: 10 or 11
662        (10, 8_589_934_591),             // lzcnt 31: always 10
663        (10, 4_294_967_295),             // lzcnt 32: always 10
664        (10, 2_147_483_647),             // lzcnt 33: always 10
665        (9, 999_999_999),                // lzcnt 34: 9 or 10
666        (9, 536_870_911),                // lzcnt 35: always 9
667        (9, 268_435_455),                // lzcnt 36: always 9
668        (8, 99_999_999),                 // lzcnt 37: 8 or 9
669        (8, 67_108_863),                 // lzcnt 38: always 8
670        (8, 33_554_431),                 // lzcnt 39: always 8
671        (7, 9_999_999),                  // lzcnt 40: 7 or 8
672        (7, 8_388_607),                  // lzcnt 41: always 7
673        (7, 4_194_303),                  // lzcnt 42: always 7
674        (7, 2_097_151),                  // lzcnt 43: always 7
675        (6, 999_999),                    // lzcnt 44: 6 or 7
676        (6, 524_287),                    // lzcnt 45: always 6
677        (6, 262_143),                    // lzcnt 46: always 6
678        (5, 99_999),                     // lzcnt 47: 5 or 6
679        (5, 65_535),                     // lzcnt 48: always 5
680        (5, 32_767),                     // lzcnt 49: always 5
681        (4, 9_999),                      // lzcnt 50: 4 or 5
682        (4, 8_191),                      // lzcnt 51: always 4
683        (4, 4_095),                      // lzcnt 52: always 4
684        (4, 2_047),                      // lzcnt 53: always 4
685        (3, 999),                        // lzcnt 54: 3 or 4
686        (3, 511),                        // lzcnt 55: always 3
687        (3, 255),                        // lzcnt 56: always 3
688        (2, 99),                         // lzcnt 57: 2 or 3
689        (2, 63),                         // lzcnt 58: always 2
690        (2, 31),                         // lzcnt 59: always 2
691        (1, 9),                          // lzcnt 60: 1 or 2
692        (1, 7),                          // lzcnt 61: always 1
693        (1, 3),                          // lzcnt 62: always 1
694        (1, 1),                          // lzcnt 63: always 1
695    ];
696    let lz = (n | 1).leading_zeros() as usize;
697    let (candidate, threshold) = TABLE[lz];
698    candidate as usize + usize::from(n > threshold)
699}
700
701/// Write `n` as decimal digits into `buf[0..end]`, filling from the tail
702/// toward index 0. Caller must ensure `buf` points to at least `end`
703/// writable bytes and that `end == fast_digit_count(n)`.
704#[inline]
705#[allow(clippy::cast_possible_truncation, unsafe_code)]
706fn write_digits_backward(mut n: u64, buf: *mut u8, end: usize) {
707    let mut pos = end;
708    while n >= 100 {
709        let r = (n % 100) as usize;
710        n /= 100;
711        pos -= 2;
712        // SAFETY: `pos` decreases in steps of 2 from `end` (which equals
713        // the digit count of the original `n`). The loop exits before
714        // `pos` underflows because each iteration consumes two decimal
715        // digits. Caller guarantees `buf[0..end]` is writable.
716        unsafe {
717            *buf.add(pos) = DIGIT_LUT[r * 2];
718            *buf.add(pos + 1) = DIGIT_LUT[r * 2 + 1];
719        }
720    }
721    if n >= 10 {
722        let r = n as usize;
723        pos -= 2;
724        unsafe {
725            *buf.add(pos) = DIGIT_LUT[r * 2];
726            *buf.add(pos + 1) = DIGIT_LUT[r * 2 + 1];
727        }
728    } else {
729        pos -= 1;
730        unsafe {
731            *buf.add(pos) = b'0' + n as u8;
732        }
733    }
734}
735
736/// Format a `u64` directly into a `Vec<u8>`. Precomputes digit count so
737/// digits land at their final position — no post-copy shift needed.
738#[cfg(feature = "alloc")]
739#[allow(clippy::cast_possible_truncation)]
740fn format_u64_direct(n: u64, out: &mut alloc::vec::Vec<u8>) {
741    let digits = fast_digit_count(n);
742    out.reserve(digits);
743    let old_len = out.len();
744    #[allow(unsafe_code)]
745    unsafe {
746        let base = out.as_mut_ptr().add(old_len);
747        write_digits_backward(n, base, digits);
748        out.set_len(old_len + digits);
749    }
750}
751
752/// Format a `u64` into `buf` (big-endian text). Returns the slice of
753/// `buf` that holds the digits.
754//
755// The `expect`s below are unreachable: every byte written is from
756// `DIGIT_LUT` (ASCII '0'..='9') or the literal `b'-'`. They are
757// monomorphization-time guards against a formatter-internal bug.
758#[allow(clippy::cast_possible_truncation)]
759fn format_u64(n: u64, buf: &mut [u8; 20]) -> &str {
760    let digits = fast_digit_count(n);
761    write_digits_backward(n, buf.as_mut_ptr(), digits);
762    core::str::from_utf8(&buf[..digits]).expect("integer formatter emits ASCII")
763}
764
765fn format_i64(n: i64, buf: &mut [u8; 20]) -> &str {
766    if n >= 0 {
767        #[allow(clippy::cast_sign_loss)]
768        return format_u64(n as u64, buf);
769    }
770    #[allow(clippy::cast_possible_truncation)]
771    let mag = i128::from(n).unsigned_abs() as u64;
772    let digits = fast_digit_count(mag);
773    buf[0] = b'-';
774    write_digits_backward(mag, buf[1..].as_mut_ptr(), digits);
775    let total = 1 + digits;
776    core::str::from_utf8(&buf[..total]).expect("integer formatter emits ASCII")
777}
778
779#[allow(clippy::cast_possible_truncation)]
780fn format_u128(mut n: u128, buf: &mut [u8; 40]) -> &str {
781    let mut pos = buf.len();
782    while n >= 100 {
783        let r = (n % 100) as usize;
784        n /= 100;
785        pos -= 2;
786        buf[pos] = DIGIT_LUT[r * 2];
787        buf[pos + 1] = DIGIT_LUT[r * 2 + 1];
788    }
789    if n >= 10 {
790        let r = n as usize;
791        pos -= 2;
792        buf[pos] = DIGIT_LUT[r * 2];
793        buf[pos + 1] = DIGIT_LUT[r * 2 + 1];
794    } else {
795        pos -= 1;
796        buf[pos] = b'0' + n as u8;
797    }
798    core::str::from_utf8(&buf[pos..]).expect("integer formatter emits ASCII")
799}
800
801fn format_i128(n: i128, buf: &mut [u8; 40]) -> &str {
802    if n >= 0 {
803        #[allow(clippy::cast_sign_loss)]
804        return format_u128(n as u128, buf);
805    }
806    let mag = n.unsigned_abs();
807    let mut tmp = [0u8; 40];
808    let s = format_u128(mag, &mut tmp);
809    let len = s.len();
810    buf[0] = b'-';
811    buf[1..=len].copy_from_slice(s.as_bytes());
812    let total = 1 + len;
813    core::str::from_utf8(&buf[..total]).expect("integer formatter emits ASCII")
814}
815
816// ---------------------------------------------------------------------------
817// Float formatting.
818//
819// `format_f64_write` dispatches to the in-tree Grisu3 formatter
820// (`crate::float::format_finite`), which falls back to libstd's
821// `Display for f64` on the ~0.5% of inputs where Grisu3 cannot prove
822// its output is the shortest round-trip representation.
823//
824// The bench in `bourne-bench/floats` pins this entry point by name.
825// ---------------------------------------------------------------------------
826
827#[cfg(feature = "alloc")]
828pub mod float {
829    //! Public so the head-to-head bench in `bourne-bench` can pin the
830    //! production formatter by name; not part of the documented API
831    //! surface.
832
833    use super::{Error, ErrorKind, Position};
834    use alloc::string::String;
835
836    /// Reject `inf` / `-inf` / `NaN` with a typed error. Position is
837    /// `START` because serializer errors don't have an input byte to
838    /// point at — symmetric with how the parse side reports
839    /// "byte offset" errors.
840    #[inline]
841    const fn reject_non_finite(f: f64) -> Result<(), Error> {
842        if f.is_finite() {
843            Ok(())
844        } else {
845            Err(Error::new(ErrorKind::NonFiniteFloat, Position::START))
846        }
847    }
848
849    /// Production float formatter. Delegates correctness to the
850    /// in-tree Grisu3 implementation (`crate::float`). Non-finite
851    /// inputs are rejected before any digit work happens.
852    #[inline]
853    pub fn format_f64_write(f: f64, out: &mut String) -> Result<(), Error> {
854        reject_non_finite(f)?;
855        crate::float::format_finite(f, out);
856        Ok(())
857    }
858}
859
860// ---------------------------------------------------------------------------
861// ToJson trait + entry points
862// ---------------------------------------------------------------------------
863
864/// Types that know how to serialize themselves to a [`JsonWrite`] sink.
865///
866/// This is the dual of [`crate::FromJson`]. Implementors call sink methods
867/// directly — there is no intermediate `Value` representation.
868pub trait ToJson {
869    /// Lower bound on the number of bytes `write_json` will emit.
870    ///
871    /// Used by [`to_vec`] / [`to_string`] to size the initial allocation.
872    /// Defaults to `0` so existing impls aren't forced to provide it.
873    const MIN_SERIALIZED_LEN: usize = 0;
874
875    /// Upper bound on the bytes a single `write_json` call emits, used by
876    /// sequence impls to pre-reserve buffer capacity. `0` means "no useful
877    /// upper bound" — sequence impls skip the reservation in that case.
878    ///
879    /// Primitives with a known maximum output length (floats, ints, bools)
880    /// override this. Variable-length types (`str`, `Vec<T>`, structs) keep
881    /// the default, since their per-element size depends on payload.
882    const MAX_SERIALIZED_LEN: usize = 0;
883
884    /// Serialize `self` into `w`.
885    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error>;
886
887    /// Serialize `self` into `w` ASSUMING the caller has already reserved
888    /// at least `MAX_SERIALIZED_LEN` writable bytes in the sink. Used by
889    /// [`[T]::write_json`] after its `reserve_hint` call so the per-element
890    /// path skips its own cap checks.
891    ///
892    /// Default impl forwards to `write_json` (safe + checked). Primitives
893    /// override with sink-specific unchecked variants. For `MAX_SERIALIZED_LEN
894    /// == 0` types this method is never called.
895    ///
896    /// # Safety
897    /// `Self::MAX_SERIALIZED_LEN` bytes of sink capacity must be guaranteed
898    /// available before this call. For types where
899    /// [`Self::NEEDS_VALIDATION`] is `true`, [`Self::pre_validate_slice`]
900    /// must have been called on the enclosing slice (and returned `Ok`)
901    /// before invoking this method, so the per-element write can assume
902    /// the type's validity precondition (e.g. finiteness for floats).
903    #[inline]
904    #[allow(unsafe_code)]
905    unsafe fn write_json_in_reserved<W: JsonWrite + ?Sized>(
906        &self,
907        w: &mut W,
908    ) -> Result<(), W::Error> {
909        self.write_json(w)
910    }
911
912    /// Whether this type requires a pre-scan validation pass before
913    /// the per-element fast path can be entered. Defaults to `false`.
914    ///
915    /// `f64` / `f32` set this to `true` so the slice writer scans the
916    /// whole slice once for non-finite values and rejects up front.
917    /// The inner element-by-element loop then runs branchless w.r.t.
918    /// finiteness — measured 13.43% of all mispredicts before the
919    /// pre-scan was added.
920    const NEEDS_VALIDATION: bool = false;
921
922    /// Validate that every element of `slice` satisfies the type's
923    /// per-element precondition (e.g. finiteness). Called by the slice
924    /// writer ONCE before the per-element loop when `NEEDS_VALIDATION`
925    /// is `true`.
926    ///
927    /// Default impl is a no-op. The `Result` carries `crate::Error`
928    /// directly because all current validators reject with that type;
929    /// callers that want a different sink error must map.
930    ///
931    /// `slice` is `&[Self]` rather than `&[T]` because the validator
932    /// always sees a homogeneous slice (it's invoked from the slice
933    /// impl). On non-slice paths this method is never called.
934    #[inline]
935    fn pre_validate_slice(slice: &[Self]) -> Result<(), Error>
936    where
937        Self: Sized,
938    {
939        let _ = slice;
940        Ok(())
941    }
942}
943
944/// Serialize `value` into a fresh `String`.
945///
946/// Returns [`Error`] (rather than `Infallible`) so non-finite floats and
947/// other typed-level failures have a place to surface.
948///
949/// Internally serializes into a `Vec<u8>` via [`ByteSink`] (avoiding
950/// per-byte UTF-8 invariant checks), then converts to `String` in one
951/// step. The resulting bytes are guaranteed valid UTF-8 because every
952/// `JsonWrite` method only emits ASCII structural bytes, `&str` slices
953/// (valid by construction), ASCII escape sequences, and ASCII digit
954/// sequences from the integer/float formatters.
955// `expect` below is unreachable from user input: every `JsonWrite` path
956// emits valid UTF-8 by construction (see `ByteSink`'s impl). The check
957// guards against a serializer-internal bug, not a user-triggerable case.
958#[cfg(feature = "alloc")]
959#[allow(clippy::missing_panics_doc)]
960pub fn to_string<T: ToJson + ?Sized>(value: &T) -> Result<String, Error> {
961    let bytes = to_vec(value)?;
962    Ok(String::from_utf8(bytes).expect("json-bourne emits only valid UTF-8"))
963}
964
965/// Serialize `value` into a fresh `Vec<u8>`.
966///
967/// This is the primary fast path: writes directly into `Vec<u8>` via
968/// [`ByteSink`], bypassing `String`'s per-write UTF-8 invariant checks.
969#[cfg(feature = "alloc")]
970pub fn to_vec<T: ToJson + ?Sized>(value: &T) -> Result<alloc::vec::Vec<u8>, Error> {
971    let cap = if T::MIN_SERIALIZED_LEN > 128 {
972        T::MIN_SERIALIZED_LEN
973    } else {
974        128
975    };
976    let mut out = alloc::vec::Vec::with_capacity(cap);
977    let mut sink = ByteSink::new(&mut out);
978    value.write_json(&mut sink)?;
979    Ok(out)
980}
981
982// ---------------------------------------------------------------------------
983// fmt::Write sink
984// ---------------------------------------------------------------------------
985
986/// `JsonWrite` sink that forwards to any `core::fmt::Write` implementor.
987///
988/// Useful when the destination is something other than a `String` —
989/// `&mut String` is the obvious case, but any `fmt::Write` works (a
990/// `Formatter`, a custom buffered writer, a tracing-style accumulator).
991///
992/// The error type is [`core::fmt::Error`] for byte/string writes and
993/// [`Error`] for the float path; the unified sink-level error is
994/// [`Error`], with `core::fmt::Error` mapped to a generic write
995/// failure.
996#[cfg(feature = "alloc")]
997#[derive(Debug)]
998pub struct FmtWriteSink<'a, W: ?Sized> {
999    out: &'a mut W,
1000}
1001
1002#[cfg(feature = "alloc")]
1003impl<'a, W: core::fmt::Write + ?Sized> FmtWriteSink<'a, W> {
1004    pub const fn new(out: &'a mut W) -> Self {
1005        Self { out }
1006    }
1007}
1008
1009#[cfg(feature = "alloc")]
1010impl<W: core::fmt::Write + ?Sized> JsonWrite for FmtWriteSink<'_, W> {
1011    type Error = Error;
1012
1013    #[inline]
1014    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
1015        self.out
1016            .write_char(b as char)
1017            .map_err(|_| fmt_write_error())
1018    }
1019
1020    #[inline]
1021    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error> {
1022        self.out.write_str(s).map_err(|_| fmt_write_error())
1023    }
1024
1025    #[inline]
1026    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error> {
1027        if !f.is_finite() {
1028            return Err(Error::new(ErrorKind::NonFiniteFloat, Position::START));
1029        }
1030        crate::float::format_finite_fmt(f, self.out).map_err(|_| fmt_write_error())
1031    }
1032}
1033
1034/// `fmt::Write` errors don't carry detail. Map to a typed parse-style
1035/// error so callers can distinguish the failure mode without losing
1036/// the trait's error contract.
1037#[cfg(feature = "alloc")]
1038#[inline]
1039const fn fmt_write_error() -> Error {
1040    Error::new(ErrorKind::TypeMismatch, Position::START)
1041}
1042
1043// ---------------------------------------------------------------------------
1044// io::Write sink (std-only)
1045// ---------------------------------------------------------------------------
1046
1047/// `JsonWrite` sink that forwards to any `std::io::Write` implementor.
1048///
1049/// The natural target for serializing JSON to a file, socket, or other
1050/// byte stream. Errors propagate through the sink's `Self::Error`,
1051/// which is [`std::io::Error`].
1052#[cfg(feature = "std")]
1053#[derive(Debug)]
1054pub struct IoWriteSink<'a, W: ?Sized> {
1055    out: &'a mut W,
1056}
1057
1058#[cfg(feature = "std")]
1059impl<'a, W: std::io::Write + ?Sized> IoWriteSink<'a, W> {
1060    pub const fn new(out: &'a mut W) -> Self {
1061        Self { out }
1062    }
1063}
1064
1065/// Adapter that lets `core::fmt::Write` write into an `io::Write` sink.
1066/// `format_finite_fmt` only needs `fmt::Write`; this carries the
1067/// underlying I/O error out so the float path's failures don't get
1068/// flattened into a generic "fmt failed".
1069#[cfg(feature = "std")]
1070struct IoFmtAdapter<'a, W: std::io::Write + ?Sized> {
1071    inner: &'a mut W,
1072    err: Option<std::io::Error>,
1073}
1074
1075#[cfg(feature = "std")]
1076impl<W: std::io::Write + ?Sized> core::fmt::Write for IoFmtAdapter<'_, W> {
1077    fn write_str(&mut self, s: &str) -> core::fmt::Result {
1078        match self.inner.write_all(s.as_bytes()) {
1079            Ok(()) => Ok(()),
1080            Err(e) => {
1081                self.err = Some(e);
1082                Err(core::fmt::Error)
1083            }
1084        }
1085    }
1086}
1087
1088#[cfg(feature = "std")]
1089impl<W: std::io::Write + ?Sized> JsonWrite for IoWriteSink<'_, W> {
1090    type Error = std::io::Error;
1091
1092    #[inline]
1093    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
1094        self.out.write_all(&[b])
1095    }
1096
1097    #[inline]
1098    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error> {
1099        self.out.write_all(s.as_bytes())
1100    }
1101
1102    #[inline]
1103    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error> {
1104        if !f.is_finite() {
1105            return Err(std::io::Error::new(
1106                std::io::ErrorKind::InvalidData,
1107                "non-finite float not representable in JSON",
1108            ));
1109        }
1110        let mut adapter = IoFmtAdapter {
1111            inner: self.out,
1112            err: None,
1113        };
1114        match crate::float::format_finite_fmt(f, &mut adapter) {
1115            Ok(()) => Ok(()),
1116            Err(_) => Err(adapter
1117                .err
1118                .unwrap_or_else(|| std::io::Error::other("fmt error in float formatter"))),
1119        }
1120    }
1121}
1122
1123/// Serialize `value` directly into a [`std::io::Write`] sink.
1124///
1125/// The standard "stream JSON to a file/socket" entry point. For an
1126/// in-memory build use [`to_string`] / [`to_vec`].
1127#[cfg(feature = "std")]
1128pub fn to_writer<T: ToJson + ?Sized, W: std::io::Write>(
1129    value: &T,
1130    writer: &mut W,
1131) -> Result<(), std::io::Error> {
1132    let mut sink = IoWriteSink::new(writer);
1133    value.write_json(&mut sink)
1134}
1135
1136/// Serialize `value` into any [`core::fmt::Write`] sink.
1137///
1138/// Returns the underlying [`Error`] (typed) on failure — including
1139/// non-finite floats. For `String` targets prefer [`to_string`]; this
1140/// entry point is for arbitrary `fmt::Write` consumers.
1141#[cfg(feature = "alloc")]
1142pub fn to_fmt<T: ToJson + ?Sized, W: core::fmt::Write + ?Sized>(
1143    value: &T,
1144    writer: &mut W,
1145) -> Result<(), Error> {
1146    let mut sink = FmtWriteSink::new(writer);
1147    value.write_json(&mut sink)
1148}
1149
1150// ---------------------------------------------------------------------------
1151// Pretty-print sink
1152// ---------------------------------------------------------------------------
1153
1154/// `JsonWrite` sink that emits indented, multi-line JSON.
1155///
1156/// Wraps a `String` and tracks container depth + a one-byte lookahead
1157/// (`pending_open`). When an opener (`[` / `{`) is followed immediately
1158/// by the matching closer (no contents), the sink emits the compact
1159/// form `[]` / `{}`. Otherwise it inserts a newline plus the current
1160/// indent before each element and before the closing bracket.
1161///
1162/// Indent unit defaults to two spaces; configure via [`Self::with_indent`].
1163#[cfg(feature = "alloc")]
1164#[derive(Debug)]
1165pub struct PrettyStringSink<'a> {
1166    out: &'a mut String,
1167    indent: &'static str,
1168    depth: usize,
1169    /// `Some(b)` when we wrote `[` or `{` and haven't yet decided
1170    /// whether the container is empty. The next structural byte
1171    /// resolves it: a matching close → emit `[]` / `{}`; anything
1172    /// else → flush the open + newline + indent for the first
1173    /// element, then continue.
1174    pending_open: Option<u8>,
1175}
1176
1177#[cfg(feature = "alloc")]
1178impl<'a> PrettyStringSink<'a> {
1179    /// Build a pretty sink writing to `out` with the default 2-space
1180    /// indent.
1181    #[must_use]
1182    pub const fn new(out: &'a mut String) -> Self {
1183        Self {
1184            out,
1185            indent: "  ",
1186            depth: 0,
1187            pending_open: None,
1188        }
1189    }
1190
1191    /// Build a pretty sink with a custom indent string. Pass `"\t"`
1192    /// for tabs, `"    "` for four spaces, etc. The indent must be
1193    /// pure whitespace — JSON doesn't validate it on the wire, but
1194    /// emitting non-whitespace would corrupt the output.
1195    #[must_use]
1196    pub const fn with_indent(out: &'a mut String, indent: &'static str) -> Self {
1197        Self {
1198            out,
1199            indent,
1200            depth: 0,
1201            pending_open: None,
1202        }
1203    }
1204
1205    /// Resolve any pending open by emitting it and dropping the
1206    /// pending state. Used before writing any non-structural byte
1207    /// (a value's first byte).
1208    fn flush_pending_open(&mut self) {
1209        if let Some(b) = self.pending_open.take() {
1210            self.out.push(b as char);
1211            self.depth += 1;
1212            self.newline_and_indent();
1213        }
1214    }
1215
1216    fn newline_and_indent(&mut self) {
1217        self.out.push('\n');
1218        for _ in 0..self.depth {
1219            self.out.push_str(self.indent);
1220        }
1221    }
1222}
1223
1224#[cfg(feature = "alloc")]
1225impl JsonWrite for PrettyStringSink<'_> {
1226    type Error = Error;
1227
1228    fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
1229        match b {
1230            b'[' | b'{' => {
1231                // Resolve any prior pending open: the prior container
1232                // is non-empty, so emit it + indent for our position.
1233                self.flush_pending_open();
1234                // Defer this open — we don't know yet if it's empty.
1235                self.pending_open = Some(b);
1236                Ok(())
1237            }
1238            b']' | b'}' => {
1239                if let Some(open) = self.pending_open.take() {
1240                    // Empty container: write the open and matching
1241                    // close back-to-back with no whitespace.
1242                    self.out.push(open as char);
1243                    self.out.push(b as char);
1244                    return Ok(());
1245                }
1246                self.depth -= 1;
1247                self.newline_and_indent();
1248                self.out.push(b as char);
1249                Ok(())
1250            }
1251            b',' => {
1252                // After a value inside a container: newline + indent
1253                // before the next element. The pending_open state
1254                // can't be live here — we must have written at least
1255                // one value to be at a comma.
1256                debug_assert!(self.pending_open.is_none());
1257                self.out.push(',');
1258                self.newline_and_indent();
1259                Ok(())
1260            }
1261            b':' => {
1262                // Object key/value separator. JSON-pretty convention
1263                // is `key: value` (one space after the colon, none
1264                // before).
1265                self.out.push_str(": ");
1266                Ok(())
1267            }
1268            _ => {
1269                // Any other single byte (rare via this entry — most
1270                // bulk text comes through write_str_raw or
1271                // write_escaped_str).
1272                self.flush_pending_open();
1273                self.out.push(b as char);
1274                Ok(())
1275            }
1276        }
1277    }
1278
1279    fn write_str_raw(&mut self, s: &str) -> Result<(), Self::Error> {
1280        if s.is_empty() {
1281            return Ok(());
1282        }
1283        self.flush_pending_open();
1284        self.out.push_str(s);
1285        Ok(())
1286    }
1287
1288    fn write_escaped_str(&mut self, s: &str) -> Result<(), Self::Error> {
1289        self.flush_pending_open();
1290        // Reuse the StringSink escape walk by constructing one
1291        // transiently. The borrow lasts only for this call.
1292        let mut inner = StringSink::new(self.out);
1293        inner.write_escaped_str(s)
1294    }
1295
1296    fn write_float_f64(&mut self, f: f64) -> Result<(), Self::Error> {
1297        self.flush_pending_open();
1298        float::format_f64_write(f, self.out)
1299    }
1300}
1301
1302/// Pretty-printed equivalent of [`to_string`]. Two-space indent, one
1303/// space after `:`, newline between every element. Empty containers
1304/// are kept compact (`[]` / `{}`).
1305#[cfg(feature = "alloc")]
1306pub fn to_string_pretty<T: ToJson + ?Sized>(value: &T) -> Result<String, Error> {
1307    let mut out = String::with_capacity(256);
1308    let mut sink = PrettyStringSink::new(&mut out);
1309    value.write_json(&mut sink)?;
1310    Ok(out)
1311}
1312
1313// ---------------------------------------------------------------------------
1314// Primitive impls
1315// ---------------------------------------------------------------------------
1316
1317impl ToJson for bool {
1318    const MIN_SERIALIZED_LEN: usize = 4; // "true"
1319    const MAX_SERIALIZED_LEN: usize = 5; // "false"
1320    #[inline]
1321    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1322        w.write_str_raw(if *self { "true" } else { "false" })
1323    }
1324}
1325
1326impl ToJson for () {
1327    const MIN_SERIALIZED_LEN: usize = 4; // "null"
1328    const MAX_SERIALIZED_LEN: usize = 4;
1329    #[inline]
1330    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1331        w.write_str_raw("null")
1332    }
1333}
1334
1335impl ToJson for str {
1336    const MIN_SERIALIZED_LEN: usize = 2; // "\"\""
1337    #[inline]
1338    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1339        w.write_escaped_str(self)
1340    }
1341}
1342
1343// `&str` is the form callers actually hold. The blanket `&T` impl below
1344// covers it via the `str` impl above — no explicit `&str` arm needed.
1345
1346macro_rules! impl_int_signed {
1347    ($($t:ty),* $(,)?) => {
1348        $(
1349            impl ToJson for $t {
1350                const MIN_SERIALIZED_LEN: usize = 1;
1351                // sign + digits. i64::MIN is "-9223372036854775808" = 20 chars.
1352                const MAX_SERIALIZED_LEN: usize = 20;
1353                #[inline]
1354                fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1355                    w.write_int_i64(i64::from(*self))
1356                }
1357            }
1358        )*
1359    };
1360}
1361
1362macro_rules! impl_int_unsigned {
1363    ($($t:ty),* $(,)?) => {
1364        $(
1365            impl ToJson for $t {
1366                const MIN_SERIALIZED_LEN: usize = 1;
1367                // u64::MAX = "18446744073709551615" = 20 chars.
1368                const MAX_SERIALIZED_LEN: usize = 20;
1369                #[inline]
1370                fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1371                    w.write_int_u64(u64::from(*self))
1372                }
1373            }
1374        )*
1375    };
1376}
1377
1378impl_int_signed!(i8, i16, i32, i64);
1379impl_int_unsigned!(u8, u16, u32, u64);
1380
1381// `isize` / `usize` widen to 64-bit on the platforms json-bourne supports.
1382// `as` is acceptable here: the cast is the documented platform widening,
1383// not a truncation.
1384#[allow(clippy::cast_possible_wrap, clippy::cast_lossless, clippy::use_self)]
1385impl ToJson for isize {
1386    const MIN_SERIALIZED_LEN: usize = 1;
1387    const MAX_SERIALIZED_LEN: usize = 20;
1388    #[inline]
1389    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1390        w.write_int_i64(*self as i64)
1391    }
1392}
1393
1394#[allow(clippy::cast_lossless, clippy::use_self)]
1395impl ToJson for usize {
1396    const MIN_SERIALIZED_LEN: usize = 1;
1397    const MAX_SERIALIZED_LEN: usize = 20;
1398    #[inline]
1399    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1400        w.write_int_u64(*self as u64)
1401    }
1402}
1403
1404impl ToJson for i128 {
1405    const MIN_SERIALIZED_LEN: usize = 1;
1406    // sign + 39 digits for i128::MIN.
1407    const MAX_SERIALIZED_LEN: usize = 40;
1408    #[inline]
1409    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1410        w.write_int_i128(*self)
1411    }
1412}
1413
1414impl ToJson for u128 {
1415    const MIN_SERIALIZED_LEN: usize = 1;
1416    // u128::MAX has 39 digits.
1417    const MAX_SERIALIZED_LEN: usize = 39;
1418    #[inline]
1419    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1420        w.write_int_u128(*self)
1421    }
1422}
1423
1424// Composite: Option<T> writes `null` for None or the inner value for Some.
1425// Mirrors the FromJson direction.
1426impl<T: ToJson> ToJson for Option<T> {
1427    const MIN_SERIALIZED_LEN: usize = 4; // "null"
1428    #[inline]
1429    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1430        match self {
1431            None => w.write_str_raw("null"),
1432            Some(v) => v.write_json(w),
1433        }
1434    }
1435}
1436
1437// References pass through. `&T: ToJson` whenever `T: ToJson` lets callers
1438// pass `&value` or `&&value` indifferently.
1439impl<T: ToJson + ?Sized> ToJson for &T {
1440    const MIN_SERIALIZED_LEN: usize = T::MIN_SERIALIZED_LEN;
1441    #[inline]
1442    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1443        (*self).write_json(w)
1444    }
1445}
1446
1447// Slices and fixed-size arrays serialize as JSON arrays. The shared
1448// helper writes the bracketed comma-separated body so impls for Vec,
1449// slice, and [T; N] don't drift on punctuation.
1450//
1451// `vec_write_json` is the symmetric hook to FromJson's `vec_from_lex`:
1452// types like `i64` / `&str` whose per-element write goes through method
1453// dispatch can override it to write directly through the sink. Default
1454// loops `T::write_json` per element.
1455fn write_array<T: ToJson, I: IntoIterator<Item = T>, W: JsonWrite + ?Sized>(
1456    iter: I,
1457    w: &mut W,
1458) -> Result<(), W::Error> {
1459    w.write_byte(b'[')?;
1460    // Peel the first element so the inner loop never re-evaluates a
1461    // `first` flag — every subsequent element unconditionally writes
1462    // `,` then itself. Saves one branch per element on hot Vec/slice
1463    // paths where the iterator is `ExactSizeIterator` and the compiler
1464    // can hoist the check out of the loop.
1465    let mut iter = iter.into_iter();
1466    if let Some(v) = iter.next() {
1467        v.write_json(w)?;
1468        for v in iter {
1469            w.write_byte(b',')?;
1470            v.write_json(w)?;
1471        }
1472    }
1473    w.write_byte(b']')
1474}
1475
1476/// Like `write_array`, but emits the brackets and commas via
1477/// `write_byte_unchecked`. Callable only after `reserve_hint` has been
1478/// invoked with at least `2 + len + len * MAX_SERIALIZED_LEN` bytes.
1479///
1480/// Why this exists: `perf record -e branch-misses` on the n=10000
1481/// float-array workload identified the comma capacity-check branch
1482/// (`cmp len, cap; jne grow`) as the single largest mispredict source —
1483/// 12.96% of all mispredicts in `to_string`. Hoisting the capacity check
1484/// via `reserve_hint` and emitting the comma unchecked removes that
1485/// branch from the inner loop entirely.
1486///
1487/// # Safety
1488/// Caller must have reserved enough sink capacity to cover `2 + len +
1489/// per_elem_max * len` bytes after the call to `reserve_hint`.
1490#[allow(unsafe_code)]
1491unsafe fn write_array_reserved<T: ToJson, W: JsonWrite + ?Sized>(
1492    slice: &[T],
1493    w: &mut W,
1494) -> Result<(), W::Error> {
1495    // SAFETY: brackets fit in the reserved space (we accounted for 2
1496    // bracket bytes in the hint). Per-element writes also fit because
1497    // each is bounded by `MAX_SERIALIZED_LEN + 1` (the +1 covers the
1498    // comma).
1499    unsafe {
1500        w.write_byte_unchecked(b'[')?;
1501    }
1502    if let Some((first, rest)) = slice.split_first() {
1503        // SAFETY: `MAX_SERIALIZED_LEN` was included in the caller's
1504        // `reserve_hint`, so each element write fits in the reserved
1505        // tail.
1506        unsafe { first.write_json_in_reserved(w) }?;
1507        for v in rest {
1508            // SAFETY: see function-level safety contract.
1509            unsafe { w.write_byte_unchecked(b',') }?;
1510            unsafe { v.write_json_in_reserved(w) }?;
1511        }
1512    }
1513    // SAFETY: same as the opening bracket.
1514    unsafe { w.write_byte_unchecked(b']') }
1515}
1516
1517impl<T: ToJson> ToJson for [T] {
1518    const MIN_SERIALIZED_LEN: usize = 2; // "[]"
1519    #[inline]
1520    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1521        // Pre-reserve when the element type has a known upper bound. The
1522        // hint covers brackets (`[`, `]`), commas (1 per element), and
1523        // the per-element max payload. After this, *every* byte the
1524        // primitive path writes fits in the reserved tail.
1525        //
1526        // The float-error surfacing below only matters under `alloc` — both
1527        // `NEEDS_VALIDATION` and the taint flag are only ever set true by
1528        // float impls, which live in the alloc-gated module. In `no_std`
1529        // builds the branches are statically unreachable; gating them also
1530        // keeps the `write_float_f64` reference (alloc-only method) out of
1531        // the no_std build.
1532        if T::MAX_SERIALIZED_LEN != 0 {
1533            #[cfg(feature = "alloc")]
1534            {
1535                // Optional pre-scan validation (kept for types that prefer
1536                // a separate validation pass; floats now use per-element
1537                // taint via `write_float_f64_taint` instead).
1538                if T::NEEDS_VALIDATION && T::pre_validate_slice(self).is_err() {
1539                    return w.write_float_f64(f64::NAN);
1540                }
1541            }
1542            let hint = self
1543                .len()
1544                .saturating_mul(T::MAX_SERIALIZED_LEN.saturating_add(1))
1545                .saturating_add(2);
1546            w.reserve_hint(hint);
1547            // SAFETY: hint above covers brackets + per-element max +
1548            // commas. The sink's `write_byte_unchecked` skips the
1549            // capacity check.
1550            #[allow(unsafe_code)]
1551            let res = unsafe { write_array_reserved(self, w) };
1552            #[cfg(feature = "alloc")]
1553            {
1554                // Flush any per-element float taint. Non-zero means at least
1555                // one input was non-finite during the loop; surface it as
1556                // a typed error through the sink's natural channel.
1557                let taint = w.take_nonfinite_taint();
1558                if taint != 0 {
1559                    return w.write_float_f64(f64::NAN);
1560                }
1561            }
1562            return res;
1563        }
1564        write_array(self.iter(), w)
1565    }
1566}
1567
1568impl<T: ToJson, const N: usize> ToJson for [T; N] {
1569    const MIN_SERIALIZED_LEN: usize = 2; // "[]"
1570    #[inline]
1571    fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1572        (self as &[T]).write_json(w)
1573    }
1574}
1575
1576// Tuples serialize as fixed-length heterogeneous arrays. Mirrors the
1577// FromJson side which accepts `[T, U, V]` for `(T, U, V)`.
1578//
1579// The macro takes the *first* element separately from the rest so the
1580// comma placement is unambiguous: emit the first, then for each rest
1581// element emit `,` followed by it. No trailing comma, no double-walk.
1582macro_rules! impl_tuple_to_json {
1583    ($first_idx:tt: $First:ident $(, $idx:tt: $T:ident)* $(,)?) => {
1584        impl<$First: ToJson $(, $T: ToJson)*> ToJson for ($First, $($T,)*) {
1585            fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1586                w.write_byte(b'[')?;
1587                self.$first_idx.write_json(w)?;
1588                $(
1589                    w.write_byte(b',')?;
1590                    self.$idx.write_json(w)?;
1591                )*
1592                w.write_byte(b']')
1593            }
1594        }
1595    };
1596}
1597
1598impl_tuple_to_json!(0: A);
1599impl_tuple_to_json!(0: A, 1: B);
1600impl_tuple_to_json!(0: A, 1: B, 2: C);
1601impl_tuple_to_json!(0: A, 1: B, 2: C, 3: D);
1602impl_tuple_to_json!(0: A, 1: B, 2: C, 3: D, 4: E);
1603impl_tuple_to_json!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
1604
1605// ---------------------------------------------------------------------------
1606// alloc-gated impls
1607// ---------------------------------------------------------------------------
1608
1609#[cfg(feature = "alloc")]
1610mod alloc_impls {
1611    use super::{Error, JsonWrite, ToJson};
1612    use alloc::borrow::Cow;
1613    use alloc::boxed::Box;
1614    use alloc::rc::Rc;
1615    use alloc::string::String;
1616    use alloc::sync::Arc;
1617
1618    impl ToJson for String {
1619        const MIN_SERIALIZED_LEN: usize = 2; // "\"\""
1620        #[inline]
1621        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1622            w.write_escaped_str(self.as_str())
1623        }
1624    }
1625
1626    impl ToJson for Cow<'_, str> {
1627        const MIN_SERIALIZED_LEN: usize = 2; // "\"\""
1628        #[inline]
1629        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1630            w.write_escaped_str(self.as_ref())
1631        }
1632    }
1633
1634    impl ToJson for f64 {
1635        const MIN_SERIALIZED_LEN: usize = 1;
1636        // Worst-case f64 string: sign + 17 digits + '.' + 'e' + sign +
1637        // 3-digit exponent = 25 bytes. Round to 32 to match the
1638        // formatter's stack-buffer size.
1639        const MAX_SERIALIZED_LEN: usize = 32;
1640        // Validation is folded into the per-element `write_float_f64_taint`
1641        // path: each call OR-accumulates a taint bit into the sink, and
1642        // the slice writer queries it once at the end. Skips the
1643        // separate pre-scan pass (which was ~20% of total cycles).
1644        const NEEDS_VALIDATION: bool = false;
1645        #[inline]
1646        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1647            w.write_float_f64(*self)
1648        }
1649        /// SAFETY: caller (`write_array_reserved`) has reserved
1650        /// `MAX_SERIALIZED_LEN` bytes via `reserve_hint`. Non-finite
1651        /// inputs do not break safety — they are routed through the
1652        /// sink's taint-tracking float write, which substitutes a
1653        /// finite placeholder before calling teju and accumulates a
1654        /// taint bit. The slice writer queries the sink's taint after
1655        /// the loop and converts it to an error.
1656        #[inline]
1657        #[allow(unsafe_code)]
1658        unsafe fn write_json_in_reserved<W: JsonWrite + ?Sized>(
1659            &self,
1660            w: &mut W,
1661        ) -> Result<(), W::Error> {
1662            // SAFETY: forwarded to the sink's taint-tracking float path.
1663            unsafe { w.write_float_f64_taint(*self) }
1664        }
1665        /// Validation is deferred into the per-element taint write —
1666        /// the pre-scan pass was costing ~20% of total cycles per
1667        /// `perf record -c cycles` (the SIMD pand/pcmpeqd loop). The
1668        /// merged taint path costs only ~3 extra instructions per
1669        /// element (bit-mask check + cmov substitute + OR accumulate)
1670        /// and removes the separate pass entirely.
1671        #[inline]
1672        fn pre_validate_slice(_slice: &[Self]) -> Result<(), Error> {
1673            Ok(())
1674        }
1675    }
1676
1677    /// `f32` widens losslessly to `f64` for serialization. The decoded
1678    /// form on the parse side narrows via `as f32`, mirroring this.
1679    impl ToJson for f32 {
1680        const MIN_SERIALIZED_LEN: usize = 1;
1681        const MAX_SERIALIZED_LEN: usize = 32;
1682        // The taint path handles validation per-element now — no pre-scan.
1683        const NEEDS_VALIDATION: bool = false;
1684        #[inline]
1685        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1686            w.write_float_f64(f64::from(*self))
1687        }
1688        /// SAFETY: caller has reserved `MAX_SERIALIZED_LEN` bytes via
1689        /// `reserve_hint`. Non-finite inputs are tolerated via the
1690        /// sink's taint accumulator.
1691        #[inline]
1692        #[allow(unsafe_code)]
1693        unsafe fn write_json_in_reserved<W: JsonWrite + ?Sized>(
1694            &self,
1695            w: &mut W,
1696        ) -> Result<(), W::Error> {
1697            // SAFETY: forwarded to the sink's taint-tracking float path.
1698            unsafe { w.write_float_f64_taint(f64::from(*self)) }
1699        }
1700    }
1701
1702    /// JSON has no `char` primitive — encode as a one-character string,
1703    /// matching the `FromJson` direction.
1704    impl ToJson for char {
1705        const MIN_SERIALIZED_LEN: usize = 3; // "\"x\""
1706        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1707            let mut buf = [0u8; 4];
1708            let s: &str = self.encode_utf8(&mut buf);
1709            w.write_escaped_str(s)
1710        }
1711    }
1712
1713    impl<T: ToJson + ?Sized> ToJson for Box<T> {
1714        const MIN_SERIALIZED_LEN: usize = T::MIN_SERIALIZED_LEN;
1715        #[inline]
1716        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1717            (**self).write_json(w)
1718        }
1719    }
1720
1721    impl<T: ToJson + ?Sized> ToJson for Rc<T> {
1722        const MIN_SERIALIZED_LEN: usize = T::MIN_SERIALIZED_LEN;
1723        #[inline]
1724        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1725            (**self).write_json(w)
1726        }
1727    }
1728
1729    impl<T: ToJson + ?Sized> ToJson for Arc<T> {
1730        const MIN_SERIALIZED_LEN: usize = T::MIN_SERIALIZED_LEN;
1731        #[inline]
1732        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1733            (**self).write_json(w)
1734        }
1735    }
1736
1737    impl<T: ToJson> ToJson for alloc::vec::Vec<T> {
1738        const MIN_SERIALIZED_LEN: usize = 2; // "[]"
1739        #[inline]
1740        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1741            // Forward to the slice impl in the parent module.
1742            self.as_slice().write_json(w)
1743        }
1744    }
1745
1746    // -----------------------------------------------------------------
1747    // Map and set collections.
1748    //
1749    // Mirror image of `de.rs`: keys are written via `MapKeyOut`, which
1750    // exposes the canonical `&str` form regardless of whether the
1751    // underlying type is `String`, `&str`, or `Cow<'_, str>`. Values
1752    // serialize via their own `ToJson` impl. Sets serialize as arrays
1753    // in iteration order — for `HashSet` that's hash-bucket order,
1754    // matching the convention serde_json uses.
1755    // -----------------------------------------------------------------
1756
1757    /// Sealed adapter from a map's key type to the borrowed `&str` form.
1758    ///
1759    /// JSON object keys are always strings, so any map serialized via
1760    /// `ToJson` needs its key type to expose a `&str` view. Implemented
1761    /// for `String`, `&str`, and `Cow<'_, str>` — the same set the
1762    /// parse side supports via `MapKey`.
1763    pub trait MapKeyOut {
1764        fn as_str(&self) -> &str;
1765    }
1766
1767    impl MapKeyOut for String {
1768        #[inline]
1769        fn as_str(&self) -> &str {
1770            self
1771        }
1772    }
1773
1774    impl MapKeyOut for &str {
1775        #[inline]
1776        fn as_str(&self) -> &str {
1777            self
1778        }
1779    }
1780
1781    impl MapKeyOut for Cow<'_, str> {
1782        #[inline]
1783        fn as_str(&self) -> &str {
1784            self.as_ref()
1785        }
1786    }
1787
1788    /// Shared object-writing helper. Mirrors `write_array` for arrays.
1789    /// Pulled out so `BTreeMap` and `HashMap` (and any future map type)
1790    /// cannot drift on punctuation or empty-object handling.
1791    fn write_object<'a, K, V, I, W>(iter: I, w: &mut W) -> Result<(), W::Error>
1792    where
1793        K: MapKeyOut + 'a,
1794        V: ToJson + 'a,
1795        I: IntoIterator<Item = (&'a K, &'a V)>,
1796        W: JsonWrite + ?Sized,
1797    {
1798        w.write_byte(b'{')?;
1799        let mut first = true;
1800        for (k, v) in iter {
1801            if !first {
1802                w.write_byte(b',')?;
1803            }
1804            w.write_escaped_str(k.as_str())?;
1805            w.write_byte(b':')?;
1806            v.write_json(w)?;
1807            first = false;
1808        }
1809        w.write_byte(b'}')
1810    }
1811
1812    impl<K: MapKeyOut, V: ToJson> ToJson for alloc::collections::BTreeMap<K, V> {
1813        #[inline]
1814        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1815            write_object(self.iter(), w)
1816        }
1817    }
1818
1819    impl<T: ToJson> ToJson for alloc::collections::BTreeSet<T> {
1820        #[inline]
1821        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1822            super::write_array(self.iter(), w)
1823        }
1824    }
1825
1826    #[cfg(feature = "std")]
1827    impl<K, V, S> ToJson for std::collections::HashMap<K, V, S>
1828    where
1829        K: MapKeyOut + ::core::hash::Hash + Eq,
1830        V: ToJson,
1831        S: ::core::hash::BuildHasher,
1832    {
1833        #[inline]
1834        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1835            write_object(self.iter(), w)
1836        }
1837    }
1838
1839    #[cfg(feature = "std")]
1840    impl<T, S> ToJson for std::collections::HashSet<T, S>
1841    where
1842        T: ToJson + ::core::hash::Hash + Eq,
1843        S: ::core::hash::BuildHasher,
1844    {
1845        #[inline]
1846        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1847            super::write_array(self.iter(), w)
1848        }
1849    }
1850
1851    /// Encode `SystemTime` as fractional seconds since `UNIX_EPOCH`.
1852    /// Times before the epoch serialize as negative numbers; the
1853    /// parse side accepts the same shape.
1854    #[cfg(feature = "std")]
1855    impl ToJson for std::time::SystemTime {
1856        #[inline]
1857        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1858            // `duration_since(UNIX_EPOCH)` returns Err with the negated
1859            // duration when self < UNIX_EPOCH. Encode the sign back.
1860            let secs = match self.duration_since(std::time::UNIX_EPOCH) {
1861                Ok(d) => d.as_secs_f64(),
1862                Err(e) => -e.duration().as_secs_f64(),
1863            };
1864            w.write_float_f64(secs)
1865        }
1866    }
1867
1868    /// Encode `Duration` as fractional seconds, mirroring the parse-side
1869    /// `from_secs_f64` adapter. Negative durations are unrepresentable
1870    /// (`Duration` is unsigned), and the float impl already rejects
1871    /// non-finite output, so this never errors for valid inputs.
1872    #[cfg(feature = "std")]
1873    impl ToJson for std::time::Duration {
1874        #[inline]
1875        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1876            w.write_float_f64(self.as_secs_f64())
1877        }
1878    }
1879
1880    // -----------------------------------------------------------------
1881    // std::net and std::path adapters.
1882    //
1883    // Each writes its canonical Display form as a quoted JSON string,
1884    // matching the FromJson `parse_from_str` adapter on the parse side.
1885    // -----------------------------------------------------------------
1886
1887    /// Display the value into a small scratch buffer, then write it as
1888    /// a JSON string. The `fmt::Write` trait fills our scratch `String`;
1889    /// from there we reuse `write_escaped_str` even though these types'
1890    /// canonical text never contains characters that need escaping —
1891    /// the cost is one SIMD scan that exits immediately, and using the
1892    /// escape path keeps a single string-writing entry point.
1893    #[cfg(feature = "std")]
1894    fn write_display<T: ::core::fmt::Display, W: JsonWrite + ?Sized>(
1895        v: &T,
1896        w: &mut W,
1897    ) -> Result<(), W::Error> {
1898        use ::core::fmt::Write as _;
1899        let mut buf = String::new();
1900        // fmt::Write into a String is infallible.
1901        let _ = write!(&mut buf, "{v}");
1902        w.write_escaped_str(&buf)
1903    }
1904
1905    #[cfg(feature = "std")]
1906    impl ToJson for std::net::IpAddr {
1907        #[inline]
1908        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1909            write_display(self, w)
1910        }
1911    }
1912
1913    #[cfg(feature = "std")]
1914    impl ToJson for std::net::Ipv4Addr {
1915        #[inline]
1916        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1917            write_display(self, w)
1918        }
1919    }
1920
1921    #[cfg(feature = "std")]
1922    impl ToJson for std::net::Ipv6Addr {
1923        #[inline]
1924        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1925            write_display(self, w)
1926        }
1927    }
1928
1929    #[cfg(feature = "std")]
1930    impl ToJson for std::net::SocketAddr {
1931        #[inline]
1932        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1933            write_display(self, w)
1934        }
1935    }
1936
1937    /// `PathBuf` round-trips only for paths whose bytes are valid UTF-8.
1938    /// `Path::display()` lossily replaces invalid bytes — we want a
1939    /// clean error in that case, but JSON has no lossless path encoding
1940    /// anyway, so the convention matches the parse side: assume UTF-8.
1941    /// `to_string_lossy` here is the symmetric move.
1942    #[cfg(feature = "std")]
1943    impl ToJson for std::path::PathBuf {
1944        #[inline]
1945        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1946            w.write_escaped_str(&self.to_string_lossy())
1947        }
1948    }
1949
1950    #[cfg(feature = "std")]
1951    impl ToJson for std::path::Path {
1952        #[inline]
1953        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1954            w.write_escaped_str(&self.to_string_lossy())
1955        }
1956    }
1957
1958    // -----------------------------------------------------------------
1959    // IndexMap / IndexSet (optional `indexmap` feature).
1960    //
1961    // Insertion-order iteration is the differentiator from
1962    // HashMap/BTreeMap; the wire shape is the same.
1963    // -----------------------------------------------------------------
1964
1965    #[cfg(feature = "indexmap")]
1966    impl<K, V, S> ToJson for indexmap::IndexMap<K, V, S>
1967    where
1968        K: super::MapKeyOut + ::core::hash::Hash + Eq,
1969        V: ToJson,
1970        S: ::core::hash::BuildHasher,
1971    {
1972        #[inline]
1973        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1974            write_object(self.iter(), w)
1975        }
1976    }
1977
1978    #[cfg(feature = "indexmap")]
1979    impl<T, S> ToJson for indexmap::IndexSet<T, S>
1980    where
1981        T: ToJson + ::core::hash::Hash + Eq,
1982        S: ::core::hash::BuildHasher,
1983    {
1984        #[inline]
1985        fn write_json<W: JsonWrite + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
1986            super::write_array(self.iter(), w)
1987        }
1988    }
1989}
1990
1991#[cfg(feature = "alloc")]
1992pub use alloc_impls::MapKeyOut;
1993
1994// Keep `Position` / `ErrorKind` imports live so PR 2's float path can lean
1995// on them without re-importing. The float impls return
1996// `Error::new(ErrorKind::NonFiniteFloat, Position::START)` since serializer
1997// errors don't have an input-byte position to point at.
1998#[allow(dead_code)]
1999const _: fn() -> Error = || Error::new(ErrorKind::NonFiniteFloat, Position::START);