rippled
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 <ripple/json/Output.h>
21 #include <ripple/json/Writer.h>
22 #include <set>
23 #include <stack>
24 
25 namespace Json {
26 
27 namespace {
28 
29 std::map<char, const char*> jsonSpecialCharacterEscape = {
30  {'"', "\\\""},
31  {'\\', "\\\\"},
32  {'/', "\\/"},
33  {'\b', "\\b"},
34  {'\f', "\\f"},
35  {'\n', "\\n"},
36  {'\r', "\\r"},
37  {'\t', "\\t"}};
38 
39 static size_t const jsonEscapeLength = 2;
40 
41 // All other JSON punctuation.
42 const char closeBrace = '}';
43 const char closeBracket = ']';
44 const char colon = ':';
45 const char comma = ',';
46 const char openBrace = '{';
47 const char openBracket = '[';
48 const char quote = '"';
49 
50 static auto const integralFloatsBecomeInts = false;
51 
52 size_t
53 lengthWithoutTrailingZeros(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 {
75 public:
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
174  isFinished() const
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 
206 private:
207  // JSON collections are either arrrays, or objects.
208  struct Collection
209  {
210  explicit Collection() = default;
211 
214 
217  bool isFirst = true;
218 
219 #ifndef NDEBUG
220 
222 #endif
223  };
224 
226 
229 
230  bool isStarted_ = false;
231 };
232 
233 Writer::Writer(Output const& output) : impl_(std::make_unique<Impl>(output))
234 {
235 }
236 
238 {
239  if (impl_)
240  impl_->finishAll();
241 }
242 
243 Writer::Writer(Writer&& w) noexcept
244 {
245  impl_ = std::move(w.impl_);
246 }
247 
248 Writer&
250 {
251  impl_ = std::move(w.impl_);
252  return *this;
253 }
254 
255 void
256 Writer::output(char const* s)
257 {
258  impl_->stringOutput(s);
259 }
260 
261 void
263 {
264  impl_->stringOutput(s);
265 }
266 
267 void
269 {
270  impl_->markStarted();
271  outputJson(value, impl_->getOutput());
272 }
273 
274 void
276 {
277  auto s = ripple::to_string(f);
278  impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
279 }
280 
281 void
282 Writer::output(double f)
283 {
284  auto s = ripple::to_string(f);
285  impl_->output({s.data(), lengthWithoutTrailingZeros(s)});
286 }
287 
289 {
290  impl_->output("null");
291 }
292 
293 void
295 {
296  impl_->output(b ? "true" : "false");
297 }
298 
299 void
301 {
302  impl_->output(s);
303 }
304 
305 void
307 {
308  if (impl_)
309  impl_->finishAll();
310 }
311 
312 void
314 {
315  impl_->nextCollectionEntry(array, "append");
316 }
317 
318 void
320 {
321  check(!tag.empty(), "Tag can't be empty");
322 
323  impl_->nextCollectionEntry(object, "set");
324  impl_->writeObjectTag(tag);
325 }
326 
327 void
329 {
330  impl_->start(type);
331 }
332 
333 void
335 {
336  impl_->nextCollectionEntry(array, "startAppend");
337  impl_->start(type);
338 }
339 
340 void
342 {
343  impl_->nextCollectionEntry(object, "startSet");
344  impl_->writeObjectTag(key);
345  impl_->start(type);
346 }
347 
348 void
350 {
351  if (impl_)
352  impl_->finish();
353 }
354 
355 } // namespace Json
Json::Writer::Impl::nextCollectionEntry
void nextCollectionEntry(CollectionType type, std::string const &message)
Definition: Writer.cpp:141
Json::Writer::rawAppend
void rawAppend()
Add a comma before this next item if not the first item in an array.
Definition: Writer.cpp:313
Json::Writer::implOutput
void implOutput(std::string const &)
Definition: Writer.cpp:300
Json::Writer::Impl::isStarted_
bool isStarted_
Definition: Writer.cpp:230
std::string
STL class.
Json::Writer::Impl::getOutput
Output const & getOutput() const
Definition: Writer.cpp:201
Json::Writer::Impl::Collection::tags
std::set< std::string > tags
What tags have we already seen in this collection?
Definition: Writer.cpp:221
Json::Writer::Impl::finishAll
void finishAll()
Definition: Writer.cpp:191
Json::Writer::Impl::Collection::isFirst
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
std::string::find_last_not_of
T find_last_not_of(T... args)
Json::Writer::Impl::Impl
Impl(Output const &output)
Definition: Writer.cpp:76
Json::Writer::finish
void finish()
Finish the collection most recently started.
Definition: Writer.cpp:349
Json::Writer::Impl::operator=
Impl & operator=(Impl &&)=delete
std::string::find
T find(T... args)
std::string::size
T size(T... args)
stack
Json::Writer::Impl::writeObjectTag
void writeObjectTag(std::string const &tag)
Definition: Writer.cpp:160
Json::check
void check(bool condition, std::string const &message)
Definition: json/Writer.h:252
Json::Writer::startAppend
void startAppend(CollectionType)
Start a new collection inside an array.
Definition: Writer.cpp:334
std::function
Json::Writer::startRoot
void startRoot(CollectionType)
Start a new collection at the root level.
Definition: Writer.cpp:328
Json::Writer::Impl::isFinished
bool isFinished() const
Definition: Writer.cpp:174
std::nullptr_t
Json::Writer::Impl::Collection::type
Writer::CollectionType type
What type of collection are we in?
Definition: Writer.cpp:213
Json::Writer::finishAll
void finishAll()
Finish all objects and arrays.
Definition: Writer.cpp:306
Json::outputJson
void outputJson(Json::Value const &value, Output const &out)
Writes a minimal representation of a Json value to an Output in O(n) time.
Definition: Output.cpp:90
Json
JSON (JavaScript Object Notation).
Definition: json_reader.cpp:29
Json::Writer::~Writer
~Writer()
Definition: Writer.cpp:237
Json::Writer::Impl::output_
Output output_
Definition: Writer.cpp:227
std::stack::pop
T pop(T... args)
std::stack::top
T top(T... args)
Json::Writer::impl_
std::unique_ptr< Impl > impl_
Definition: json/Writer.h:244
Json::Writer::array
@ array
Definition: json/Writer.h:129
std::map
STL class.
Json::Writer::Impl::start
void start(CollectionType ct)
Definition: Writer.cpp:92
Json::Writer::Impl::Collection::Collection
Collection()=default
Json::Writer::Impl::markStarted
void markStarted()
Definition: Writer.cpp:134
Json::Writer::Impl::~Impl
~Impl()=default
Json::Writer::startSet
void startSet(CollectionType, std::string const &key)
Start a new collection inside an object.
Definition: Writer.cpp:341
Json::Writer::operator=
Writer & operator=(Writer &&) noexcept
Definition: Writer.cpp:249
Json::Writer::Impl::stack_
Stack stack_
Definition: Writer.cpp:228
Json::Writer::Impl
Definition: Writer.cpp:73
std
STL namespace.
Json::Writer::rawSet
void rawSet(std::string const &key)
Emit just "tag": as part of an object.
Definition: Writer.cpp:319
std::stack::empty
T empty(T... args)
std::stack::push
T push(T... args)
Json::Writer::Impl::finish
void finish()
Definition: Writer.cpp:180
Json::Writer::Writer
Writer(Output const &output)
Definition: Writer.cpp:233
Json::Writer::Impl::empty
bool empty() const
Definition: Writer.cpp:86
Json::Writer::output
void output(std::string const &)
Definition: Writer.cpp:262
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
Json::Writer::Impl::output
void output(boost::beast::string_view const &bytes)
Definition: Writer.cpp:101
std::map::end
T end(T... args)
Json::Writer::Impl::Collection
Definition: Writer.cpp:208
Json::Writer::Impl::stringOutput
void stringOutput(boost::beast::string_view const &bytes)
Definition: Writer.cpp:108
Json::Writer::CollectionType
CollectionType
Definition: json/Writer.h:129
Json::Writer
Writer implements an O(1)-space, O(1)-granular output JSON writer.
Definition: json/Writer.h:126
std::string::data
T data(T... args)
set
Json::Value
Represents a JSON value.
Definition: json_value.h:145