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