1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
base / json / string_escape.cc [blame]
// Copyright 2006-2008 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/json/string_escape.h"
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <string>
#include <string_view>
#include "base/check_op.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/strings/utf_string_conversions.h"
#include "base/third_party/icu/icu_utf.h"
namespace base {
namespace {
// Format string for printing a \uXXXX escape sequence.
const char kU16EscapeFormat[] = "\\u%04X";
// The code point to output for an invalid input code unit.
const base_icu::UChar32 kReplacementCodePoint = 0xFFFD;
// Used below in EscapeSpecialCodePoint().
static_assert('<' == 0x3C, "less than sign must be 0x3c");
// Try to escape the |code_point| if it is a known special character. If
// successful, returns true and appends the escape sequence to |dest|. This
// isn't required by the spec, but it's more readable by humans.
bool EscapeSpecialCodePoint(base_icu::UChar32 code_point, std::string* dest) {
// WARNING: if you add a new case here, you need to update the reader as well.
// Note: \v is in the reader, but not here since the JSON spec doesn't
// allow it.
switch (code_point) {
case '\b':
dest->append("\\b");
break;
case '\f':
dest->append("\\f");
break;
case '\n':
dest->append("\\n");
break;
case '\r':
dest->append("\\r");
break;
case '\t':
dest->append("\\t");
break;
case '\\':
dest->append("\\\\");
break;
case '"':
dest->append("\\\"");
break;
// Escape < to prevent script execution; escaping > is not necessary and
// not doing so save a few bytes.
case '<':
dest->append("\\u003C");
break;
// Escape the "Line Separator" and "Paragraph Separator" characters, since
// they should be treated like a new line \r or \n.
case 0x2028:
dest->append("\\u2028");
break;
case 0x2029:
dest->append("\\u2029");
break;
default:
return false;
}
return true;
}
template <typename S>
bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) {
bool did_replacement = false;
if (put_in_quotes)
dest->push_back('"');
const size_t length = str.length();
for (size_t i = 0; i < length; ++i) {
base_icu::UChar32 code_point;
if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
code_point == CBU_SENTINEL) {
code_point = kReplacementCodePoint;
did_replacement = true;
}
if (EscapeSpecialCodePoint(code_point, dest))
continue;
// Escape non-printing characters.
if (code_point < 32)
base::StringAppendF(dest, kU16EscapeFormat, code_point);
else
WriteUnicodeCharacter(code_point, dest);
}
if (put_in_quotes)
dest->push_back('"');
return !did_replacement;
}
} // namespace
bool EscapeJSONString(std::string_view str,
bool put_in_quotes,
std::string* dest) {
return EscapeJSONStringImpl(str, put_in_quotes, dest);
}
bool EscapeJSONString(std::u16string_view str,
bool put_in_quotes,
std::string* dest) {
return EscapeJSONStringImpl(str, put_in_quotes, dest);
}
std::string GetQuotedJSONString(std::string_view str) {
std::string dest;
EscapeJSONStringImpl(str, true, &dest);
return dest;
}
std::string GetQuotedJSONString(std::u16string_view str) {
std::string dest;
EscapeJSONStringImpl(str, true, &dest);
return dest;
}
std::string EscapeBytesAsInvalidJSONString(std::string_view str,
bool put_in_quotes) {
std::string dest;
if (put_in_quotes)
dest.push_back('"');
for (char c : str) {
if (EscapeSpecialCodePoint(c, &dest))
continue;
if (c < 32 || c > 126) {
base::StringAppendF(&dest, kU16EscapeFormat,
static_cast<unsigned char>(c));
} else {
dest.push_back(c);
}
}
if (put_in_quotes)
dest.push_back('"');
return dest;
}
} // namespace base