219 lines
6.2 KiB
C++
219 lines
6.2 KiB
C++
// 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 <cfloat>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#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 <typename Tuple, typename F, size_t... i>
|
|
void EachInner(const Tuple& value, F f, std::index_sequence<i...>) {
|
|
int ignored[] = {
|
|
(f(std::get<i>(value)), 0)...}; // NOLINT(readability/braces)
|
|
(void)ignored;
|
|
}
|
|
|
|
// Executes f on each element of value.
|
|
template <typename Tuple, typename F>
|
|
void Each(const Tuple& value, F f) {
|
|
EachInner(value, f,
|
|
std::make_index_sequence<std::tuple_size<Tuple>::value>());
|
|
}
|
|
|
|
// See JsonWriter::Write().
|
|
template <typename... T>
|
|
struct Quoted {
|
|
std::tuple<T...> value;
|
|
};
|
|
|
|
// Because this is not C++17 yet, we cannot add a deduction guide.
|
|
template <typename... T>
|
|
static Quoted<T...> MakeQuoted(T... t) {
|
|
return Quoted<T...>{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<size_t>(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<size_t>(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<size_t>(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<size_t>(len));
|
|
Write(view);
|
|
}
|
|
|
|
template <typename... Ts>
|
|
void Write(Quoted<Ts...> val) {
|
|
Write('"');
|
|
Each(val.value, [this](auto x) { this->WriteQuoted(x); });
|
|
Write('"');
|
|
}
|
|
|
|
template <typename... Ts>
|
|
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<sizeof...(Ts) != 1, void>::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 <typename T>
|
|
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__
|