rippled
Loading...
Searching...
No Matches
Writer.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/basics/ToString.h>
21#include <xrpl/json/Output.h>
22#include <xrpl/json/Writer.h>
23
24#include <cstddef>
25#include <map>
26#include <memory>
27#include <set>
28#include <stack>
29#include <string>
30#include <utility>
31#include <vector>
32
33namespace Json {
34
35namespace {
36
37std::map<char, char const*> jsonSpecialCharacterEscape = {
38 {'"', "\\\""},
39 {'\\', "\\\\"},
40 {'/', "\\/"},
41 {'\b', "\\b"},
42 {'\f', "\\f"},
43 {'\n', "\\n"},
44 {'\r', "\\r"},
45 {'\t', "\\t"}};
46
47static size_t const jsonEscapeLength = 2;
48
49// All other JSON punctuation.
50char const closeBrace = '}';
51char const closeBracket = ']';
52char const colon = ':';
53char const comma = ',';
54char const openBrace = '{';
55char const openBracket = '[';
56char const quote = '"';
57
58static auto const integralFloatsBecomeInts = false;
59
60size_t
61lengthWithoutTrailingZeros(std::string const& s)
62{
63 auto dotPos = s.find('.');
64 if (dotPos == std::string::npos)
65 return s.size();
66
67 auto lastNonZero = s.find_last_not_of('0');
68 auto hasDecimals = dotPos != lastNonZero;
69
70 if (hasDecimals)
71 return lastNonZero + 1;
72
73 if (integralFloatsBecomeInts || lastNonZero + 2 > s.size())
74 return lastNonZero;
75
76 return lastNonZero + 2;
77}
78
79} // namespace
80
82{
83public:
84 explicit Impl(Output const& output) : output_(output)
85 {
86 }
87 ~Impl() = default;
88
89 Impl(Impl&&) = delete;
90 Impl&
91 operator=(Impl&&) = delete;
92
93 bool
94 empty() const
95 {
96 return stack_.empty();
97 }
98
99 void
101 {
102 char ch = (ct == array) ? openBracket : openBrace;
103 output({&ch, 1});
105 stack_.top().type = ct;
106 }
107
108 void
109 output(boost::beast::string_view const& bytes)
110 {
111 markStarted();
112 output_(bytes);
113 }
114
115 void
116 stringOutput(boost::beast::string_view const& bytes)
117 {
118 markStarted();
119 std::size_t position = 0, writtenUntil = 0;
120
121 output_({&quote, 1});
122 auto data = bytes.data();
123 for (; position < bytes.size(); ++position)
124 {
125 auto i = jsonSpecialCharacterEscape.find(data[position]);
126 if (i != jsonSpecialCharacterEscape.end())
127 {
128 if (writtenUntil < position)
129 {
130 output_({data + writtenUntil, position - writtenUntil});
131 }
132 output_({i->second, jsonEscapeLength});
133 writtenUntil = position + 1;
134 };
135 }
136 if (writtenUntil < position)
137 output_({data + writtenUntil, position - writtenUntil});
138 output_({&quote, 1});
139 }
140
141 void
143 {
144 check(!isFinished(), "isFinished() in output.");
145 isStarted_ = true;
146 }
147
148 void
150 {
151 check(!empty(), "empty () in " + message);
152
153 auto t = stack_.top().type;
154 if (t != type)
155 {
156 check(
157 false,
158 "Not an " +
159 ((type == array ? "array: " : "object: ") + message));
160 }
161 if (stack_.top().isFirst)
162 stack_.top().isFirst = false;
163 else
164 output_({&comma, 1});
165 }
166
167 void
169 {
170#ifndef NDEBUG
171 // Make sure we haven't already seen this tag.
172 auto& tags = stack_.top().tags;
173 check(tags.find(tag) == tags.end(), "Already seen tag " + tag);
174 tags.insert(tag);
175#endif
176
177 stringOutput(tag);
178 output_({&colon, 1});
179 }
180
181 bool
183 {
184 return isStarted_ && empty();
185 }
186
187 void
189 {
190 check(!empty(), "Empty stack in finish()");
191
192 auto isArray = stack_.top().type == array;
193 auto ch = isArray ? closeBracket : closeBrace;
194 output_({&ch, 1});
195 stack_.pop();
196 }
197
198 void
200 {
201 if (isStarted_)
202 {
203 while (!isFinished())
204 finish();
205 }
206 }
207
208 Output const&
209 getOutput() const
210 {
211 return output_;
212 }
213
214private:
215 // JSON collections are either arrrays, or objects.
217 {
218 explicit Collection() = default;
219
222
225 bool isFirst = true;
226
227#ifndef NDEBUG
230#endif
231 };
232
234
237
238 bool isStarted_ = false;
239};
240
241Writer::Writer(Output const& output) : impl_(std::make_unique<Impl>(output))
242{
243}
244
246{
247 if (impl_)
248 impl_->finishAll();
249}
250
252{
253 impl_ = std::move(w.impl_);
254}
255
256Writer&
258{
259 impl_ = std::move(w.impl_);
260 return *this;
261}
262
263void
264Writer::output(char const* s)
265{
266 impl_->stringOutput(s);
267}
268
269void
271{
272 impl_->stringOutput(s);
273}
274
275void
277{
278 impl_->markStarted();
279 outputJson(value, impl_->getOutput());
280}
281
282void
284{
285 auto s = ripple::to_string(f);
286 impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
287}
288
289void
291{
292 auto s = ripple::to_string(f);
293 impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
294}
295
296void
298{
299 impl_->output("null");
300}
301
302void
304{
305 impl_->output(b ? "true" : "false");
306}
307
308void
310{
311 impl_->output(s);
312}
313
314void
316{
317 if (impl_)
318 impl_->finishAll();
319}
320
321void
323{
324 impl_->nextCollectionEntry(array, "append");
325}
326
327void
329{
330 check(!tag.empty(), "Tag can't be empty");
331
332 impl_->nextCollectionEntry(object, "set");
333 impl_->writeObjectTag(tag);
334}
335
336void
338{
339 impl_->start(type);
340}
341
342void
344{
345 impl_->nextCollectionEntry(array, "startAppend");
346 impl_->start(type);
347}
348
349void
351{
352 impl_->nextCollectionEntry(object, "startSet");
353 impl_->writeObjectTag(key);
354 impl_->start(type);
355}
356
357void
359{
360 if (impl_)
361 impl_->finish();
362}
363
364} // namespace Json
Represents a JSON value.
Definition json_value.h:149
void nextCollectionEntry(CollectionType type, std::string const &message)
Definition Writer.cpp:149
bool isFinished() const
Definition Writer.cpp:182
void stringOutput(boost::beast::string_view const &bytes)
Definition Writer.cpp:116
Impl(Impl &&)=delete
bool empty() const
Definition Writer.cpp:94
void writeObjectTag(std::string const &tag)
Definition Writer.cpp:168
Impl & operator=(Impl &&)=delete
Output const & getOutput() const
Definition Writer.cpp:209
void output(boost::beast::string_view const &bytes)
Definition Writer.cpp:109
Impl(Output const &output)
Definition Writer.cpp:84
void start(CollectionType ct)
Definition Writer.cpp:100
Writer implements an O(1)-space, O(1)-granular output JSON writer.
Writer & operator=(Writer &&) noexcept
Definition Writer.cpp:257
void finish()
Finish the collection most recently started.
Definition Writer.cpp:358
void output(std::string const &)
Definition Writer.cpp:270
void implOutput(std::string const &)
Definition Writer.cpp:309
void startRoot(CollectionType)
Start a new collection at the root level.
Definition Writer.cpp:337
void rawAppend()
Add a comma before this next item if not the first item in an array.
Definition Writer.cpp:322
void rawSet(std::string const &key)
Emit just "tag": as part of an object.
Definition Writer.cpp:328
void finishAll()
Finish all objects and arrays.
Definition Writer.cpp:315
Writer(Output const &output)
Definition Writer.cpp:241
void startAppend(CollectionType)
Start a new collection inside an array.
Definition Writer.cpp:343
void startSet(CollectionType, std::string const &key)
Start a new collection inside an object.
Definition Writer.cpp:350
std::unique_ptr< Impl > impl_
T data(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
T find_last_not_of(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:25
void outputJson(Json::Value const &, Output const &)
Writes a minimal representation of a Json value to an Output in O(n) time.
Definition Output.cpp:93
void check(bool condition, std::string const &message)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
STL namespace.
T pop(T... args)
T push(T... args)
T size(T... args)
std::set< std::string > tags
What tags have we already seen in this collection?
Definition Writer.cpp:229
bool isFirst
Is this the first entry in a collection? If false, we have to emit a , before we write the next entry...
Definition Writer.cpp:225
Writer::CollectionType type
What type of collection are we in?
Definition Writer.cpp:221
T top(T... args)