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