/proc/self/cwd/source/common/json/json_streamer.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <memory> |
4 | | #include <stack> |
5 | | #include <string> |
6 | | |
7 | | #include "envoy/buffer/buffer.h" |
8 | | |
9 | | #include "absl/strings/string_view.h" |
10 | | #include "absl/types/variant.h" |
11 | | |
12 | | namespace Envoy { |
13 | | namespace Json { |
14 | | |
15 | | /** |
16 | | * Provides an API for streaming JSON output, as an alternative to populating a |
17 | | * JSON structure with an image of what you want to serialize, or using a |
18 | | * protobuf with reflection. The advantage of this approach is that it does not |
19 | | * require building an intermediate data structure with redundant copies of all |
20 | | * strings, maps, and arrays. |
21 | | */ |
22 | | class Streamer { |
23 | | public: |
24 | | using Value = absl::variant<absl::string_view, double, uint64_t, int64_t>; |
25 | | |
26 | | /** |
27 | | * @param response The buffer in which to stream output. Note: this buffer can |
28 | | * be flushed during population; it is not necessary to hold |
29 | | * the entire json structure in memory before streaming it to |
30 | | * the network. |
31 | | */ |
32 | 0 | explicit Streamer(Buffer::Instance& response) : response_(response) {} |
33 | | |
34 | | class Array; |
35 | | using ArrayPtr = std::unique_ptr<Array>; |
36 | | class Map; |
37 | | using MapPtr = std::unique_ptr<Map>; |
38 | | |
39 | | /** |
40 | | * Represents the current map or array. We keep track of what character is |
41 | | * needed to close it, and whether or not the first entry has been added. |
42 | | */ |
43 | | class Level { |
44 | | public: |
45 | | Level(Streamer& streamer, absl::string_view opener, absl::string_view closer); |
46 | | virtual ~Level(); |
47 | | |
48 | | /** |
49 | | * This must be called on the top level map or array. It's a programming |
50 | | * error to call this method on a map that's not the top level. |
51 | | * It's also a programming error to call this on map that isn't expecting |
52 | | * a value. You must call Map::addKey prior to calling this. |
53 | | * |
54 | | * @return a newly created subordinate map, which becomes the new top level until destroyed. |
55 | | */ |
56 | | MapPtr addMap(); |
57 | | |
58 | | /** |
59 | | * This must be called on the top level map or array. It's a programming |
60 | | * error to call this method on a map or array that's not the top level. |
61 | | * It's also a programming error to call this on map that isn't expecting |
62 | | * a value. You must call Map::addKey prior to calling this. |
63 | | * |
64 | | * @return a newly created subordinate array, which becomes the new top level until destroyed. |
65 | | */ |
66 | | ArrayPtr addArray(); |
67 | | |
68 | | /** |
69 | | * Adds a numeric value to the current array or map. It's a programming |
70 | | * error to call this method on a map or array that's not the top level. |
71 | | * It's also a programming error to call this on map that isn't expecting |
72 | | * a value. You must call Map::addKey prior to calling this. |
73 | | */ |
74 | | void addNumber(double d); |
75 | | void addNumber(uint64_t u); |
76 | | void addNumber(int64_t i); |
77 | | |
78 | | /** |
79 | | * Adds a string constant value to the current array or map. The string |
80 | | * will be sanitized per JSON rules. |
81 | | * |
82 | | * It's a programming error to call this method on a map or array that's not |
83 | | * the top level. It's also a programming error to call this on map that |
84 | | * isn't expecting a value. You must call Map::addKey prior to calling this. |
85 | | */ |
86 | | void addString(absl::string_view str); |
87 | | |
88 | | protected: |
89 | | /** |
90 | | * Initiates a new field, serializing a comma separator if this is not the |
91 | | * first one. |
92 | | */ |
93 | | virtual void nextField(); |
94 | | |
95 | | /** |
96 | | * Renders a string or a number in json format. Doubles that are NaN are |
97 | | * rendered as 'null'. Strings are json-sanitized if needed, and surrounded |
98 | | * by quotes. |
99 | | * |
100 | | * @param Value the value to render. |
101 | | */ |
102 | | void addValue(const Value& value); |
103 | | |
104 | | private: |
105 | | friend Streamer; |
106 | | |
107 | | bool is_first_{true}; // Used to control whether a comma-separator is added for a new entry. |
108 | | Streamer& streamer_; |
109 | | absl::string_view closer_; |
110 | | }; |
111 | | using LevelPtr = std::unique_ptr<Level>; |
112 | | |
113 | | /** |
114 | | * Represents a JSON map while it is being serialized. No data is buffered |
115 | | * in the structure; just enough state to be able emit delimiters properly. |
116 | | */ |
117 | | class Map : public Level { |
118 | | public: |
119 | | using NameValue = std::pair<const absl::string_view, Value>; |
120 | | using Entries = absl::Span<const NameValue>; |
121 | | |
122 | 0 | Map(Streamer& streamer) : Level(streamer, "{", "}") {} |
123 | | |
124 | | /** |
125 | | * Initiates a new map key. This must be followed by rendering a value, |
126 | | * sub-array, or sub-map. It is a programming error to delete a map that has |
127 | | * rendered a key without a matching value. It's also a programming error to |
128 | | * call this method on a map that's not the current top level. |
129 | | * |
130 | | * See also addEntries, which directly populates a list of name/value |
131 | | * pairs in a single call. |
132 | | */ |
133 | | void addKey(absl::string_view key); |
134 | | |
135 | | /** |
136 | | * Populates a list of name/value pairs in a single call. This function |
137 | | * makes it easy to populate structures with scalar values. It's a |
138 | | * programming error to call this method on a map that's not the current top |
139 | | * level. |
140 | | */ |
141 | | void addEntries(const Entries& entries); |
142 | | |
143 | | protected: |
144 | | void nextField() override; |
145 | | |
146 | | private: |
147 | | bool expecting_value_{false}; |
148 | | }; |
149 | | |
150 | | /** |
151 | | * Represents a JSON array while it is being serialized. No data is buffered |
152 | | * in the structure; just enough state to be able emit delimiters properly. |
153 | | */ |
154 | | class Array : public Level { |
155 | | public: |
156 | 0 | Array(Streamer& streamer) : Level(streamer, "[", "]") {} |
157 | | using Entries = absl::Span<const Value>; |
158 | | |
159 | | /** |
160 | | * Adds values to an array. The values may be numeric or strings; strings |
161 | | * will be escaped if needed. It's a programming error to call this method |
162 | | * on an array that's not the current top level. |
163 | | * |
164 | | * @param entries the array of numeric or string values. |
165 | | */ |
166 | | void addEntries(const Entries& entries); |
167 | | }; |
168 | | |
169 | | /** |
170 | | * Makes a root map for the streamer. |
171 | | * |
172 | | * You must create a root map or array before any of the JSON population |
173 | | * functions can be called, as those are only available on Map and Array |
174 | | * objects. |
175 | | */ |
176 | | MapPtr makeRootMap(); |
177 | | |
178 | | /** |
179 | | * Makes a root array for the streamer. |
180 | | * |
181 | | * You must create a root map or array before any of the JSON population |
182 | | * functions can be called, as those are only available on Map and Array |
183 | | * objects. |
184 | | */ |
185 | | ArrayPtr makeRootArray(); |
186 | | |
187 | | private: |
188 | | friend Level; |
189 | | friend Map; |
190 | | friend Array; |
191 | | |
192 | | /** |
193 | | * Takes a raw string, sanitizes it using JSON syntax, surrounds it |
194 | | * with a prefix and suffix, and streams it out. |
195 | | */ |
196 | | void addSanitized(absl::string_view prefix, absl::string_view token, absl::string_view suffix); |
197 | | |
198 | | /** |
199 | | * Serializes a number. |
200 | | */ |
201 | | void addNumber(double d); |
202 | | void addNumber(uint64_t u); |
203 | | void addNumber(int64_t i); |
204 | | |
205 | | /** |
206 | | * Flushes out any pending fragments. |
207 | | */ |
208 | | void flush(); |
209 | | |
210 | | /** |
211 | | * Adds a constant string to the output stream. The string must outlive the |
212 | | * Streamer object, and is intended for literal strings such as punctuation. |
213 | | */ |
214 | 0 | void addConstantString(absl::string_view str) { response_.addFragments({str}); } |
215 | | |
216 | | #ifndef NDEBUG |
217 | | /** |
218 | | * @return the top Level*. This is used for asserts. |
219 | | */ |
220 | 0 | Level* topLevel() const { return levels_.top(); } |
221 | | |
222 | | /** |
223 | | * Pushes a new level onto the stack. |
224 | | */ |
225 | | void push(Level* level); |
226 | | |
227 | | /** |
228 | | * Pops a level off of a stack, asserting that it matches. |
229 | | */ |
230 | | void pop(Level* level); |
231 | | #endif |
232 | | |
233 | | Buffer::Instance& response_; |
234 | | std::string sanitize_buffer_; |
235 | | |
236 | | #ifndef NDEBUG |
237 | | // Keeps a stack of Maps or Arrays (subclasses of Level) to facilitate |
238 | | // assertions that only the top-level map/array can be written. |
239 | | std::stack<Level*> levels_; |
240 | | #endif |
241 | | }; |
242 | | |
243 | | } // namespace Json |
244 | | } // namespace Envoy |