nop_json/
escape.rs

1use std::borrow::Cow;
2
3const HEX_DIGITS: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F'];
4
5/// Adds slashes before `"` and `\` characters, converts `\t`, `\r`, `\n`, `\b`, `\f` characters as needed,
6/// and encodes characters with codes less than space (32) with `\u00XX`.
7/// If input string doesn't contain something that JSON standard wants us to escape, it just returns the input string
8/// without memory allocation.
9///
10/// # Examples
11///
12/// ```
13/// let orig = "Some \"quote\" and some \\.";
14/// let json_encoded = format!("{{\"value\": \"{}\"}}", nop_json::escape(orig));
15/// assert_eq!(json_encoded, "{\"value\": \"Some \\\"quote\\\" and some \\\\.\"}");
16/// ```
17pub fn escape(s: &str) -> Cow<str>
18{	let bytes = s.as_bytes();
19	if let Some(pos) = bytes.iter().position(|c| match *c {b'"' | b'\\' | 0..=31 => true, _ => false})
20	{	Cow::Owned(String::from_utf8(do_escape_bytes(bytes, pos)).unwrap())
21	}
22	else
23	{	Cow::Borrowed(s)
24	}
25}
26
27/// Like [escape](fn.escape.html), but for `&[u8]`.
28pub fn escape_bytes(bytes: &[u8]) -> Cow<[u8]>
29{	if let Some(pos) = bytes.iter().position(|c| match *c {b'"' | b'\\' | 0..=31 => true, _ => false})
30	{	Cow::Owned(do_escape_bytes(bytes, pos))
31	}
32	else
33	{	Cow::Borrowed(bytes)
34	}
35}
36
37fn do_escape_bytes(bytes: &[u8], mut pos: usize) -> Vec<u8>
38{	let mut buffer = Vec::with_capacity(bytes.len() + 8);
39	let mut from = 0;
40	loop
41	{	buffer.extend_from_slice(&bytes[from .. pos]);
42		let c = bytes[pos];
43		if c >= 32
44		{	buffer.push(b'\\');
45			buffer.push(c);
46		}
47		else
48		{	match c
49			{	9 =>
50				{	buffer.push(b'\\');
51					buffer.push(b't');
52				}
53				13 =>
54				{	buffer.push(b'\\');
55					buffer.push(b'r');
56				}
57				10 =>
58				{	buffer.push(b'\\');
59					buffer.push(b'n');
60				}
61				8 =>
62				{	buffer.push(b'\\');
63					buffer.push(b'b');
64				}
65				12 =>
66				{	buffer.push(b'\\');
67					buffer.push(b'f');
68				}
69				_ =>
70				{	buffer.push(b'\\');
71					buffer.push(b'u');
72					buffer.push(b'0');
73					buffer.push(b'0');
74					buffer.push(HEX_DIGITS[(c >> 4) as usize]);
75					buffer.push(HEX_DIGITS[(c & 0xF) as usize]);
76				}
77			}
78		}
79		from = pos + 1;
80		if let Some(new_pos) = &bytes[from ..].iter().position(|c| match *c {b'"' | b'\\' | 0..=31 => true, _ => false})
81		{	pos = from + *new_pos;
82		}
83		else
84		{	buffer.extend_from_slice(&bytes[from .. ]);
85			break;
86		}
87	}
88	buffer
89}