/src/hermes/include/hermes/Support/JSON.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #ifndef HERMES_SUPPORT_JSON_H |
9 | | #define HERMES_SUPPORT_JSON_H |
10 | | |
11 | | #include "hermes/Platform/Unicode/CharacterProperties.h" |
12 | | |
13 | | #include "llvh/ADT/ArrayRef.h" |
14 | | |
15 | | namespace hermes { |
16 | | |
17 | | template <typename Output> |
18 | 0 | void appendUTF16Escaped(Output &output, char16_t cp) { |
19 | 0 | auto toLowerHex = [](uint8_t u) { |
20 | 0 | assert(u <= 0xF); |
21 | 0 | return u"0123456789abcdef"[u]; |
22 | 0 | }; |
23 | 0 | output.append({u'\\', u'u'}); |
24 | 0 | output.push_back(toLowerHex(cp >> 12)); |
25 | 0 | output.push_back(toLowerHex((cp >> 8) & 0xF)); |
26 | 0 | output.push_back(toLowerHex((cp >> 4) & 0xF)); |
27 | 0 | output.push_back(toLowerHex(cp & 0xF)); |
28 | 0 | } |
29 | | |
30 | | // If there is a valid surrogate pair at position \p i in \p view, then write |
31 | | // both the high and low surrogate into \p output. Otherwise, write an escaped |
32 | | // UTF16 value into \p output. \return true if a pair was found. |
33 | | template <typename Output, typename CharT> |
34 | 0 | bool handleSurrogate(Output &output, llvh::ArrayRef<CharT> view, size_t i) { |
35 | 0 | char16_t ch = view[i]; |
36 | 0 | assert( |
37 | 0 | ch >= UNICODE_SURROGATE_FIRST && ch <= UNICODE_SURROGATE_LAST && |
38 | 0 | "charcter should be a surrogate character"); |
39 | | // Handle well-formed-ness here: Represent unpaired surrogate code points as |
40 | | // JSON escape sequences. |
41 | 0 | if (isHighSurrogate(ch) && i + 1 < view.size()) { |
42 | 0 | char16_t next = view[i + 1]; |
43 | 0 | if (isLowSurrogate(next)) { |
44 | | // We found a surrogate pair. Simply write both of them unescaped to the |
45 | | // output. |
46 | 0 | output.push_back(ch); |
47 | 0 | output.push_back(next); |
48 | 0 | return true; |
49 | 0 | } |
50 | 0 | } |
51 | | // We did not find a valid pair, so the current surrogate character must be |
52 | | // written as an escaped JSON sequence. |
53 | 0 | appendUTF16Escaped(output, ch); |
54 | 0 | return false; |
55 | 0 | } |
56 | | |
57 | | /// Quotes a string given by \p view and puts the quoted version into \p output. |
58 | | /// \p view must be utf16 or ASCII encoded. |
59 | | /// \post output is a container that has a sequential list of utf16 characters |
60 | | /// that can be embedded into a JSON string. |
61 | | template <typename Output, typename CharT> |
62 | 0 | void quoteStringForJSON(Output &output, llvh::ArrayRef<CharT> view) { |
63 | | // Quote.1. |
64 | 0 | output.push_back(u'"'); |
65 | | // Quote.2. |
66 | 0 | for (size_t i = 0, e = view.size(); i < e; ++i) { |
67 | 0 | CharT ch = view[i]; |
68 | 0 | #define ESCAPE(ch, replace) \ |
69 | 0 | case ch: \ |
70 | 0 | output.push_back(u'\\'); \ |
71 | 0 | output.push_back(replace); \ |
72 | 0 | break |
73 | |
|
74 | 0 | switch (ch) { |
75 | | // Quote.2.a. |
76 | 0 | ESCAPE(u'\\', u'\\'); |
77 | 0 | ESCAPE(u'"', u'"'); |
78 | | // Quote.2.b. |
79 | 0 | ESCAPE(u'\b', u'b'); |
80 | 0 | ESCAPE(u'\f', u'f'); |
81 | 0 | ESCAPE(u'\n', u'n'); |
82 | 0 | ESCAPE(u'\r', u'r'); |
83 | 0 | ESCAPE(u'\t', u't'); |
84 | 0 | default: |
85 | 0 | if (ch < u' ') { |
86 | | // Quote.2.c. |
87 | 0 | output.append({u'\\', u'u', u'0', u'0'}); |
88 | 0 | output.push_back(u'0' + (ch / 16)); |
89 | 0 | if (ch % 16 < 10) { |
90 | 0 | output.push_back(u'0' + (ch % 16)); |
91 | 0 | } else { |
92 | 0 | output.push_back(u'a' + (ch % 16 - 10)); |
93 | 0 | } |
94 | 0 | } else { |
95 | | // This check should only generate code when CharT is more than 1 |
96 | | // byte. |
97 | | if constexpr ( |
98 | 0 | std::numeric_limits<CharT>::max() >= UNICODE_SURROGATE_FIRST) { |
99 | 0 | if (ch >= UNICODE_SURROGATE_FIRST && ch <= UNICODE_SURROGATE_LAST) { |
100 | 0 | if (handleSurrogate(output, view, i)) { |
101 | | // Found a valid surrogate pair, so skip over the next |
102 | | // character. |
103 | 0 | i++; |
104 | 0 | } |
105 | 0 | } else { |
106 | | // Quote.2.d. |
107 | 0 | output.push_back(ch); |
108 | 0 | } |
109 | 0 | } else { |
110 | | // Quote.2.d. |
111 | 0 | output.push_back(ch); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | } |
115 | 0 | } |
116 | | // Quote.3. |
117 | 0 | output.push_back(u'"'); |
118 | 0 | } Unexecuted instantiation: void hermes::quoteStringForJSON<llvh::SmallVector<char16_t, 32u>, char>(llvh::SmallVector<char16_t, 32u>&, llvh::ArrayRef<char>) Unexecuted instantiation: void hermes::quoteStringForJSON<llvh::SmallVector<char16_t, 32u>, char16_t>(llvh::SmallVector<char16_t, 32u>&, llvh::ArrayRef<char16_t>) |
119 | | |
120 | | } // namespace hermes |
121 | | |
122 | | #endif |