1use std::convert::Infallible;
2use std::ops::Deref;
3use std::pin::Pin;
4use std::{fmt, io, str};
5
6use serde::Serialize;
7use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer};
8
9use super::FastWritable;
10use crate::ascii_str::{AsciiChar, AsciiStr};
11
12#[inline]
47pub fn json(value: impl Serialize) -> Result<impl fmt::Display, Infallible> {
48 Ok(ToJson { value })
49}
50
51#[inline]
86pub fn json_pretty(
87 value: impl Serialize,
88 indent: impl AsIndent,
89) -> Result<impl fmt::Display, Infallible> {
90 Ok(ToJsonPretty { value, indent })
91}
92
93#[derive(Debug, Clone)]
94struct ToJson<S> {
95 value: S,
96}
97
98#[derive(Debug, Clone)]
99struct ToJsonPretty<S, I> {
100 value: S,
101 indent: I,
102}
103
104pub trait AsIndent {
112 fn as_indent(&self) -> &str;
114}
115
116impl AsIndent for str {
117 #[inline]
118 fn as_indent(&self) -> &str {
119 self
120 }
121}
122
123#[cfg(feature = "alloc")]
124impl AsIndent for alloc::string::String {
125 #[inline]
126 fn as_indent(&self) -> &str {
127 self
128 }
129}
130
131impl AsIndent for usize {
132 #[inline]
133 fn as_indent(&self) -> &str {
134 spaces(*self)
135 }
136}
137
138impl AsIndent for std::num::Wrapping<usize> {
139 #[inline]
140 fn as_indent(&self) -> &str {
141 spaces(self.0)
142 }
143}
144
145impl AsIndent for std::num::NonZeroUsize {
146 #[inline]
147 fn as_indent(&self) -> &str {
148 spaces(self.get())
149 }
150}
151
152fn spaces(width: usize) -> &'static str {
153 const MAX_SPACES: usize = 16;
154 const SPACES: &str = match str::from_utf8(&[b' '; MAX_SPACES]) {
155 Ok(spaces) => spaces,
156 Err(_) => panic!(),
157 };
158
159 &SPACES[..width.min(SPACES.len())]
160}
161
162#[cfg(feature = "alloc")]
163impl<T: AsIndent + alloc::borrow::ToOwned + ?Sized> AsIndent for alloc::borrow::Cow<'_, T> {
164 #[inline]
165 fn as_indent(&self) -> &str {
166 T::as_indent(self)
167 }
168}
169
170crate::impl_for_ref! {
171 impl AsIndent for T {
172 #[inline]
173 fn as_indent(&self) -> &str {
174 <T>::as_indent(self)
175 }
176 }
177}
178
179impl<T> AsIndent for Pin<T>
180where
181 T: Deref,
182 <T as Deref>::Target: AsIndent,
183{
184 #[inline]
185 fn as_indent(&self) -> &str {
186 self.as_ref().get_ref().as_indent()
187 }
188}
189
190impl<S: Serialize> FastWritable for ToJson<S> {
191 fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
192 serialize(f, &self.value, CompactFormatter)
193 }
194}
195
196impl<S: Serialize> fmt::Display for ToJson<S> {
197 #[inline]
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 Ok(self.write_into(f)?)
200 }
201}
202
203impl<S: Serialize, I: AsIndent> FastWritable for ToJsonPretty<S, I> {
204 fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
205 serialize(
206 f,
207 &self.value,
208 PrettyFormatter::with_indent(self.indent.as_indent().as_bytes()),
209 )
210 }
211}
212
213impl<S: Serialize, I: AsIndent> fmt::Display for ToJsonPretty<S, I> {
214 #[inline]
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 Ok(self.write_into(f)?)
217 }
218}
219
220#[inline]
221fn serialize<S, W, F>(dest: &mut W, value: &S, formatter: F) -> Result<(), crate::Error>
222where
223 S: Serialize + ?Sized,
224 W: fmt::Write + ?Sized,
225 F: serde_json::ser::Formatter,
226{
227 struct JsonWriter<'a, W: fmt::Write + ?Sized>(&'a mut W);
231
232 impl<W: fmt::Write + ?Sized> io::Write for JsonWriter<'_, W> {
233 #[inline]
235 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
236 self.write_all(bytes)?;
237 Ok(bytes.len())
238 }
239
240 fn write_all(&mut self, bytes: &[u8]) -> io::Result<()> {
242 let string = unsafe { std::str::from_utf8_unchecked(bytes) };
244 write_escaped_str(&mut *self.0, string)
245 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
246 }
247
248 #[inline]
249 fn flush(&mut self) -> io::Result<()> {
250 Ok(())
251 }
252 }
253
254 #[inline]
257 fn write_escaped_str(dest: &mut (impl fmt::Write + ?Sized), src: &str) -> fmt::Result {
258 let mut escaped_buf = ESCAPED_BUF_INIT;
262 let mut last = 0;
263
264 for (index, byte) in src.bytes().enumerate() {
265 if let Some(escaped) = get_escaped(byte) {
266 [escaped_buf[4], escaped_buf[5]] = escaped;
267 write_str_if_nonempty(dest, &src[last..index])?;
268 dest.write_str(AsciiStr::from_slice(&escaped_buf[..ESCAPED_BUF_LEN]))?;
269 last = index + 1;
270 }
271 }
272 write_str_if_nonempty(dest, &src[last..])
273 }
274
275 let mut serializer = Serializer::with_formatter(JsonWriter(dest), formatter);
276 Ok(value.serialize(&mut serializer)?)
277}
278
279#[inline]
281fn get_escaped(byte: u8) -> Option<[AsciiChar; 2]> {
282 const _: () = assert!(CHAR_RANGE < 32);
283
284 if let MIN_CHAR..=MAX_CHAR = byte {
285 if (1u32 << (byte - MIN_CHAR)) & BITS != 0 {
286 return Some(TABLE.0[(byte - MIN_CHAR) as usize]);
287 }
288 }
289 None
290}
291
292#[inline(always)]
293fn write_str_if_nonempty(output: &mut (impl fmt::Write + ?Sized), input: &str) -> fmt::Result {
294 if !input.is_empty() {
295 output.write_str(input)
296 } else {
297 Ok(())
298 }
299}
300
301const CHARS: &[u8] = br#"&'<>"#;
303
304const MIN_CHAR: u8 = {
306 let mut v = u8::MAX;
307 let mut i = 0;
308 while i < CHARS.len() {
309 if v > CHARS[i] {
310 v = CHARS[i];
311 }
312 i += 1;
313 }
314 v
315};
316
317const MAX_CHAR: u8 = {
319 let mut v = u8::MIN;
320 let mut i = 0;
321 while i < CHARS.len() {
322 if v < CHARS[i] {
323 v = CHARS[i];
324 }
325 i += 1;
326 }
327 v
328};
329
330const BITS: u32 = {
331 let mut bits = 0;
332 let mut i = 0;
333 while i < CHARS.len() {
334 bits |= 1 << (CHARS[i] - MIN_CHAR);
335 i += 1;
336 }
337 bits
338};
339
340const CHAR_RANGE: usize = (MAX_CHAR - MIN_CHAR + 1) as usize;
342
343#[repr(align(64))]
344struct Table([[AsciiChar; 2]; CHAR_RANGE]);
345
346const TABLE: &Table = &{
349 let mut table = Table([UNESCAPED; CHAR_RANGE]);
350 let mut i = 0;
351 while i < CHARS.len() {
352 let c = CHARS[i];
353 table.0[c as u32 as usize - MIN_CHAR as usize] = AsciiChar::two_hex_digits(c as u32);
354 i += 1;
355 }
356 table
357};
358
359const UNESCAPED: [AsciiChar; 2] = AsciiStr::new_sized("");
360
361const ESCAPED_BUF_INIT_UNPADDED: &str = "\\u00__";
362const ESCAPED_BUF_INIT: [AsciiChar; 8] = AsciiStr::new_sized(ESCAPED_BUF_INIT_UNPADDED);
364const ESCAPED_BUF_LEN: usize = ESCAPED_BUF_INIT_UNPADDED.len();
365
366#[cfg(all(test, feature = "alloc"))]
367mod tests {
368 use alloc::string::ToString;
369 use alloc::vec;
370
371 use super::*;
372
373 #[test]
374 fn test_ugly() {
375 assert_eq!(json(true).unwrap().to_string(), "true");
376 assert_eq!(json("foo").unwrap().to_string(), r#""foo""#);
377 assert_eq!(json(true).unwrap().to_string(), "true");
378 assert_eq!(json("foo").unwrap().to_string(), r#""foo""#);
379 assert_eq!(
380 json("<script>").unwrap().to_string(),
381 r#""\u003cscript\u003e""#
382 );
383 assert_eq!(
384 json(vec!["foo", "bar"]).unwrap().to_string(),
385 r#"["foo","bar"]"#
386 );
387 }
388
389 #[test]
390 fn test_pretty() {
391 assert_eq!(json_pretty(true, "").unwrap().to_string(), "true");
392 assert_eq!(
393 json_pretty("<script>", "").unwrap().to_string(),
394 r#""\u003cscript\u003e""#
395 );
396 assert_eq!(
397 json_pretty(vec!["foo", "bar"], "").unwrap().to_string(),
398 r#"[
399"foo",
400"bar"
401]"#
402 );
403 assert_eq!(
404 json_pretty(vec!["foo", "bar"], 2).unwrap().to_string(),
405 r#"[
406 "foo",
407 "bar"
408]"#
409 );
410 assert_eq!(
411 json_pretty(vec!["foo", "bar"], "————").unwrap().to_string(),
412 r#"[
413————"foo",
414————"bar"
415]"#
416 );
417 }
418}