/src/hermes/include/hermes/Support/JSONEmitter.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_JSONEMITTER_H |
9 | | #define HERMES_SUPPORT_JSONEMITTER_H |
10 | | |
11 | | #include <cstdint> |
12 | | #include "llvh/ADT/ArrayRef.h" |
13 | | #include "llvh/ADT/SmallVector.h" |
14 | | #include "llvh/ADT/StringRef.h" |
15 | | #include "llvh/Support/Format.h" |
16 | | #include "llvh/Support/raw_ostream.h" |
17 | | |
18 | | namespace hermes { |
19 | | |
20 | | /// JSONEmitter is a simple, stateful class for emitting JSON to an |
21 | | /// llvh::raw_ostream. |
22 | | /// To emit an array, use openArray(). Then call a sequence of emitValue() or |
23 | | /// emitValues(), or use openArray()/openDict() to nest a collection. Finish |
24 | | /// with closeArray(). To emit a dictionary, use openDict(), a sequence of |
25 | | /// emitKeyValue() or emitKey() followed by openArray()/openDict() to nest a |
26 | | /// collection. Finish with closeDict(). Unbalanced dictionaries/arrays are |
27 | | /// caught via assert(). |
28 | | /// |
29 | | /// JSONEmitter accepts 8-bit strings which it expects to be in UTF-8 format. |
30 | | /// Invalid UTF-8 strings are fatal errors. It is the caller's responsibility |
31 | | /// to ensure only valid UTF-8 is passed. |
32 | | /// |
33 | | /// Example usage: |
34 | | /// JSONEmitter json(llvh::outs()); |
35 | | /// json.openDict(); |
36 | | /// json.emitKeyValue("name", "hermes"); |
37 | | /// json.emitKeyValue("age", 2); |
38 | | /// json.emitKey("platforms"); |
39 | | /// json.openArray(); |
40 | | /// json.emitValues({"linux", "mac", "android"}); |
41 | | /// json.closeArray(); |
42 | | /// json.closeDict(); |
43 | | /// |
44 | | class JSONEmitter { |
45 | | public: |
46 | | /// Construct a JSONEmitter to output to a stream \p OS. The emitter is |
47 | | /// initially empty. Usually you will want to invoke openDict() immediately on |
48 | | /// it. |
49 | | /// \p pretty is the option to pretty print the JSON object, with new lines |
50 | | /// and indentation. Note that since the JSONEmitter does not know when |
51 | | /// the printing will stop, the user needs to print a new line themselves |
52 | | /// when they finish. |
53 | | JSONEmitter(llvh::raw_ostream &OS, bool pretty = false) |
54 | 0 | : OS(OS), pretty_(pretty) {} |
55 | | |
56 | | /// Emit a boolean value \p val. |
57 | | void emitValue(bool val); |
58 | | |
59 | | /// Emit an integer value \p val. |
60 | | void emitValue(short val); |
61 | | void emitValue(int val); |
62 | | void emitValue(long val); |
63 | | void emitValue(long long val); |
64 | | |
65 | | /// Emit an unsigned integer value \p val. |
66 | | void emitValue(unsigned short val); |
67 | | void emitValue(unsigned int val); |
68 | | void emitValue(unsigned long val); |
69 | | void emitValue(unsigned long long val); |
70 | | |
71 | | /// Emit a double value \p val. |
72 | | /// Note that only finite values may be so emitted. Infinities and NaNs are |
73 | | /// an error. |
74 | | void emitValue(double val); |
75 | | |
76 | | /// Emit a string \p val. This is not used to emit dictionary keys: use |
77 | | /// emitKey() instead. |
78 | | void emitValue(llvh::StringRef val); |
79 | 0 | void emitValue(const char *val) { |
80 | 0 | emitValue(llvh::StringRef(val)); |
81 | 0 | } |
82 | | /// Emit a string \p val. Any ascii characters will be emitted as is. Any |
83 | | /// non-ascii characters will be emitted as the escaped 16-bit code unit. |
84 | | void emitValue(llvh::ArrayRef<char16_t> val); |
85 | | |
86 | | /// Emit a null as value. |
87 | | void emitNullValue(); |
88 | | |
89 | | /// Emit a dictionary key \p key. This requires that we are currently emitting |
90 | | /// a dictionary, and it expects a key (not a value). |
91 | | void emitKey(llvh::StringRef key); |
92 | | |
93 | | /// Emit a key \p key followed by a value \p val. This requires that we are |
94 | | /// currently emitting a dictionary and it expects a key. |
95 | | template <typename T> |
96 | 0 | void emitKeyValue(llvh::StringRef key, const T &val) { |
97 | 0 | emitKey(key); |
98 | 0 | emitValue(val); |
99 | 0 | } Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [7]>(llvh::StringRef, char const (&) [7]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<int>(llvh::StringRef, int const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<unsigned int>(llvh::StringRef, unsigned int const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<unsigned long>(llvh::StringRef, unsigned long const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<double>(llvh::StringRef, double const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<long>(llvh::StringRef, long const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(llvh::StringRef, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<long long>(llvh::StringRef, long long const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<llvh::StringRef>(llvh::StringRef, llvh::StringRef const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [8]>(llvh::StringRef, char const (&) [8]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<bool>(llvh::StringRef, bool const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [13]>(llvh::StringRef, char const (&) [13]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char const*>(llvh::StringRef, char const* const&) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [11]>(llvh::StringRef, char const (&) [11]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [3]>(llvh::StringRef, char const (&) [3]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [12]>(llvh::StringRef, char const (&) [12]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [1]>(llvh::StringRef, char const (&) [1]) Unexecuted instantiation: void hermes::JSONEmitter::emitKeyValue<char [5]>(llvh::StringRef, char const (&) [5]) |
100 | | |
101 | | /// Emit a sequence of values \p val. This requires that we are currently |
102 | | /// emitting an array. |
103 | | template <typename T> |
104 | 0 | void emitValues(llvh::ArrayRef<T> vals) { |
105 | 0 | for (const T &val : vals) |
106 | 0 | emitValue(val); |
107 | 0 | } Unexecuted instantiation: void hermes::JSONEmitter::emitValues<llvh::StringRef>(llvh::ArrayRef<llvh::StringRef>) Unexecuted instantiation: void hermes::JSONEmitter::emitValues<unsigned int>(llvh::ArrayRef<unsigned int>) |
108 | | |
109 | | template <typename T> |
110 | 0 | void emitValues(std::initializer_list<T> vals) { |
111 | 0 | for (const T &val : vals) |
112 | 0 | emitValue(val); |
113 | 0 | } Unexecuted instantiation: void hermes::JSONEmitter::emitValues<unsigned long>(std::initializer_list<unsigned long>) Unexecuted instantiation: void hermes::JSONEmitter::emitValues<char const*>(std::initializer_list<char const*>) |
114 | | |
115 | | /// Begin emitting a dictionary. |
116 | | void openDict(); |
117 | | |
118 | | /// Close the currently emitting dictionary. |
119 | | void closeDict(); |
120 | | |
121 | | /// Begin emitting an array. |
122 | | void openArray(); |
123 | | |
124 | | /// Close the currently emitting array. |
125 | | void closeArray(); |
126 | | |
127 | | /// Terminate a JSON Lines record. |
128 | | void endJSONL(); |
129 | | |
130 | | /// JSONEmitters hold raw_ostreams by reference. |
131 | | /// They may be moved but not copied. |
132 | | JSONEmitter(JSONEmitter &&); |
133 | | |
134 | | private: |
135 | | JSONEmitter(const JSONEmitter &) = delete; |
136 | | void operator=(const JSONEmitter &) = delete; |
137 | | void operator=(JSONEmitter &&) = delete; |
138 | | |
139 | | /// A function to unconditionally emit a string. This is used for both |
140 | | /// dictionary keys and ordinary string values. |
141 | | /// Assumes \p str is encoded in utf-8. |
142 | | /// Escapes certain control characters and non-ascii characters. |
143 | | void primitiveEmitString(llvh::StringRef str); |
144 | | |
145 | | /// \return whether a dictionary is currently being emitted. |
146 | 0 | bool inDict() const { |
147 | 0 | return !states_.empty() && states_.back().type == State::Dict; |
148 | 0 | } |
149 | | |
150 | | /// \return whether an array is currently being emitted. |
151 | 0 | bool inArray() const { |
152 | 0 | return !states_.empty() && states_.back().type == State::Array; |
153 | 0 | } |
154 | | |
155 | | /// Given that we are about to emit a value of any type (but not a dictionary |
156 | | /// key), perform housekeeping tasks such as emitting a trailing comma. |
157 | | void willEmitValue(); |
158 | | |
159 | | /// In pretty printing mode, print a new line then indent. |
160 | | void prettyNewLine(); |
161 | | |
162 | | /// In pretty printing mode, indent one level more. |
163 | | void indentMore(); |
164 | | |
165 | | /// In pretty printing mode, indent one level less. |
166 | | void indentLess(); |
167 | | |
168 | | /// A State represents the status of a single object (Dictionary or Array) |
169 | | /// being emitted. |
170 | | struct State { |
171 | | /// Whether the object is a dictionary or array. |
172 | | enum Type : uint8_t { Dict, Array } type; |
173 | | |
174 | | /// Whether we need a comma before emitting another value. |
175 | | bool needsComma; |
176 | | |
177 | | /// Whether we are a dictionary and expect a key. |
178 | | bool needsKey; |
179 | | |
180 | | /// Whether we expect a value (only after emitting a key in a dict). |
181 | | bool needsValue; |
182 | | |
183 | | /// Whether the dict or object is empty. |
184 | | bool isEmpty; |
185 | | |
186 | | /// Construct a State given a type \p t. |
187 | | /* implicit */ State(Type t) |
188 | 0 | : type(t), |
189 | 0 | needsComma(false), |
190 | 0 | needsKey(type == Dict), |
191 | 0 | needsValue(false), |
192 | 0 | isEmpty(true) {} |
193 | | }; |
194 | | |
195 | | /// The stack of states. |
196 | | llvh::SmallVector<State, 8> states_; |
197 | | |
198 | | /// The stream to output to. |
199 | | llvh::raw_ostream &OS; |
200 | | |
201 | | /// Pretty print the JSON object. |
202 | | bool pretty_{false}; |
203 | | |
204 | | /// Number of spaces needed to indent. |
205 | | uint32_t indent_{0}; |
206 | | }; |
207 | | |
208 | | } // namespace hermes |
209 | | |
210 | | #endif // HERMES_SUPPORT_JSONEMITTER_H |