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/json/Output.h>
21#include <xrpl/json/Writer.h>
22#include <set>
23#include <stack>
24
25namespace Json {
26
27namespace {
28
29std::map<char, const char*> jsonSpecialCharacterEscape = {
30 {'"', "\\\""},
31 {'\\', "\\\\"},
32 {'/', "\\/"},
33 {'\b', "\\b"},
34 {'\f', "\\f"},
35 {'\n', "\\n"},
36 {'\r', "\\r"},
37 {'\t', "\\t"}};
38
39static size_t const jsonEscapeLength = 2;
40
41// All other JSON punctuation.
42const char closeBrace = '}';
43const char closeBracket = ']';
44const char colon = ':';
45const char comma = ',';
46const char openBrace = '{';
47const char openBracket = '[';
48const char quote = '"';
49
50static auto const integralFloatsBecomeInts = false;
51
52size_t
53lengthWithoutTrailingZeros(std::string const& s)
54{
55 auto dotPos = s.find('.');
56 if (dotPos == std::string::npos)
57 return s.size();
58
59 auto lastNonZero = s.find_last_not_of('0');
60 auto hasDecimals = dotPos != lastNonZero;
61
62 if (hasDecimals)
63 return lastNonZero + 1;
64
65 if (integralFloatsBecomeInts || lastNonZero + 2 > s.size())
66 return lastNonZero;
67
68 return lastNonZero + 2;
69}
70
71} // namespace
72
74{
75public:
76 explicit Impl(Output const& output) : output_(output)
77 {
78 }
79 ~Impl() = default;
80
81 Impl(Impl&&) = delete;
82 Impl&
83 operator=(Impl&&) = delete;
84
85 bool
86 empty() const
87 {
88 return stack_.empty();
89 }
90
91 void
93 {
94 char ch = (ct == array) ? openBracket : openBrace;
95 output({&ch, 1});
97 stack_.top().type = ct;
98 }
99
100 void
101 output(boost::beast::string_view const& bytes)
102 {
103 markStarted();
104 output_(bytes);
105 }
106
107 void
108 stringOutput(boost::beast::string_view const& bytes)
109 {
110 markStarted();
111 std::size_t position = 0, writtenUntil = 0;
112
113 output_({&quote, 1});
114 auto data = bytes.data();
115 for (; position < bytes.size(); ++position)
116 {
117 auto i = jsonSpecialCharacterEscape.find(data[position]);
118 if (i != jsonSpecialCharacterEscape.end())
119 {
120 if (writtenUntil < position)
121 {
122 output_({data + writtenUntil, position - writtenUntil});
123 }
124 output_({i->second, jsonEscapeLength});
125 writtenUntil = position + 1;
126 };
127 }
128 if (writtenUntil < position)
129 output_({data + writtenUntil, position - writtenUntil});
130 output_({&quote, 1});
131 }
132
133 void
135 {
136 check(!isFinished(), "isFinished() in output.");
137 isStarted_ = true;
138 }
139
140 void
142 {
143 check(!empty(), "empty () in " + message);
144
145 auto t = stack_.top().type;
146 if (t != type)
147 {
148 check(
149 false,
150 "Not an " +
151 ((type == array ? "array: " : "object: ") + message));
152 }
153 if (stack_.top().isFirst)
154 stack_.top().isFirst = false;
155 else
156 output_({&comma, 1});
157 }
158
159 void
161 {
162#ifndef NDEBUG
163 // Make sure we haven't already seen this tag.
164 auto& tags = stack_.top().tags;
165 check(tags.find(tag) == tags.end(), "Already seen tag " + tag);
166 tags.insert(tag);
167#endif
168
169 stringOutput(tag);
170 output_({&colon, 1});
171 }
172
173 bool
175 {
176 return isStarted_ && empty();
177 }
178
179 void
181 {
182 check(!empty(), "Empty stack in finish()");
183
184 auto isArray = stack_.top().type == array;
185 auto ch = isArray ? closeBracket : closeBrace;
186 output_({&ch, 1});
187 stack_.pop();
188 }
189
190 void
192 {
193 if (isStarted_)
194 {
195 while (!isFinished())
196 finish();
197 }
198 }
199
200 Output const&
201 getOutput() const
202 {
203 return output_;
204 }
205
206private:
207 // JSON collections are either arrrays, or objects.
209 {
210 explicit Collection() = default;
211
214
217 bool isFirst = true;
218
219#ifndef NDEBUG
222#endif
223 };
224
226
229
230 bool isStarted_ = false;
231};
232
233Writer::Writer(Output const& output) : impl_(std::make_unique<Impl>(output))
234{
235}
236
238{
239 if (impl_)
240 impl_->finishAll();
241}
242
244{
245 impl_ = std::move(w.impl_);
246}
247
248Writer&
250{
251 impl_ = std::move(w.impl_);
252 return *this;
253}
254
255void
256Writer::output(char const* s)
257{
258 impl_->stringOutput(s);
259}
260
261void
263{
264 impl_->stringOutput(s);
265}
266
267void
269{
270 impl_->markStarted();
271 outputJson(value, impl_->getOutput());
272}
273
274void
276{
277 auto s = ripple::to_string(f);
278 impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
279}
280
281void
283{
284 auto s = ripple::to_string(f);
285 impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
286}
287
288void
290{
291 impl_->output("null");
292}
293
294void
296{
297 impl_->output(b ? "true" : "false");
298}
299
300void
302{
303 impl_->output(s);
304}
305
306void
308{
309 if (impl_)
310 impl_->finishAll();
311}
312
313void
315{
316 impl_->nextCollectionEntry(array, "append");
317}
318
319void
321{
322 check(!tag.empty(), "Tag can't be empty");
323
324 impl_->nextCollectionEntry(object, "set");
325 impl_->writeObjectTag(tag);
326}
327
328void
330{
331 impl_->start(type);
332}
333
334void
336{
337 impl_->nextCollectionEntry(array, "startAppend");
338 impl_->start(type);
339}
340
341void
343{
344 impl_->nextCollectionEntry(object, "startSet");
345 impl_->writeObjectTag(key);
346 impl_->start(type);
347}
348
349void
351{
352 if (impl_)
353 impl_->finish();
354}
355
356} // namespace Json
Represents a JSON value.
Definition: json_value.h:147
void markStarted()
Definition: Writer.cpp:134
void nextCollectionEntry(CollectionType type, std::string const &message)
Definition: Writer.cpp:141
bool isFinished() const
Definition: Writer.cpp:174
void stringOutput(boost::beast::string_view const &bytes)
Definition: Writer.cpp:108
Impl(Impl &&)=delete
bool empty() const
Definition: Writer.cpp:86
void writeObjectTag(std::string const &tag)
Definition: Writer.cpp:160
Impl & operator=(Impl &&)=delete
Output const & getOutput() const
Definition: Writer.cpp:201
void output(boost::beast::string_view const &bytes)
Definition: Writer.cpp:101
Impl(Output const &output)
Definition: Writer.cpp:76
void start(CollectionType ct)
Definition: Writer.cpp:92
Writer implements an O(1)-space, O(1)-granular output JSON writer.
Definition: json/Writer.h:127
Writer & operator=(Writer &&) noexcept
Definition: Writer.cpp:249
void finish()
Finish the collection most recently started.
Definition: Writer.cpp:350
void output(std::string const &)
Definition: Writer.cpp:262
void implOutput(std::string const &)
Definition: Writer.cpp:301
void startRoot(CollectionType)
Start a new collection at the root level.
Definition: Writer.cpp:329
void rawAppend()
Add a comma before this next item if not the first item in an array.
Definition: Writer.cpp:314
void rawSet(std::string const &key)
Emit just "tag": as part of an object.
Definition: Writer.cpp:320
void finishAll()
Finish all objects and arrays.
Definition: Writer.cpp:307
Writer(Output const &output)
Definition: Writer.cpp:233
void startAppend(CollectionType)
Start a new collection inside an array.
Definition: Writer.cpp:335
void startSet(CollectionType, std::string const &key)
Start a new collection inside an object.
Definition: Writer.cpp:342
std::unique_ptr< Impl > impl_
Definition: json/Writer.h:245
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:90
void check(bool condition, std::string const &message)
Definition: json/Writer.h:252
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
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:221
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:217
Writer::CollectionType type
What type of collection are we in?
Definition: Writer.cpp:213
T top(T... args)