// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__ #define GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__ #include #include #include #include #include #include #include #include #include #include #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "google/protobuf/io/strtod.h" #include "google/protobuf/io/zero_copy_sink.h" #include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/stubs/status_macros.h" // Must be included last. #include "google/protobuf/port_def.inc" namespace google { namespace protobuf { namespace json_internal { struct WriterOptions { // Whether to add spaces, line breaks and indentation to make the JSON output // easy to read. bool add_whitespace = false; // Whether to always print fields which do not support presence if they would // otherwise be omitted, namely: // - Implicit presence fields set to their 0 value // - Empty lists and maps bool always_print_fields_with_no_presence = false; // Whether to always print enums as ints. By default they are rendered as // strings. bool always_print_enums_as_ints = false; // Whether to preserve proto field names bool preserve_proto_field_names = false; // If set, int64 values that can be represented exactly as a double are // printed without quotes. bool unquote_int64_if_possible = false; // The original parser used by json_util2 accepted a number of non-standard // options. Setting this flag enables them. // // What those extensions were is explicitly not documented, beyond what exists // in the unit tests; we intend to remove this setting eventually. See // b/234868512. bool allow_legacy_syntax = false; }; template void EachInner(const Tuple& value, F f, std::index_sequence) { int ignored[] = { (f(std::get(value)), 0)...}; // NOLINT(readability/braces) (void)ignored; } // Executes f on each element of value. template void Each(const Tuple& value, F f) { EachInner(value, f, std::make_index_sequence::value>()); } // See JsonWriter::Write(). template struct Quoted { std::tuple value; }; // Because this is not C++17 yet, we cannot add a deduction guide. template static Quoted MakeQuoted(T... t) { return Quoted{std::make_tuple(t...)}; } class JsonWriter { public: JsonWriter(io::ZeroCopyOutputStream* out, WriterOptions options) : sink_(out), options_(options) {} const WriterOptions& options() const { return options_; } void Push() { ++indent_; } void Pop() { --indent_; } // The many overloads of Write() will write a value to the underlying stream. // Some values may want to be quoted; the Quoted<> type will automatically add // quotes and escape sequences. // // Note that Write() is not implemented for 64-bit integers, since they // cannot be crisply represented without quotes; use MakeQuoted for that. void Write(absl::string_view str) { sink_.Append(str.data(), str.size()); } void Write(char c) { sink_.Append(&c, 1); } // The precision on this and the following function are completely made-up, // in an attempt to match the behavior of the ESF parser. void Write(double val) { if (!MaybeWriteSpecialFp(val)) { Write(io::SimpleDtoa(val)); } } void Write(float val) { if (!MaybeWriteSpecialFp(val)) { Write(io::SimpleFtoa(val)); } } void Write(int32_t val) { char buf[22]; int len = absl::SNPrintF(buf, sizeof(buf), "%d", val); absl::string_view view(buf, static_cast(len)); Write(view); } void Write(uint32_t val) { char buf[22]; int len = absl::SNPrintF(buf, sizeof(buf), "%d", val); absl::string_view view(buf, static_cast(len)); Write(view); } void Write(int64_t val) { char buf[22]; int len = absl::SNPrintF(buf, sizeof(buf), "%d", val); absl::string_view view(buf, static_cast(len)); Write(view); } void Write(uint64_t val) { char buf[22]; int len = absl::SNPrintF(buf, sizeof(buf), "%d", val); absl::string_view view(buf, static_cast(len)); Write(view); } template void Write(Quoted val) { Write('"'); Each(val.value, [this](auto x) { this->WriteQuoted(x); }); Write('"'); } template auto Write(Ts... args) -> // This bit of SFINAE avoids this function being called with one argument, // so the other overloads of Write() can be picked up instead. typename std::enable_if::type { Each(std::make_tuple(args...), [this](auto x) { this->Write(x); }); } void Whitespace(absl::string_view ws) { if (options_.add_whitespace) { Write(ws); } } void NewLine() { Whitespace("\n"); for (int i = 0; i < indent_; ++i) { Whitespace(" "); } } void WriteComma(bool& is_first) { if (is_first) { is_first = false; return; } Write(","); } void WriteBase64(absl::string_view str); // Returns a buffer that can be re-used throughout a writing session as // variable-length scratch space. std::string& ScratchBuf() { return scratch_buf_; } private: template void WriteQuoted(T val) { Write(val); } void WriteQuoted(absl::string_view val) { WriteEscapedUtf8(val); } // Tries to write a non-finite double if necessary; returns false if // nothing was written. bool MaybeWriteSpecialFp(double val); void WriteEscapedUtf8(absl::string_view str); void WriteUEscape(uint16_t val); io::zc_sink_internal::ZeroCopyStreamByteSink sink_; WriterOptions options_; int indent_ = 0; std::string scratch_buf_; }; } // namespace json_internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_JSON_INTERNAL_WRITER_H__