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 <stack>
23 #include <set>
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 
40 static size_t const jsonEscapeLength = 2;
41 
42 // All other JSON punctuation.
43 const char closeBrace = '}';
44 const char closeBracket = ']';
45 const char colon = ':';
46 const char comma = ',';
47 const char openBrace = '{';
48 const char openBracket = '[';
49 const char quote = '"';
50 
51 const std::string none;
52 
53 static auto const integralFloatsBecomeInts = false;
54 
55 size_t 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
79  Impl (Output const& output) : output_(output) {}
80  ~Impl() = default;
81 
82  Impl(Impl&&) = delete;
83  Impl& operator=(Impl&&) = delete;
84 
85  bool empty() const { return stack_.empty (); }
86 
88  {
89  char ch = (ct == array) ? openBracket : openBrace;
90  output ({&ch, 1});
92  stack_.top().type = ct;
93  }
94 
95  void output (boost::beast::string_view const& bytes)
96  {
97  markStarted ();
98  output_ (bytes);
99  }
100 
101  void stringOutput (boost::beast::string_view const& bytes)
102  {
103  markStarted ();
104  std::size_t position = 0, writtenUntil = 0;
105 
106  output_ ({&quote, 1});
107  auto data = bytes.data();
108  for (; position < bytes.size(); ++position)
109  {
110  auto i = jsonSpecialCharacterEscape.find (data[position]);
111  if (i != jsonSpecialCharacterEscape.end ())
112  {
113  if (writtenUntil < position)
114  {
115  output_ ({data + writtenUntil, position - writtenUntil});
116  }
117  output_ ({i->second, jsonEscapeLength});
118  writtenUntil = position + 1;
119  };
120  }
121  if (writtenUntil < position)
122  output_ ({data + writtenUntil, position - writtenUntil});
123  output_ ({&quote, 1});
124  }
125 
126  void markStarted ()
127  {
128  check (!isFinished(), "isFinished() in output.");
129  isStarted_ = true;
130  }
131 
132  void nextCollectionEntry (CollectionType type, std::string const& message)
133  {
134  check (!empty() , "empty () in " + message);
135 
136  auto t = stack_.top ().type;
137  if (t != type)
138  {
139  check (false, "Not an " +
140  ((type == array ? "array: " : "object: ") + message));
141  }
142  if (stack_.top ().isFirst)
143  stack_.top ().isFirst = false;
144  else
145  output_ ({&comma, 1});
146  }
147 
148  void writeObjectTag (std::string const& tag)
149  {
150 #ifndef NDEBUG
151  // Make sure we haven't already seen this tag.
152  auto& tags = stack_.top ().tags;
153  check (tags.find (tag) == tags.end (), "Already seen tag " + tag);
154  tags.insert (tag);
155 #endif
156 
157  stringOutput (tag);
158  output_ ({&colon, 1});
159  }
160 
161  bool isFinished() const
162  {
163  return isStarted_ && empty();
164  }
165 
166  void finish ()
167  {
168  check (!empty(), "Empty stack in finish()");
169 
170  auto isArray = stack_.top().type == array;
171  auto ch = isArray ? closeBracket : closeBrace;
172  output_ ({&ch, 1});
173  stack_.pop();
174  }
175 
176  void finishAll ()
177  {
178  if (isStarted_)
179  {
180  while (!isFinished())
181  finish();
182  }
183  }
184 
185  Output const& getOutput() const { return output_; }
186 
187 private:
188  // JSON collections are either arrrays, or objects.
189  struct Collection
190  {
191  explicit Collection() = default;
192 
195 
198  bool isFirst = true;
199 
200 #ifndef NDEBUG
201 
203 #endif
204  };
205 
207 
210 
211  bool isStarted_ = false;
212 };
213 
214 Writer::Writer (Output const &output)
215  : impl_(std::make_unique <Impl> (output))
216 {
217 }
218 
220 {
221  if (impl_)
222  impl_->finishAll ();
223 }
224 
225 Writer::Writer(Writer&& w) noexcept
226 {
227  impl_ = std::move (w.impl_);
228 }
229 
231 {
232  impl_ = std::move (w.impl_);
233  return *this;
234 }
235 
236 void Writer::output (char const* s)
237 {
238  impl_->stringOutput (s);
239 }
240 
242 {
243  impl_->stringOutput (s);
244 }
245 
246 void Writer::output (Json::Value const& value)
247 {
248  impl_->markStarted();
249  outputJson (value, impl_->getOutput());
250 }
251 
252 void Writer::output (float f)
253 {
254  auto s = ripple::to_string (f);
255  impl_->output ({s.data (), lengthWithoutTrailingZeros (s)});
256 }
257 
258 void Writer::output (double f)
259 {
260  auto s = ripple::to_string (f);
261  impl_->output ({s.data (), lengthWithoutTrailingZeros (s)});
262 }
263 
265 {
266  impl_->output ("null");
267 }
268 
269 void Writer::output (bool b)
270 {
271  impl_->output (b ? "true" : "false");
272 }
273 
275 {
276  impl_->output (s);
277 }
278 
280 {
281  if (impl_)
282  impl_->finishAll ();
283 }
284 
286 {
287  impl_->nextCollectionEntry (array, "append");
288 }
289 
290 void Writer::rawSet (std::string const& tag)
291 {
292  check (!tag.empty(), "Tag can't be empty");
293 
294  impl_->nextCollectionEntry (object, "set");
295  impl_->writeObjectTag (tag);
296 }
297 
299 {
300  impl_->start (type);
301 }
302 
304 {
305  impl_->nextCollectionEntry (array, "startAppend");
306  impl_->start (type);
307 }
308 
310 {
311  impl_->nextCollectionEntry (object, "startSet");
312  impl_->writeObjectTag (key);
313  impl_->start (type);
314 }
315 
317 {
318  if (impl_)
319  impl_->finish ();
320 }
321 
322 } // Json
Json::Writer::Impl::nextCollectionEntry
void nextCollectionEntry(CollectionType type, std::string const &message)
Definition: Writer.cpp:132
Json::Writer::rawAppend
void rawAppend()
Add a comma before this next item if not the first item in an array.
Definition: Writer.cpp:285
Json::Writer::implOutput
void implOutput(std::string const &)
Definition: Writer.cpp:274
Json::Writer::Impl::isStarted_
bool isStarted_
Definition: Writer.cpp:211
std::string
STL class.
Json::Writer::Impl::getOutput
Output const & getOutput() const
Definition: Writer.cpp:185
Json::Writer::Impl::Collection::tags
std::set< std::string > tags
What tags have we already seen in this collection?
Definition: Writer.cpp:202
Json::Writer::Impl::finishAll
void finishAll()
Definition: Writer.cpp:176
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:198
std::string::find_last_not_of
T find_last_not_of(T... args)
Json::Writer::Impl::Impl
Impl(Output const &output)
Definition: Writer.cpp:79
Json::Writer::finish
void finish()
Finish the collection most recently started.
Definition: Writer.cpp:316
Json::Writer::Impl::operator=
Impl & operator=(Impl &&)=delete
std::string::find
T find(T... args)
std::string::size
T size(T... args)
ripple::CashFilter::none
@ none
stack
Json::Writer::Impl::writeObjectTag
void writeObjectTag(std::string const &tag)
Definition: Writer.cpp:148
Json::check
void check(bool condition, std::string const &message)
Definition: json/Writer.h:234
Json::Writer::startAppend
void startAppend(CollectionType)
Start a new collection inside an array.
Definition: Writer.cpp:303
std::function
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
Json::Writer::startRoot
void startRoot(CollectionType)
Start a new collection at the root level.
Definition: Writer.cpp:298
Json::Writer::Impl::isFinished
bool isFinished() const
Definition: Writer.cpp:161
std::nullptr_t
Json::Writer::Impl::Collection::type
Writer::CollectionType type
What type of collection are we in?
Definition: Writer.cpp:194
Json::Writer::finishAll
void finishAll()
Finish all objects and arrays.
Definition: Writer.cpp:279
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:96
Json
JSON (JavaScript Object Notation).
Definition: json_reader.cpp:26
Json::Writer::~Writer
~Writer()
Definition: Writer.cpp:219
Json::Writer::Impl::output_
Output output_
Definition: Writer.cpp:208
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:228
Json::Writer::array
@ array
Definition: json/Writer.h:129
std::map
STL class.
Json::Writer::Impl::start
void start(CollectionType ct)
Definition: Writer.cpp:87
Json::Writer::Impl::Collection::Collection
Collection()=default
Json::Writer::Impl::markStarted
void markStarted()
Definition: Writer.cpp:126
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:309
Json::Writer::operator=
Writer & operator=(Writer &&) noexcept
Definition: Writer.cpp:230
Json::Writer::Impl::stack_
Stack stack_
Definition: Writer.cpp:209
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:290
std::stack::empty
T empty(T... args)
std::stack::push
T push(T... args)
Json::Writer::Impl::finish
void finish()
Definition: Writer.cpp:166
Json::Writer::Writer
Writer(Output const &output)
Definition: Writer.cpp:214
Json::Writer::Impl::empty
bool empty() const
Definition: Writer.cpp:85
Json::Writer::output
void output(std::string const &)
Definition: Writer.cpp:241
std::size_t
Json::Writer::Impl::output
void output(boost::beast::string_view const &bytes)
Definition: Writer.cpp:95
std::map::end
T end(T... args)
Json::Writer::Impl::Collection
Definition: Writer.cpp:189
Json::Writer::Impl::stringOutput
void stringOutput(boost::beast::string_view const &bytes)
Definition: Writer.cpp:101
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:141