• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__
9 #define GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__
10 
11 #include <cfloat>
12 #include <cmath>
13 #include <cstdint>
14 #include <iostream>
15 #include <limits>
16 #include <ostream>
17 #include <string>
18 #include <tuple>
19 #include <type_traits>
20 #include <utility>
21 
22 #include "absl/strings/str_format.h"
23 #include "absl/strings/string_view.h"
24 #include "google/protobuf/io/strtod.h"
25 #include "google/protobuf/io/zero_copy_sink.h"
26 #include "google/protobuf/io/zero_copy_stream.h"
27 #include "google/protobuf/stubs/status_macros.h"
28 
29 // Must be included last.
30 #include "google/protobuf/port_def.inc"
31 
32 namespace google {
33 namespace protobuf {
34 namespace json_internal {
35 struct WriterOptions {
36   // Whether to add spaces, line breaks and indentation to make the JSON output
37   // easy to read.
38   bool add_whitespace = false;
39   // Whether to always print fields which do not support presence if they would
40   // otherwise be omitted, namely:
41   // - Implicit presence fields set to their 0 value
42   // - Empty lists and maps
43   bool always_print_fields_with_no_presence = false;
44   // Whether to always print enums as ints. By default they are rendered as
45   // strings.
46   bool always_print_enums_as_ints = false;
47   // Whether to preserve proto field names
48   bool preserve_proto_field_names = false;
49   // If set, int64 values that can be represented exactly as a double are
50   // printed without quotes.
51   bool unquote_int64_if_possible = false;
52   // The original parser used by json_util2 accepted a number of non-standard
53   // options. Setting this flag enables them.
54   //
55   // What those extensions were is explicitly not documented, beyond what exists
56   // in the unit tests; we intend to remove this setting eventually. See
57   // b/234868512.
58   bool allow_legacy_syntax = false;
59 };
60 
61 template <typename Tuple, typename F, size_t... i>
EachInner(const Tuple & value,F f,std::index_sequence<i...>)62 void EachInner(const Tuple& value, F f, std::index_sequence<i...>) {
63   int ignored[] = {
64       (f(std::get<i>(value)), 0)...};  // NOLINT(readability/braces)
65   (void)ignored;
66 }
67 
68 // Executes f on each element of value.
69 template <typename Tuple, typename F>
Each(const Tuple & value,F f)70 void Each(const Tuple& value, F f) {
71   EachInner(value, f,
72             std::make_index_sequence<std::tuple_size<Tuple>::value>());
73 }
74 
75 // See JsonWriter::Write().
76 template <typename... T>
77 struct Quoted {
78   std::tuple<T...> value;
79 };
80 
81 // Because this is not C++17 yet, we cannot add a deduction guide.
82 template <typename... T>
MakeQuoted(T...t)83 static Quoted<T...> MakeQuoted(T... t) {
84   return Quoted<T...>{std::make_tuple(t...)};
85 }
86 
87 class JsonWriter {
88  public:
JsonWriter(io::ZeroCopyOutputStream * out,WriterOptions options)89   JsonWriter(io::ZeroCopyOutputStream* out, WriterOptions options)
90       : sink_(out), options_(options) {}
91 
options()92   const WriterOptions& options() const { return options_; }
93 
Push()94   void Push() { ++indent_; }
Pop()95   void Pop() { --indent_; }
96 
97   // The many overloads of Write() will write a value to the underlying stream.
98   // Some values may want to be quoted; the Quoted<> type will automatically add
99   // quotes and escape sequences.
100   //
101   // Note that Write() is not implemented for 64-bit integers, since they
102   // cannot be crisply represented without quotes; use MakeQuoted for that.
103 
Write(absl::string_view str)104   void Write(absl::string_view str) { sink_.Append(str.data(), str.size()); }
105 
Write(char c)106   void Write(char c) { sink_.Append(&c, 1); }
107 
108   // The precision on this and the following function are completely made-up,
109   // in an attempt to match the behavior of the ESF parser.
Write(double val)110   void Write(double val) {
111     if (!MaybeWriteSpecialFp(val)) {
112       Write(io::SimpleDtoa(val));
113     }
114   }
115 
Write(float val)116   void Write(float val) {
117     if (!MaybeWriteSpecialFp(val)) {
118       Write(io::SimpleFtoa(val));
119     }
120   }
121 
Write(int32_t val)122   void Write(int32_t val) {
123     char buf[22];
124     int len = absl::SNPrintF(buf, sizeof(buf), "%d", val);
125     absl::string_view view(buf, static_cast<size_t>(len));
126     Write(view);
127   }
128 
Write(uint32_t val)129   void Write(uint32_t val) {
130     char buf[22];
131     int len = absl::SNPrintF(buf, sizeof(buf), "%d", val);
132     absl::string_view view(buf, static_cast<size_t>(len));
133     Write(view);
134   }
135 
Write(int64_t val)136   void Write(int64_t val) {
137     char buf[22];
138     int len = absl::SNPrintF(buf, sizeof(buf), "%d", val);
139     absl::string_view view(buf, static_cast<size_t>(len));
140     Write(view);
141   }
142 
Write(uint64_t val)143   void Write(uint64_t val) {
144     char buf[22];
145     int len = absl::SNPrintF(buf, sizeof(buf), "%d", val);
146     absl::string_view view(buf, static_cast<size_t>(len));
147     Write(view);
148   }
149 
150   template <typename... Ts>
Write(Quoted<Ts...> val)151   void Write(Quoted<Ts...> val) {
152     Write('"');
153     Each(val.value, [this](auto x) { this->WriteQuoted(x); });
154     Write('"');
155   }
156 
157   template <typename... Ts>
158   auto Write(Ts... args) ->
159       // This bit of SFINAE avoids this function being called with one argument,
160       // so the other overloads of Write() can be picked up instead.
161       typename std::enable_if<sizeof...(Ts) != 1, void>::type {
162     Each(std::make_tuple(args...), [this](auto x) { this->Write(x); });
163   }
164 
Whitespace(absl::string_view ws)165   void Whitespace(absl::string_view ws) {
166     if (options_.add_whitespace) {
167       Write(ws);
168     }
169   }
170 
NewLine()171   void NewLine() {
172     Whitespace("\n");
173     for (int i = 0; i < indent_; ++i) {
174       Whitespace(" ");
175     }
176   }
177 
WriteComma(bool & is_first)178   void WriteComma(bool& is_first) {
179     if (is_first) {
180       is_first = false;
181       return;
182     }
183     Write(",");
184   }
185 
186   void WriteBase64(absl::string_view str);
187 
188   // Returns a buffer that can be re-used throughout a writing session as
189   // variable-length scratch space.
ScratchBuf()190   std::string& ScratchBuf() { return scratch_buf_; }
191 
192  private:
193   template <typename T>
WriteQuoted(T val)194   void WriteQuoted(T val) {
195     Write(val);
196   }
197 
WriteQuoted(absl::string_view val)198   void WriteQuoted(absl::string_view val) { WriteEscapedUtf8(val); }
199 
200   // Tries to write a non-finite double if necessary; returns false if
201   // nothing was written.
202   bool MaybeWriteSpecialFp(double val);
203 
204   void WriteEscapedUtf8(absl::string_view str);
205   void WriteUEscape(uint16_t val);
206 
207   io::zc_sink_internal::ZeroCopyStreamByteSink sink_;
208   WriterOptions options_;
209   int indent_ = 0;
210 
211   std::string scratch_buf_;
212 };
213 }  // namespace json_internal
214 }  // namespace protobuf
215 }  // namespace google
216 
217 #include "google/protobuf/port_undef.inc"
218 #endif  // GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__
219