Coverage Report

Created: 2023-11-12 09:30

/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