react-native-windows 0.74.39 → 0.74.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Directory.Build.props +2 -2
- package/Folly/TEMP_UntilFollyUpdate/json.cpp +1114 -0
- package/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.cpp +23 -15
- package/Folly/TEMP_UntilFollyUpdate/lang/ToAscii.h +5 -5
- package/Folly/cgmanifest.json +1 -1
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/Shared/Networking/WinRTWebSocketResource.cpp +5 -0
- package/package.json +1 -1
package/Directory.Build.props
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
-->
|
|
17
17
|
<EnableSourceLink Condition="'$(EnableSourceLink)' == ''">false</EnableSourceLink>
|
|
18
18
|
<!-- When bumping the Folly version, be sure to bump the git hash of that version's commit and build Folly.vcxproj (to update its cgmanifest.json) too. -->
|
|
19
|
-
<FollyVersion>
|
|
20
|
-
<FollyCommitHash>
|
|
19
|
+
<FollyVersion>2024.01.01.00</FollyVersion>
|
|
20
|
+
<FollyCommitHash>234d39a36a43106747d10cc19efada72fd810dd3</FollyCommitHash>
|
|
21
21
|
<!-- When bumping the fmt version, be sure to bump the git hash of that version's commit and build fmt.vcxproj (to update its cgmanifest.json) too. -->
|
|
22
22
|
<FmtVersion>10.1.0</FmtVersion>
|
|
23
23
|
<FmtCommitHash>ca2e3685b160617d3d95fcd9e789c4e06ca88</FmtCommitHash>
|
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include <folly/json.h>
|
|
18
|
+
|
|
19
|
+
#include <algorithm>
|
|
20
|
+
#include <functional>
|
|
21
|
+
#include <iterator>
|
|
22
|
+
#include <sstream>
|
|
23
|
+
#include <type_traits>
|
|
24
|
+
|
|
25
|
+
#include <boost/algorithm/string.hpp>
|
|
26
|
+
#include <glog/logging.h>
|
|
27
|
+
|
|
28
|
+
#include <folly/Conv.h>
|
|
29
|
+
#include <folly/Portability.h>
|
|
30
|
+
#include <folly/Range.h>
|
|
31
|
+
#include <folly/String.h>
|
|
32
|
+
#include <folly/Unicode.h>
|
|
33
|
+
#include <folly/Utility.h>
|
|
34
|
+
#include <folly/lang/Bits.h>
|
|
35
|
+
#include <folly/portability/Constexpr.h>
|
|
36
|
+
|
|
37
|
+
namespace folly {
|
|
38
|
+
|
|
39
|
+
//////////////////////////////////////////////////////////////////////
|
|
40
|
+
|
|
41
|
+
namespace json {
|
|
42
|
+
|
|
43
|
+
namespace {
|
|
44
|
+
|
|
45
|
+
parse_error make_parse_error(
|
|
46
|
+
unsigned int line,
|
|
47
|
+
std::string const& context,
|
|
48
|
+
std::string const& expected) {
|
|
49
|
+
return parse_error(to<std::string>(
|
|
50
|
+
"json parse error on line ",
|
|
51
|
+
line,
|
|
52
|
+
!context.empty() ? to<std::string>(" near `", context, '\'') : "",
|
|
53
|
+
": ",
|
|
54
|
+
expected));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
struct Printer {
|
|
58
|
+
// Context class is allows to restore the path to element that we are about to
|
|
59
|
+
// print so that if error happens we can throw meaningful exception.
|
|
60
|
+
class Context {
|
|
61
|
+
public:
|
|
62
|
+
Context(const Context* parent_context, const dynamic& key)
|
|
63
|
+
: parent_context_(parent_context), key_(key), is_key_(false) {}
|
|
64
|
+
Context(const Context* parent_context, const dynamic& key, bool is_key)
|
|
65
|
+
: parent_context_(parent_context), key_(key), is_key_(is_key) {}
|
|
66
|
+
|
|
67
|
+
// Return location description of a context as a chain of keys
|
|
68
|
+
// ex., '"outherKey"->"innerKey"'.
|
|
69
|
+
std::string locationDescription() const {
|
|
70
|
+
std::vector<std::string> keys;
|
|
71
|
+
const Context* ptr = parent_context_;
|
|
72
|
+
while (ptr) {
|
|
73
|
+
keys.push_back(ptr->getName());
|
|
74
|
+
ptr = ptr->parent_context_;
|
|
75
|
+
}
|
|
76
|
+
keys.push_back(getName());
|
|
77
|
+
std::ostringstream stream;
|
|
78
|
+
std::reverse_copy(
|
|
79
|
+
keys.begin(),
|
|
80
|
+
keys.end() - 1,
|
|
81
|
+
std::ostream_iterator<std::string>(stream, "->"));
|
|
82
|
+
|
|
83
|
+
// Add current key.
|
|
84
|
+
stream << keys.back();
|
|
85
|
+
return stream.str();
|
|
86
|
+
}
|
|
87
|
+
std::string getName() const {
|
|
88
|
+
return Printer::toStringOr(key_, "<unprintable>");
|
|
89
|
+
}
|
|
90
|
+
std::string typeDescription() const { return is_key_ ? "key" : "value"; }
|
|
91
|
+
|
|
92
|
+
private:
|
|
93
|
+
const Context* const parent_context_;
|
|
94
|
+
const dynamic& key_;
|
|
95
|
+
bool is_key_;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
explicit Printer(
|
|
99
|
+
std::string& out, unsigned* indentLevel, serialization_opts const* opts)
|
|
100
|
+
: out_(out), indentLevel_(indentLevel), opts_(*opts) {}
|
|
101
|
+
|
|
102
|
+
void operator()(dynamic const& v, const Context& context) const {
|
|
103
|
+
(*this)(v, &context);
|
|
104
|
+
}
|
|
105
|
+
void operator()(dynamic const& v, const Context* context) const {
|
|
106
|
+
switch (v.type()) {
|
|
107
|
+
case dynamic::DOUBLE:
|
|
108
|
+
if (!opts_.allow_nan_inf) {
|
|
109
|
+
if (std::isnan(v.asDouble())) {
|
|
110
|
+
throw json::print_error(
|
|
111
|
+
"folly::toJson: JSON object value was a NaN when serializing " +
|
|
112
|
+
contextDescription(context));
|
|
113
|
+
}
|
|
114
|
+
if (std::isinf(v.asDouble())) {
|
|
115
|
+
throw json::print_error(
|
|
116
|
+
"folly::toJson: JSON object value was an INF when serializing " +
|
|
117
|
+
contextDescription(context));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
toAppend(
|
|
121
|
+
v.asDouble(),
|
|
122
|
+
&out_,
|
|
123
|
+
opts_.double_mode,
|
|
124
|
+
opts_.double_num_digits,
|
|
125
|
+
opts_.double_flags);
|
|
126
|
+
break;
|
|
127
|
+
case dynamic::INT64: {
|
|
128
|
+
auto intval = v.asInt();
|
|
129
|
+
if (opts_.javascript_safe) {
|
|
130
|
+
// Use folly::to to check that this integer can be represented
|
|
131
|
+
// as a double without loss of precision.
|
|
132
|
+
intval = int64_t(to<double>(intval));
|
|
133
|
+
}
|
|
134
|
+
toAppend(intval, &out_);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case dynamic::BOOL:
|
|
138
|
+
out_ += v.asBool() ? "true" : "false";
|
|
139
|
+
break;
|
|
140
|
+
case dynamic::NULLT:
|
|
141
|
+
out_ += "null";
|
|
142
|
+
break;
|
|
143
|
+
case dynamic::STRING:
|
|
144
|
+
escapeString(v.stringPiece(), out_, opts_);
|
|
145
|
+
break;
|
|
146
|
+
case dynamic::OBJECT:
|
|
147
|
+
printObject(v, context);
|
|
148
|
+
break;
|
|
149
|
+
case dynamic::ARRAY:
|
|
150
|
+
printArray(v, context);
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
CHECK(0) << "Bad type " << v.type();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private:
|
|
158
|
+
void printKV(
|
|
159
|
+
dynamic const& o,
|
|
160
|
+
const std::pair<const dynamic, dynamic>& p,
|
|
161
|
+
const Context* context) const {
|
|
162
|
+
if (opts_.convert_int_keys && p.first.isInt()) {
|
|
163
|
+
auto strKey = p.first.asString();
|
|
164
|
+
if (o.count(strKey)) {
|
|
165
|
+
throw json::print_error(folly::to<std::string>(
|
|
166
|
+
"folly::toJson: Source object has integer and string keys "
|
|
167
|
+
"representing the same value: ",
|
|
168
|
+
p.first.asInt()));
|
|
169
|
+
}
|
|
170
|
+
(*this)(p.first.asString(), Context(context, p.first, true));
|
|
171
|
+
} else if (!opts_.allow_non_string_keys && !p.first.isString()) {
|
|
172
|
+
throw json::print_error(
|
|
173
|
+
"folly::toJson: JSON object key " +
|
|
174
|
+
toStringOr(p.first, "<unprintable key>") + " was not a string " +
|
|
175
|
+
(opts_.convert_int_keys ? "or integer " : "") +
|
|
176
|
+
"when serializing key at " +
|
|
177
|
+
Context(context, p.first, true).locationDescription());
|
|
178
|
+
} else {
|
|
179
|
+
(*this)(p.first, Context(context, p.first, true)); // Key
|
|
180
|
+
}
|
|
181
|
+
mapColon();
|
|
182
|
+
(*this)(p.second, Context(context, p.first, false)); // Value
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
template <typename Iterator>
|
|
186
|
+
void printKVPairs(
|
|
187
|
+
dynamic const& o,
|
|
188
|
+
Iterator begin,
|
|
189
|
+
Iterator end,
|
|
190
|
+
const Context* context) const {
|
|
191
|
+
printKV(o, *begin, context);
|
|
192
|
+
for (++begin; begin != end; ++begin) {
|
|
193
|
+
out_ += ',';
|
|
194
|
+
newline();
|
|
195
|
+
printKV(o, *begin, context);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
void printObject(dynamic const& o, const Context* context) const {
|
|
200
|
+
if (o.empty()) {
|
|
201
|
+
out_ += "{}";
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
out_ += '{';
|
|
206
|
+
indent();
|
|
207
|
+
newline();
|
|
208
|
+
if (opts_.sort_keys || opts_.sort_keys_by) {
|
|
209
|
+
using ref = std::reference_wrapper<decltype(o.items())::value_type const>;
|
|
210
|
+
auto sort_keys_by = [&](auto begin, auto end, const auto& comp) {
|
|
211
|
+
std::sort(begin, end, [&](ref a, ref b) {
|
|
212
|
+
// Only compare keys. No ordering among identical keys.
|
|
213
|
+
return comp(a.get().first, b.get().first);
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
std::vector<ref> refs(o.items().begin(), o.items().end());
|
|
217
|
+
if (opts_.sort_keys_by) {
|
|
218
|
+
sort_keys_by(refs.begin(), refs.end(), opts_.sort_keys_by);
|
|
219
|
+
} else {
|
|
220
|
+
sort_keys_by(refs.begin(), refs.end(), std::less<>());
|
|
221
|
+
}
|
|
222
|
+
printKVPairs(o, refs.cbegin(), refs.cend(), context);
|
|
223
|
+
} else {
|
|
224
|
+
printKVPairs(o, o.items().begin(), o.items().end(), context);
|
|
225
|
+
}
|
|
226
|
+
outdent();
|
|
227
|
+
newline();
|
|
228
|
+
out_ += '}';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static std::string toStringOr(dynamic const& v, const char* placeholder) {
|
|
232
|
+
try {
|
|
233
|
+
std::string result;
|
|
234
|
+
unsigned indentLevel = 0;
|
|
235
|
+
serialization_opts opts;
|
|
236
|
+
opts.allow_nan_inf = true;
|
|
237
|
+
opts.allow_non_string_keys = true;
|
|
238
|
+
Printer printer(result, &indentLevel, &opts);
|
|
239
|
+
printer(v, nullptr);
|
|
240
|
+
return result;
|
|
241
|
+
} catch (...) {
|
|
242
|
+
return placeholder;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static std::string contextDescription(const Context* context) {
|
|
247
|
+
if (!context) {
|
|
248
|
+
return "<undefined location>";
|
|
249
|
+
}
|
|
250
|
+
return context->typeDescription() + " at " + context->locationDescription();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void printArray(dynamic const& a, const Context* context) const {
|
|
254
|
+
if (a.empty()) {
|
|
255
|
+
out_ += "[]";
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
out_ += '[';
|
|
260
|
+
indent();
|
|
261
|
+
newline();
|
|
262
|
+
(*this)(a[0], Context(context, dynamic(0)));
|
|
263
|
+
for (auto it = std::next(a.begin()); it != a.end(); ++it) {
|
|
264
|
+
out_ += ',';
|
|
265
|
+
newline();
|
|
266
|
+
(*this)(*it, Context(context, dynamic(std::distance(a.begin(), it))));
|
|
267
|
+
}
|
|
268
|
+
outdent();
|
|
269
|
+
newline();
|
|
270
|
+
out_ += ']';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private:
|
|
274
|
+
void outdent() const {
|
|
275
|
+
if (indentLevel_) {
|
|
276
|
+
--*indentLevel_;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
void indent() const {
|
|
281
|
+
if (indentLevel_) {
|
|
282
|
+
++*indentLevel_;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void newline() const {
|
|
287
|
+
if (indentLevel_) {
|
|
288
|
+
auto indent = *indentLevel_ * opts_.pretty_formatting_indent_width;
|
|
289
|
+
out_ += to<std::string>('\n', std::string(indent, ' '));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
void mapColon() const { out_ += indentLevel_ ? ": " : ":"; }
|
|
294
|
+
|
|
295
|
+
private:
|
|
296
|
+
std::string& out_;
|
|
297
|
+
unsigned* const indentLevel_;
|
|
298
|
+
serialization_opts const& opts_;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
//////////////////////////////////////////////////////////////////////
|
|
302
|
+
|
|
303
|
+
// Wraps our input buffer with some helper functions.
|
|
304
|
+
struct Input {
|
|
305
|
+
explicit Input(StringPiece range, json::serialization_opts const* opts)
|
|
306
|
+
: range_(range), opts_(*opts), lineNum_(0) {
|
|
307
|
+
storeCurrent();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
Input(Input const&) = delete;
|
|
311
|
+
Input& operator=(Input const&) = delete;
|
|
312
|
+
|
|
313
|
+
char const* begin() const { return range_.begin(); }
|
|
314
|
+
|
|
315
|
+
unsigned getLineNum() const { return lineNum_; }
|
|
316
|
+
|
|
317
|
+
// Parse ahead for as long as the supplied predicate is satisfied,
|
|
318
|
+
// returning a range of what was skipped.
|
|
319
|
+
template <class Predicate>
|
|
320
|
+
StringPiece skipWhile(const Predicate& p) {
|
|
321
|
+
std::size_t skipped = 0;
|
|
322
|
+
for (; skipped < range_.size(); ++skipped) {
|
|
323
|
+
if (!p(range_[skipped])) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
if (range_[skipped] == '\n') {
|
|
327
|
+
++lineNum_;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
auto ret = range_.subpiece(0, skipped);
|
|
331
|
+
range_.advance(skipped);
|
|
332
|
+
storeCurrent();
|
|
333
|
+
return ret;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
StringPiece skipDigits() {
|
|
337
|
+
return skipWhile([](char c) { return c >= '0' && c <= '9'; });
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
StringPiece skipMinusAndDigits() {
|
|
341
|
+
bool firstChar = true;
|
|
342
|
+
return skipWhile([&firstChar](char c) {
|
|
343
|
+
bool result = (c >= '0' && c <= '9') || (firstChar && c == '-');
|
|
344
|
+
firstChar = false;
|
|
345
|
+
return result;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
void skipWhitespace() {
|
|
350
|
+
// [Windows #12703 - CodeQL patch]
|
|
351
|
+
std::size_t index = 0;
|
|
352
|
+
while (true) {
|
|
353
|
+
while (index < range_.size() && range_[index] == ' ') {
|
|
354
|
+
index++;
|
|
355
|
+
}
|
|
356
|
+
if (index < range_.size()) {
|
|
357
|
+
if (range_[index] == '\n') {
|
|
358
|
+
index++;
|
|
359
|
+
++lineNum_;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (range_[index] == '\t' || range_[index] == '\r') {
|
|
363
|
+
index++;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
range_.advance(index);
|
|
370
|
+
storeCurrent();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
void expect(char c) {
|
|
374
|
+
if (**this != c) {
|
|
375
|
+
throw json::make_parse_error(
|
|
376
|
+
lineNum_, context(), to<std::string>("expected '", c, '\''));
|
|
377
|
+
}
|
|
378
|
+
++*this;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
std::size_t size() const { return range_.size(); }
|
|
382
|
+
|
|
383
|
+
int operator*() const { return current_; }
|
|
384
|
+
|
|
385
|
+
void operator++() {
|
|
386
|
+
range_.pop_front();
|
|
387
|
+
storeCurrent();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
template <class T>
|
|
391
|
+
T extract() {
|
|
392
|
+
try {
|
|
393
|
+
return to<T>(&range_);
|
|
394
|
+
} catch (std::exception const& e) {
|
|
395
|
+
error(e.what());
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
bool consume(StringPiece str) {
|
|
400
|
+
if (boost::starts_with(range_, str)) {
|
|
401
|
+
range_.advance(str.size());
|
|
402
|
+
storeCurrent();
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
std::string context() const {
|
|
409
|
+
return range_.subpiece(0, 16 /* arbitrary */).toString();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
[[noreturn]] dynamic error(char const* what) const {
|
|
413
|
+
throw json::make_parse_error(lineNum_, context(), what);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
json::serialization_opts const& getOpts() { return opts_; }
|
|
417
|
+
|
|
418
|
+
void incrementRecursionLevel() {
|
|
419
|
+
if (currentRecursionLevel_ > opts_.recursion_limit) {
|
|
420
|
+
error("recursion limit exceeded");
|
|
421
|
+
}
|
|
422
|
+
currentRecursionLevel_++;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
void decrementRecursionLevel() { currentRecursionLevel_--; }
|
|
426
|
+
|
|
427
|
+
private:
|
|
428
|
+
void storeCurrent() { current_ = range_.empty() ? EOF : range_.front(); }
|
|
429
|
+
|
|
430
|
+
private:
|
|
431
|
+
StringPiece range_;
|
|
432
|
+
json::serialization_opts const& opts_;
|
|
433
|
+
unsigned lineNum_;
|
|
434
|
+
int current_;
|
|
435
|
+
unsigned int currentRecursionLevel_{0};
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
class RecursionGuard {
|
|
439
|
+
public:
|
|
440
|
+
explicit RecursionGuard(Input& in) : in_(in) {
|
|
441
|
+
in_.incrementRecursionLevel();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
~RecursionGuard() { in_.decrementRecursionLevel(); }
|
|
445
|
+
|
|
446
|
+
private:
|
|
447
|
+
Input& in_;
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
dynamic parseValue(Input& in, json::metadata_map* map);
|
|
451
|
+
std::string parseString(Input& in);
|
|
452
|
+
dynamic parseNumber(Input& in);
|
|
453
|
+
|
|
454
|
+
void parseObjectKeyValue(
|
|
455
|
+
Input& in,
|
|
456
|
+
dynamic& ret,
|
|
457
|
+
dynamic&& key,
|
|
458
|
+
json::metadata_map* map,
|
|
459
|
+
bool distinct) {
|
|
460
|
+
auto keyLineNumber = in.getLineNum();
|
|
461
|
+
in.skipWhitespace();
|
|
462
|
+
in.expect(':');
|
|
463
|
+
in.skipWhitespace();
|
|
464
|
+
auto valueLineNumber = in.getLineNum();
|
|
465
|
+
auto value = parseValue(in, map);
|
|
466
|
+
auto [it, inserted] = ret.try_emplace(std::move(key), std::move(value));
|
|
467
|
+
if (!inserted) {
|
|
468
|
+
if (distinct) {
|
|
469
|
+
in.error("duplicate key inserted");
|
|
470
|
+
}
|
|
471
|
+
it->second = std::move(value);
|
|
472
|
+
}
|
|
473
|
+
if (map) {
|
|
474
|
+
map->emplace(
|
|
475
|
+
&it->second,
|
|
476
|
+
json::parse_metadata{{{keyLineNumber}}, {{valueLineNumber}}});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
dynamic parseObject(Input& in, json::metadata_map* map) {
|
|
481
|
+
DCHECK_EQ(*in, '{');
|
|
482
|
+
++in;
|
|
483
|
+
|
|
484
|
+
dynamic ret = dynamic::object;
|
|
485
|
+
|
|
486
|
+
in.skipWhitespace();
|
|
487
|
+
if (*in == '}') {
|
|
488
|
+
++in;
|
|
489
|
+
return ret;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const auto& opts = in.getOpts();
|
|
493
|
+
const bool distinct = opts.validate_keys || opts.convert_int_keys;
|
|
494
|
+
for (;;) {
|
|
495
|
+
if (opts.allow_trailing_comma && *in == '}') {
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
dynamic key = parseValue(in, map);
|
|
499
|
+
if (opts.convert_int_keys && key.isInt()) {
|
|
500
|
+
key = key.asString();
|
|
501
|
+
} else if (!opts.allow_non_string_keys && !key.isString()) {
|
|
502
|
+
in.error(
|
|
503
|
+
opts.convert_int_keys ? "expected string or integer for object key"
|
|
504
|
+
: "expected string for object key");
|
|
505
|
+
}
|
|
506
|
+
parseObjectKeyValue(in, ret, std::move(key), map, distinct);
|
|
507
|
+
|
|
508
|
+
in.skipWhitespace();
|
|
509
|
+
if (*in != ',') {
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
++in;
|
|
513
|
+
in.skipWhitespace();
|
|
514
|
+
}
|
|
515
|
+
in.expect('}');
|
|
516
|
+
|
|
517
|
+
return ret;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
dynamic parseArray(Input& in, json::metadata_map* map) {
|
|
521
|
+
DCHECK_EQ(*in, '[');
|
|
522
|
+
++in;
|
|
523
|
+
|
|
524
|
+
dynamic ret = dynamic::array;
|
|
525
|
+
|
|
526
|
+
in.skipWhitespace();
|
|
527
|
+
if (*in == ']') {
|
|
528
|
+
++in;
|
|
529
|
+
return ret;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
std::vector<uint32_t> lineNumbers;
|
|
533
|
+
for (;;) {
|
|
534
|
+
if (in.getOpts().allow_trailing_comma && *in == ']') {
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
ret.push_back(parseValue(in, map));
|
|
538
|
+
if (map) {
|
|
539
|
+
lineNumbers.push_back(in.getLineNum());
|
|
540
|
+
}
|
|
541
|
+
in.skipWhitespace();
|
|
542
|
+
if (*in != ',') {
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
++in;
|
|
546
|
+
in.skipWhitespace();
|
|
547
|
+
}
|
|
548
|
+
if (map) {
|
|
549
|
+
for (size_t i = 0, e = ret.size(); i < e; i++) {
|
|
550
|
+
map->emplace(&ret[i], json::parse_metadata{{{0}}, {{lineNumbers[i]}}});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
in.expect(']');
|
|
554
|
+
|
|
555
|
+
return ret;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
dynamic parseNumber(Input& in) {
|
|
559
|
+
bool const negative = (*in == '-');
|
|
560
|
+
if (negative && in.consume("-Infinity")) {
|
|
561
|
+
if (in.getOpts().parse_numbers_as_strings) {
|
|
562
|
+
return "-Infinity";
|
|
563
|
+
} else {
|
|
564
|
+
return -std::numeric_limits<double>::infinity();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
auto integral = in.skipMinusAndDigits();
|
|
569
|
+
if (negative && integral.size() < 2) {
|
|
570
|
+
in.error("expected digits after `-'");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
auto const wasE = *in == 'e' || *in == 'E';
|
|
574
|
+
|
|
575
|
+
if (*in != '.' && !wasE && in.getOpts().parse_numbers_as_strings) {
|
|
576
|
+
return integral;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
constexpr const char* maxIntStr = "9223372036854775807";
|
|
580
|
+
constexpr const char* minIntStr = "-9223372036854775808";
|
|
581
|
+
constexpr auto maxIntLen = constexpr_strlen(maxIntStr);
|
|
582
|
+
constexpr auto minIntLen = constexpr_strlen(minIntStr);
|
|
583
|
+
auto extremaLen = negative ? minIntLen : maxIntLen;
|
|
584
|
+
auto extremaStr = negative ? minIntStr : maxIntStr;
|
|
585
|
+
if (*in != '.' && !wasE) {
|
|
586
|
+
if (FOLLY_LIKELY(
|
|
587
|
+
!in.getOpts().double_fallback || integral.size() < extremaLen) ||
|
|
588
|
+
(integral.size() == extremaLen && integral <= extremaStr)) {
|
|
589
|
+
auto val = to<int64_t>(integral);
|
|
590
|
+
in.skipWhitespace();
|
|
591
|
+
return val;
|
|
592
|
+
} else {
|
|
593
|
+
auto val = to<double>(integral);
|
|
594
|
+
in.skipWhitespace();
|
|
595
|
+
return val;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
auto end = !wasE ? (++in, in.skipDigits().end()) : in.begin();
|
|
600
|
+
if (*in == 'e' || *in == 'E') {
|
|
601
|
+
++in;
|
|
602
|
+
if (*in == '+' || *in == '-') {
|
|
603
|
+
++in;
|
|
604
|
+
}
|
|
605
|
+
auto expPart = in.skipDigits();
|
|
606
|
+
end = expPart.end();
|
|
607
|
+
}
|
|
608
|
+
auto fullNum = range(integral.begin(), end);
|
|
609
|
+
if (in.getOpts().parse_numbers_as_strings) {
|
|
610
|
+
return fullNum;
|
|
611
|
+
}
|
|
612
|
+
auto val = to<double>(fullNum);
|
|
613
|
+
return val;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
void decodeUnicodeEscape(Input& in, std::string& out) {
|
|
617
|
+
auto hexVal = [&](int c) -> uint16_t {
|
|
618
|
+
// clang-format off
|
|
619
|
+
return uint16_t(
|
|
620
|
+
c >= '0' && c <= '9' ? c - '0' :
|
|
621
|
+
c >= 'a' && c <= 'f' ? c - 'a' + 10 :
|
|
622
|
+
c >= 'A' && c <= 'F' ? c - 'A' + 10 :
|
|
623
|
+
(in.error("invalid hex digit"), 0));
|
|
624
|
+
// clang-format on
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
auto readHex = [&]() -> uint16_t {
|
|
628
|
+
if (in.size() < 4) {
|
|
629
|
+
in.error("expected 4 hex digits");
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
auto ret = uint16_t(hexVal(*in) * 4096);
|
|
633
|
+
++in;
|
|
634
|
+
ret += hexVal(*in) * 256;
|
|
635
|
+
++in;
|
|
636
|
+
ret += hexVal(*in) * 16;
|
|
637
|
+
++in;
|
|
638
|
+
ret += hexVal(*in);
|
|
639
|
+
++in;
|
|
640
|
+
return ret;
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// If the value encoded is in the surrogate pair range, we need to make
|
|
644
|
+
// sure there is another escape that we can use also.
|
|
645
|
+
//
|
|
646
|
+
// See the explanation in folly/Unicode.h.
|
|
647
|
+
uint16_t prefix = readHex();
|
|
648
|
+
char32_t codePoint = prefix;
|
|
649
|
+
if (utf16_code_unit_is_high_surrogate(prefix)) {
|
|
650
|
+
if (!in.consume("\\u")) {
|
|
651
|
+
in.error(
|
|
652
|
+
"expected another unicode escape for second half of "
|
|
653
|
+
"surrogate pair");
|
|
654
|
+
}
|
|
655
|
+
uint16_t suffix = readHex();
|
|
656
|
+
if (!utf16_code_unit_is_low_surrogate(suffix)) {
|
|
657
|
+
in.error("second character in surrogate pair is invalid");
|
|
658
|
+
}
|
|
659
|
+
codePoint = unicode_code_point_from_utf16_surrogate_pair(prefix, suffix);
|
|
660
|
+
} else if (!utf16_code_unit_is_bmp(prefix)) {
|
|
661
|
+
in.error("invalid unicode code point (in range [0xdc00,0xdfff])");
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
appendCodePointToUtf8(codePoint, out);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
std::string parseString(Input& in) {
|
|
668
|
+
DCHECK_EQ(*in, '\"');
|
|
669
|
+
++in;
|
|
670
|
+
|
|
671
|
+
std::string ret;
|
|
672
|
+
for (;;) {
|
|
673
|
+
auto range = in.skipWhile([](char c) { return c != '\"' && c != '\\'; });
|
|
674
|
+
ret.append(range.begin(), range.end());
|
|
675
|
+
|
|
676
|
+
if (*in == '\"') {
|
|
677
|
+
++in;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
if (*in == '\\') {
|
|
681
|
+
++in;
|
|
682
|
+
switch (*in) {
|
|
683
|
+
// clang-format off
|
|
684
|
+
case '\"': ret.push_back('\"'); ++in; break;
|
|
685
|
+
case '\\': ret.push_back('\\'); ++in; break;
|
|
686
|
+
case '/': ret.push_back('/'); ++in; break;
|
|
687
|
+
case 'b': ret.push_back('\b'); ++in; break;
|
|
688
|
+
case 'f': ret.push_back('\f'); ++in; break;
|
|
689
|
+
case 'n': ret.push_back('\n'); ++in; break;
|
|
690
|
+
case 'r': ret.push_back('\r'); ++in; break;
|
|
691
|
+
case 't': ret.push_back('\t'); ++in; break;
|
|
692
|
+
case 'u': ++in; decodeUnicodeEscape(in, ret); break;
|
|
693
|
+
// clang-format on
|
|
694
|
+
default:
|
|
695
|
+
in.error(
|
|
696
|
+
to<std::string>("unknown escape ", *in, " in string").c_str());
|
|
697
|
+
}
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
if (*in == EOF) {
|
|
701
|
+
in.error("unterminated string");
|
|
702
|
+
}
|
|
703
|
+
if (!*in) {
|
|
704
|
+
/*
|
|
705
|
+
* Apparently we're actually supposed to ban all control
|
|
706
|
+
* characters from strings. This seems unnecessarily
|
|
707
|
+
* restrictive, so we're only banning zero bytes. (Since the
|
|
708
|
+
* string is presumed to be UTF-8 encoded it's fine to just
|
|
709
|
+
* check this way.)
|
|
710
|
+
*/
|
|
711
|
+
in.error("null byte in string");
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
ret.push_back(char(*in));
|
|
715
|
+
++in;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return ret;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
dynamic parseValue(Input& in, json::metadata_map* map) {
|
|
722
|
+
RecursionGuard guard(in);
|
|
723
|
+
|
|
724
|
+
in.skipWhitespace();
|
|
725
|
+
// clang-format off
|
|
726
|
+
return
|
|
727
|
+
*in == '[' ? parseArray(in, map) :
|
|
728
|
+
*in == '{' ? parseObject(in, map) :
|
|
729
|
+
*in == '\"' ? parseString(in) :
|
|
730
|
+
(*in == '-' || (*in >= '0' && *in <= '9')) ? parseNumber(in) :
|
|
731
|
+
in.consume("true") ? true :
|
|
732
|
+
in.consume("false") ? false :
|
|
733
|
+
in.consume("null") ? nullptr :
|
|
734
|
+
in.consume("Infinity") ?
|
|
735
|
+
(in.getOpts().parse_numbers_as_strings ? (dynamic)"Infinity" :
|
|
736
|
+
(dynamic)std::numeric_limits<double>::infinity()) :
|
|
737
|
+
in.consume("NaN") ?
|
|
738
|
+
(in.getOpts().parse_numbers_as_strings ? (dynamic)"NaN" :
|
|
739
|
+
(dynamic)std::numeric_limits<double>::quiet_NaN()) :
|
|
740
|
+
in.error("expected json value");
|
|
741
|
+
// clang-format on
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
} // namespace
|
|
745
|
+
|
|
746
|
+
//////////////////////////////////////////////////////////////////////
|
|
747
|
+
|
|
748
|
+
std::array<uint64_t, 2> buildExtraAsciiToEscapeBitmap(StringPiece chars) {
|
|
749
|
+
std::array<uint64_t, 2> escapes{{0, 0}};
|
|
750
|
+
for (auto b : ByteRange(chars)) {
|
|
751
|
+
if (b >= 0x20 && b < 0x80) {
|
|
752
|
+
escapes[b / 64] |= uint64_t(1) << (b % 64);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return escapes;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
std::string serialize(dynamic const& dyn, serialization_opts const& opts) {
|
|
759
|
+
std::string ret;
|
|
760
|
+
unsigned indentLevel = 0;
|
|
761
|
+
Printer p(ret, opts.pretty_formatting ? &indentLevel : nullptr, &opts);
|
|
762
|
+
p(dyn, nullptr);
|
|
763
|
+
return ret;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Fast path to determine the longest prefix that can be left
|
|
767
|
+
// unescaped in a string of sizeof(T) bytes packed in an integer of
|
|
768
|
+
// type T.
|
|
769
|
+
template <bool EnableExtraAsciiEscapes, class T>
|
|
770
|
+
size_t firstEscapableInWord(T s, const serialization_opts& opts) {
|
|
771
|
+
static_assert(std::is_unsigned<T>::value, "Unsigned integer required");
|
|
772
|
+
static constexpr T kOnes = ~T() / 255; // 0x...0101
|
|
773
|
+
static constexpr T kMsbs = kOnes * 0x80; // 0x...8080
|
|
774
|
+
|
|
775
|
+
// Sets the MSB of bytes < b. Precondition: b < 128.
|
|
776
|
+
auto isLess = [](T w, uint8_t b) {
|
|
777
|
+
// A byte is < b iff subtracting b underflows, so we check that
|
|
778
|
+
// the MSB wasn't set before and it's set after the subtraction.
|
|
779
|
+
return (w - kOnes * b) & ~w & kMsbs;
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
auto isChar = [&](uint8_t c) {
|
|
783
|
+
// A byte is == c iff it is 0 if xor'd with c.
|
|
784
|
+
return isLess(s ^ (kOnes * c), 1);
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// The following masks have the MSB set for each byte of the word
|
|
788
|
+
// that satisfies the corresponding condition.
|
|
789
|
+
auto isHigh = s & kMsbs; // >= 128
|
|
790
|
+
auto isLow = isLess(s, 0x20); // <= 0x1f
|
|
791
|
+
auto needsEscape = isHigh | isLow | isChar('\\') | isChar('"');
|
|
792
|
+
|
|
793
|
+
if /* constexpr */ (EnableExtraAsciiEscapes) {
|
|
794
|
+
// Deal with optional bitmap for unicode escapes. Escapes can optionally be
|
|
795
|
+
// set for ascii characters 32 - 127, so the inner loop may run up to 96
|
|
796
|
+
// times. However, for the case where 0 or a handful of bits are set,
|
|
797
|
+
// looping will be minimal through use of findFirstSet.
|
|
798
|
+
for (size_t i = 0, e = opts.extra_ascii_to_escape_bitmap.size(); i < e;
|
|
799
|
+
++i) {
|
|
800
|
+
const auto offset = i * 64;
|
|
801
|
+
// Clear first 32 characters if this is the first index, since those are
|
|
802
|
+
// always escaped.
|
|
803
|
+
auto bitmap = opts.extra_ascii_to_escape_bitmap[i] &
|
|
804
|
+
(i == 0 ? uint64_t(-1) << 32 : ~0ULL);
|
|
805
|
+
while (bitmap) {
|
|
806
|
+
auto bit = folly::findFirstSet(bitmap);
|
|
807
|
+
needsEscape |= isChar(static_cast<uint8_t>(offset + bit - 1));
|
|
808
|
+
bitmap &= bitmap - 1;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (!needsEscape) {
|
|
814
|
+
return sizeof(T);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (folly::kIsLittleEndian) {
|
|
818
|
+
return folly::findFirstSet(needsEscape) / 8 - 1;
|
|
819
|
+
} else {
|
|
820
|
+
return sizeof(T) - folly::findLastSet(needsEscape) / 8;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Escape a string so that it is legal to print it in JSON text.
|
|
825
|
+
template <bool EnableExtraAsciiEscapes>
|
|
826
|
+
void escapeStringImpl(
|
|
827
|
+
StringPiece input, std::string& out, const serialization_opts& opts) {
|
|
828
|
+
auto hexDigit = [](uint8_t c) -> char {
|
|
829
|
+
return c < 10 ? c + '0' : c - 10 + 'a';
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
out.push_back('\"');
|
|
833
|
+
|
|
834
|
+
auto* p = reinterpret_cast<const unsigned char*>(input.begin());
|
|
835
|
+
auto* q = reinterpret_cast<const unsigned char*>(input.begin());
|
|
836
|
+
auto* e = reinterpret_cast<const unsigned char*>(input.end());
|
|
837
|
+
|
|
838
|
+
while (p < e) {
|
|
839
|
+
// Find the longest prefix that does not need escaping, and copy
|
|
840
|
+
// it literally into the output string.
|
|
841
|
+
auto firstEsc = p;
|
|
842
|
+
while (firstEsc < e) {
|
|
843
|
+
auto avail = to_unsigned(e - firstEsc);
|
|
844
|
+
uint64_t word = 0;
|
|
845
|
+
if (avail >= 8) {
|
|
846
|
+
word = folly::loadUnaligned<uint64_t>(firstEsc);
|
|
847
|
+
} else {
|
|
848
|
+
word = folly::partialLoadUnaligned<uint64_t>(firstEsc, avail);
|
|
849
|
+
}
|
|
850
|
+
auto prefix = firstEscapableInWord<EnableExtraAsciiEscapes>(word, opts);
|
|
851
|
+
DCHECK_LE(prefix, avail);
|
|
852
|
+
// [Windows Sometimes prefix is completely wrong (corrupt?), only in Release, causing a later AV (see issue #14394).
|
|
853
|
+
// Prefix should always be <= avail, capping it here as a workaround, hoping the assert above eventually catches this in Debug.
|
|
854
|
+
prefix = std::min(prefix, (size_t)avail);
|
|
855
|
+
// Windows]
|
|
856
|
+
firstEsc += prefix;
|
|
857
|
+
if (prefix < 8) {
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (firstEsc > p) {
|
|
862
|
+
out.append(reinterpret_cast<const char*>(p), firstEsc - p);
|
|
863
|
+
p = firstEsc;
|
|
864
|
+
// We can't be in the middle of a multibyte sequence, so we can reset q.
|
|
865
|
+
q = p;
|
|
866
|
+
if (p == e) {
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Handle the next byte that may need escaping.
|
|
872
|
+
|
|
873
|
+
// Since non-ascii encoding inherently does utf8 validation
|
|
874
|
+
// we explicitly validate utf8 only if non-ascii encoding is disabled.
|
|
875
|
+
if ((opts.validate_utf8 || opts.skip_invalid_utf8) &&
|
|
876
|
+
!opts.encode_non_ascii) {
|
|
877
|
+
// To achieve better spatial and temporal coherence
|
|
878
|
+
// we do utf8 validation progressively along with the
|
|
879
|
+
// string-escaping instead of two separate passes.
|
|
880
|
+
|
|
881
|
+
// As the encoding progresses, q will stay at or ahead of p.
|
|
882
|
+
CHECK_GE(q, p);
|
|
883
|
+
|
|
884
|
+
// As p catches up with q, move q forward.
|
|
885
|
+
if (q == p) {
|
|
886
|
+
// calling utf8_decode has the side effect of
|
|
887
|
+
// checking that utf8 encodings are valid
|
|
888
|
+
char32_t v = utf8ToCodePoint(q, e, opts.skip_invalid_utf8);
|
|
889
|
+
if (opts.skip_invalid_utf8 && v == U'\ufffd') {
|
|
890
|
+
out.append(reinterpret_cast<const char*>(u8"\ufffd"));
|
|
891
|
+
p = q;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
auto encodeUnicode = opts.encode_non_ascii && (*p & 0x80);
|
|
898
|
+
if /* constexpr */ (EnableExtraAsciiEscapes) {
|
|
899
|
+
encodeUnicode = encodeUnicode ||
|
|
900
|
+
(*p >= 0x20 && *p < 0x80 &&
|
|
901
|
+
(opts.extra_ascii_to_escape_bitmap[*p / 64] &
|
|
902
|
+
(uint64_t(1) << (*p % 64))));
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (encodeUnicode) {
|
|
906
|
+
// note that this if condition captures utf8 chars
|
|
907
|
+
// with value > 127, so size > 1 byte (or they are whitelisted for
|
|
908
|
+
// Unicode encoding).
|
|
909
|
+
// NOTE: char32_t / char16_t are both unsigned.
|
|
910
|
+
char32_t cp = utf8ToCodePoint(p, e, opts.skip_invalid_utf8);
|
|
911
|
+
auto writeHex = [&](char16_t v) {
|
|
912
|
+
char buf[] = "\\u\0\0\0\0";
|
|
913
|
+
buf[2] = hexDigit((v >> 12) & 0x0f);
|
|
914
|
+
buf[3] = hexDigit((v >> 8) & 0x0f);
|
|
915
|
+
buf[4] = hexDigit((v >> 4) & 0x0f);
|
|
916
|
+
buf[5] = hexDigit(v & 0x0f);
|
|
917
|
+
out.append(buf, 6);
|
|
918
|
+
};
|
|
919
|
+
// From the ECMA-404 The JSON Data Interchange Syntax 2nd Edition Dec 2017
|
|
920
|
+
if (cp < 0x10000u) {
|
|
921
|
+
// If the code point is in the Basic Multilingual Plane (U+0000 through
|
|
922
|
+
// U+FFFF), then it may be represented as a six-character sequence:
|
|
923
|
+
// a reverse solidus, followed by the lowercase letter u, followed by
|
|
924
|
+
// four hexadecimal digits that encode the code point.
|
|
925
|
+
writeHex(static_cast<char16_t>(cp));
|
|
926
|
+
} else {
|
|
927
|
+
// To escape a code point that is not in the Basic Multilingual Plane,
|
|
928
|
+
// the character may be represented as a twelve-character sequence,
|
|
929
|
+
// encoding the UTF-16 surrogate pair corresponding to the code point.
|
|
930
|
+
writeHex(static_cast<char16_t>(
|
|
931
|
+
0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu)));
|
|
932
|
+
writeHex(static_cast<char16_t>(0xdc00u + ((cp - 0x10000u) & 0x3ffu)));
|
|
933
|
+
}
|
|
934
|
+
} else if (*p == '\\' || *p == '\"') {
|
|
935
|
+
char buf[] = "\\\0";
|
|
936
|
+
buf[1] = char(*p++);
|
|
937
|
+
out.append(buf, 2);
|
|
938
|
+
} else if (*p <= 0x1f) {
|
|
939
|
+
switch (*p) {
|
|
940
|
+
// clang-format off
|
|
941
|
+
case '\b': out.append("\\b"); p++; break;
|
|
942
|
+
case '\f': out.append("\\f"); p++; break;
|
|
943
|
+
case '\n': out.append("\\n"); p++; break;
|
|
944
|
+
case '\r': out.append("\\r"); p++; break;
|
|
945
|
+
case '\t': out.append("\\t"); p++; break;
|
|
946
|
+
// clang-format on
|
|
947
|
+
default:
|
|
948
|
+
// Note that this if condition captures non readable chars
|
|
949
|
+
// with value < 32, so size = 1 byte (e.g control chars).
|
|
950
|
+
char buf[] = "\\u00\0\0";
|
|
951
|
+
buf[4] = hexDigit(uint8_t((*p & 0xf0) >> 4));
|
|
952
|
+
buf[5] = hexDigit(uint8_t(*p & 0xf));
|
|
953
|
+
out.append(buf, 6);
|
|
954
|
+
p++;
|
|
955
|
+
}
|
|
956
|
+
} else {
|
|
957
|
+
out.push_back(char(*p++));
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
out.push_back('\"');
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
void escapeString(
|
|
965
|
+
StringPiece input, std::string& out, const serialization_opts& opts) {
|
|
966
|
+
if (FOLLY_UNLIKELY(
|
|
967
|
+
opts.extra_ascii_to_escape_bitmap[0] ||
|
|
968
|
+
opts.extra_ascii_to_escape_bitmap[1])) {
|
|
969
|
+
escapeStringImpl<true>(input, out, opts);
|
|
970
|
+
} else {
|
|
971
|
+
escapeStringImpl<false>(input, out, opts);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
std::string stripComments(StringPiece jsonC) {
|
|
976
|
+
std::string result;
|
|
977
|
+
enum class State {
|
|
978
|
+
None,
|
|
979
|
+
InString,
|
|
980
|
+
InlineComment,
|
|
981
|
+
LineComment
|
|
982
|
+
} state = State::None;
|
|
983
|
+
|
|
984
|
+
for (size_t i = 0; i < jsonC.size(); ++i) {
|
|
985
|
+
auto s = jsonC.subpiece(i);
|
|
986
|
+
switch (state) {
|
|
987
|
+
case State::None:
|
|
988
|
+
if (s.startsWith("/*")) {
|
|
989
|
+
state = State::InlineComment;
|
|
990
|
+
++i;
|
|
991
|
+
continue;
|
|
992
|
+
} else if (s.startsWith("//")) {
|
|
993
|
+
state = State::LineComment;
|
|
994
|
+
++i;
|
|
995
|
+
continue;
|
|
996
|
+
} else if (s[0] == '\"') {
|
|
997
|
+
state = State::InString;
|
|
998
|
+
}
|
|
999
|
+
result.push_back(s[0]);
|
|
1000
|
+
break;
|
|
1001
|
+
case State::InString:
|
|
1002
|
+
if (s[0] == '\\') {
|
|
1003
|
+
if (FOLLY_UNLIKELY(s.size() == 1)) {
|
|
1004
|
+
throw std::logic_error("Invalid JSONC: string is not terminated");
|
|
1005
|
+
}
|
|
1006
|
+
result.push_back(s[0]);
|
|
1007
|
+
result.push_back(s[1]);
|
|
1008
|
+
++i;
|
|
1009
|
+
continue;
|
|
1010
|
+
} else if (s[0] == '\"') {
|
|
1011
|
+
state = State::None;
|
|
1012
|
+
}
|
|
1013
|
+
result.push_back(s[0]);
|
|
1014
|
+
break;
|
|
1015
|
+
case State::InlineComment:
|
|
1016
|
+
if (s.startsWith('\n')) {
|
|
1017
|
+
// preserve the line break to preserve the line count
|
|
1018
|
+
result.push_back(s[0]);
|
|
1019
|
+
} else if (s.startsWith("*/")) {
|
|
1020
|
+
state = State::None;
|
|
1021
|
+
++i;
|
|
1022
|
+
}
|
|
1023
|
+
break;
|
|
1024
|
+
case State::LineComment:
|
|
1025
|
+
if (s[0] == '\n') {
|
|
1026
|
+
// preserve the line break to preserve the line count
|
|
1027
|
+
result.push_back(s[0]);
|
|
1028
|
+
state = State::None;
|
|
1029
|
+
}
|
|
1030
|
+
break;
|
|
1031
|
+
default:
|
|
1032
|
+
throw std::logic_error("Unknown comment state");
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
} // namespace json
|
|
1039
|
+
|
|
1040
|
+
//////////////////////////////////////////////////////////////////////
|
|
1041
|
+
|
|
1042
|
+
dynamic parseJsonWithMetadata(StringPiece range, json::metadata_map* map) {
|
|
1043
|
+
return parseJsonWithMetadata(range, json::serialization_opts(), map);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
dynamic parseJsonWithMetadata(
|
|
1047
|
+
StringPiece range,
|
|
1048
|
+
json::serialization_opts const& opts,
|
|
1049
|
+
json::metadata_map* map) {
|
|
1050
|
+
json::Input in(range, &opts);
|
|
1051
|
+
|
|
1052
|
+
uint32_t n = in.getLineNum();
|
|
1053
|
+
auto ret = parseValue(in, map);
|
|
1054
|
+
if (map) {
|
|
1055
|
+
map->emplace(&ret, json::parse_metadata{{{0}}, {{n}}});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
in.skipWhitespace();
|
|
1059
|
+
if (in.size() && *in != '\0') {
|
|
1060
|
+
in.error("parsing didn't consume all input");
|
|
1061
|
+
}
|
|
1062
|
+
return ret;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
dynamic parseJson(StringPiece range) {
|
|
1066
|
+
return parseJson(range, json::serialization_opts());
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
dynamic parseJson(StringPiece range, json::serialization_opts const& opts) {
|
|
1070
|
+
json::Input in(range, &opts);
|
|
1071
|
+
|
|
1072
|
+
auto ret = parseValue(in, nullptr);
|
|
1073
|
+
in.skipWhitespace();
|
|
1074
|
+
if (in.size() && *in != '\0') {
|
|
1075
|
+
in.error("parsing didn't consume all input");
|
|
1076
|
+
}
|
|
1077
|
+
return ret;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
std::string toJson(dynamic const& dyn) {
|
|
1081
|
+
return json::serialize(dyn, json::serialization_opts());
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
std::string toPrettyJson(dynamic const& dyn) {
|
|
1085
|
+
json::serialization_opts opts;
|
|
1086
|
+
opts.pretty_formatting = true;
|
|
1087
|
+
opts.sort_keys = true;
|
|
1088
|
+
return json::serialize(dyn, opts);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
//////////////////////////////////////////////////////////////////////
|
|
1092
|
+
// dynamic::print_as_pseudo_json() is implemented here for header
|
|
1093
|
+
// ordering reasons (most of the dynamic implementation is in
|
|
1094
|
+
// dynamic-inl.h, which we don't want to include json.h).
|
|
1095
|
+
|
|
1096
|
+
void dynamic::print_as_pseudo_json(std::ostream& out) const {
|
|
1097
|
+
json::serialization_opts opts;
|
|
1098
|
+
opts.allow_non_string_keys = true;
|
|
1099
|
+
opts.allow_nan_inf = true;
|
|
1100
|
+
out << json::serialize(*this, opts);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
void PrintTo(const dynamic& dyn, std::ostream* os) {
|
|
1104
|
+
json::serialization_opts opts;
|
|
1105
|
+
opts.allow_nan_inf = true;
|
|
1106
|
+
opts.allow_non_string_keys = true;
|
|
1107
|
+
opts.pretty_formatting = true;
|
|
1108
|
+
opts.sort_keys = true;
|
|
1109
|
+
*os << json::serialize(dyn, opts);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
//////////////////////////////////////////////////////////////////////
|
|
1113
|
+
|
|
1114
|
+
} // namespace folly
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
* you may not use this file except in compliance with the License.
|
|
@@ -37,7 +37,6 @@ struct to_ascii_array {
|
|
|
37
37
|
return data.data[index];
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
-
|
|
41
40
|
template <uint64_t Base, typename Alphabet>
|
|
42
41
|
alignas(kIsMobile ? sizeof(size_t) : hardware_constructive_interference_size)
|
|
43
42
|
typename to_ascii_array<Base, Alphabet>::data_type_ const
|
|
@@ -92,13 +91,13 @@ extern template to_ascii_table<10, to_ascii_alphabet_upper>::data_type_ const
|
|
|
92
91
|
extern template to_ascii_table<16, to_ascii_alphabet_upper>::data_type_ const
|
|
93
92
|
to_ascii_table<16, to_ascii_alphabet_upper>::data;
|
|
94
93
|
|
|
95
|
-
template <uint64_t Base, typename
|
|
94
|
+
template <uint64_t Base, typename Int>
|
|
96
95
|
struct to_ascii_powers {
|
|
97
|
-
static constexpr size_t size_(
|
|
96
|
+
static constexpr size_t size_(Int v) {
|
|
98
97
|
return 1 + (v < Base ? 0 : size_(v / Base));
|
|
99
98
|
}
|
|
100
|
-
static constexpr size_t const size = size_(~
|
|
101
|
-
using data_type_ = c_array<
|
|
99
|
+
static constexpr size_t const size = size_(~Int(0));
|
|
100
|
+
using data_type_ = c_array<Int, size>;
|
|
102
101
|
static constexpr data_type_ data_() {
|
|
103
102
|
data_type_ result{};
|
|
104
103
|
for (size_t i = 0; i < size; ++i) {
|
|
@@ -109,12 +108,14 @@ struct to_ascii_powers {
|
|
|
109
108
|
// @lint-ignore CLANGTIDY
|
|
110
109
|
static data_type_ const data;
|
|
111
110
|
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
#if FOLLY_CPLUSPLUS < 201703L
|
|
112
|
+
template <uint64_t Base, typename Int>
|
|
113
|
+
constexpr size_t const to_ascii_powers<Base, Int>::size;
|
|
114
|
+
#endif
|
|
115
|
+
template <uint64_t Base, typename Int>
|
|
115
116
|
alignas(hardware_constructive_interference_size)
|
|
116
|
-
typename to_ascii_powers<Base,
|
|
117
|
-
to_ascii_powers<Base,
|
|
117
|
+
typename to_ascii_powers<Base, Int>::data_type_ const
|
|
118
|
+
to_ascii_powers<Base, Int>::data = to_ascii_powers<Base, Int>::data_();
|
|
118
119
|
|
|
119
120
|
extern template to_ascii_powers<8, uint64_t>::data_type_ const
|
|
120
121
|
to_ascii_powers<8, uint64_t>::data;
|
|
@@ -149,7 +150,7 @@ template <uint64_t Base>
|
|
|
149
150
|
FOLLY_ALWAYS_INLINE size_t to_ascii_size_array(uint64_t v) {
|
|
150
151
|
using powers = to_ascii_powers<Base, uint64_t>;
|
|
151
152
|
for (size_t i = 0u; i < powers::size; ++i) {
|
|
152
|
-
if (
|
|
153
|
+
if (FOLLY_UNLIKELY(v < powers::data.data[i])) {
|
|
153
154
|
return i + size_t(i == 0);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
@@ -270,6 +271,15 @@ FOLLY_ALWAYS_INLINE size_t to_ascii_with_table(char* out, uint64_t v) {
|
|
|
270
271
|
return size;
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
// Assumes that size >= number of digits in v. If >, the result is left-padded
|
|
275
|
+
// with 0s.
|
|
276
|
+
template <uint64_t Base, typename Alphabet>
|
|
277
|
+
FOLLY_ALWAYS_INLINE void to_ascii_with_route(
|
|
278
|
+
char* outb, size_t size, uint64_t v) {
|
|
279
|
+
kIsMobile //
|
|
280
|
+
? to_ascii_with_array<Base, Alphabet>(outb, size, v)
|
|
281
|
+
: to_ascii_with_table<Base, Alphabet>(outb, size, v);
|
|
282
|
+
}
|
|
273
283
|
template <uint64_t Base, typename Alphabet>
|
|
274
284
|
FOLLY_ALWAYS_INLINE size_t
|
|
275
285
|
to_ascii_with_route(char* outb, char const* oute, uint64_t v) {
|
|
@@ -277,9 +287,7 @@ to_ascii_with_route(char* outb, char const* oute, uint64_t v) {
|
|
|
277
287
|
if (FOLLY_UNLIKELY(oute < outb || size_t(oute - outb) < size)) {
|
|
278
288
|
return 0;
|
|
279
289
|
}
|
|
280
|
-
|
|
281
|
-
? to_ascii_with_array<Base, Alphabet>(outb, size, v)
|
|
282
|
-
: to_ascii_with_table<Base, Alphabet>(outb, size, v);
|
|
290
|
+
to_ascii_with_route<Base, Alphabet>(outb, size, v);
|
|
283
291
|
return size;
|
|
284
292
|
}
|
|
285
293
|
template <uint64_t Base, typename Alphabet, size_t N>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
* you may not use this file except in compliance with the License.
|
|
@@ -61,14 +61,14 @@ using to_ascii_alphabet_upper = to_ascii_alphabet<true>;
|
|
|
61
61
|
// In base 10, u64 requires at most 20 bytes, u32 at most 10, u16 at most 5,
|
|
62
62
|
// and u8 at most 3.
|
|
63
63
|
/*
|
|
64
|
-
template <uint64_t Base, typename
|
|
64
|
+
template <uint64_t Base, typename Int>
|
|
65
65
|
FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max =
|
|
66
|
-
|
|
66
|
+
detail::to_ascii_powers<Base, Int>::size;
|
|
67
67
|
*/
|
|
68
68
|
// to_ascii_size_max_decimal
|
|
69
69
|
//
|
|
70
70
|
// An alias to to_ascii_size_max<10>.
|
|
71
|
-
template <typename
|
|
71
|
+
template <typename Int>
|
|
72
72
|
FOLLY_INLINE_VARIABLE constexpr size_t to_ascii_size_max_decimal;
|
|
73
73
|
|
|
74
74
|
template <>
|
|
@@ -170,7 +170,7 @@ size_t to_ascii_upper(char (&out)[N], uint64_t v) {
|
|
|
170
170
|
//
|
|
171
171
|
// An alias to to_ascii<10, false>.
|
|
172
172
|
//
|
|
173
|
-
// async-
|
|
173
|
+
// async-signal-safe
|
|
174
174
|
inline size_t to_ascii_decimal(char* outb, char const* oute, uint64_t v) {
|
|
175
175
|
return to_ascii_lower<10>(outb, oute, v);
|
|
176
176
|
}
|
package/Folly/cgmanifest.json
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.74.
|
|
13
|
+
<ReactNativeWindowsVersion>0.74.41</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>74</ReactNativeWindowsMinor>
|
|
16
|
-
<ReactNativeWindowsPatch>
|
|
16
|
+
<ReactNativeWindowsPatch>41</ReactNativeWindowsPatch>
|
|
17
17
|
<ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
|
|
18
|
-
<ReactNativeWindowsCommitId>
|
|
18
|
+
<ReactNativeWindowsCommitId>9cb5b6f1cdb748aa6cc7c042e1acbe21605da86c</ReactNativeWindowsCommitId>
|
|
19
19
|
</PropertyGroup>
|
|
20
20
|
</Project>
|
|
@@ -290,6 +290,10 @@ fire_and_forget WinRTWebSocketResource2::PerformWrite(string &&message, bool isB
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
IAsyncAction WinRTWebSocketResource2::SendPendingMessages() noexcept {
|
|
293
|
+
// Enforcing execution in the background queue.
|
|
294
|
+
// Awaiting of this coroutine will schedule its execution in the thread pool, ignoring the intended dispatch queue.
|
|
295
|
+
co_await resume_in_queue(m_backgroundQueue);
|
|
296
|
+
|
|
293
297
|
auto self = shared_from_this();
|
|
294
298
|
|
|
295
299
|
while (!self->m_outgoingMessages.empty()) {
|
|
@@ -334,6 +338,7 @@ IAsyncAction WinRTWebSocketResource2::SendPendingMessages() noexcept {
|
|
|
334
338
|
auto result = async.ErrorCode();
|
|
335
339
|
if (result < 0) {
|
|
336
340
|
Fail(std::move(result), ErrorType::Send);
|
|
341
|
+
co_return;
|
|
337
342
|
}
|
|
338
343
|
}
|
|
339
344
|
}
|