From e47a228a868018791eebc7d12665f5a589b62a27 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Mon, 23 Jul 2018 16:39:30 -0700 Subject: [PATCH] common/formatter: json formattable is now also a formatter This way we can construct a formattable structure by encoding objects into it. Signed-off-by: Yehuda Sadeh --- src/common/Formatter.cc | 74 +++++++----- src/common/Formatter.h | 39 ++++++- src/common/ceph_json.cc | 88 ++++++++++----- src/common/ceph_json.h | 137 ++++++++++++++++++----- src/test/common/test_json_formattable.cc | 3 + 5 files changed, 257 insertions(+), 84 deletions(-) diff --git a/src/common/Formatter.cc b/src/common/Formatter.cc index 34c81317263..2a013e18d6b 100644 --- a/src/common/Formatter.cc +++ b/src/common/Formatter.cc @@ -186,9 +186,18 @@ void JSONFormatter::print_name(const char *name) ++entry.size; } -void JSONFormatter::open_section(const char *name, bool is_array) +void JSONFormatter::open_section(const char *name, const char *ns, bool is_array) { - print_name(name); + if (handle_open_section(name, ns, is_array)) { + return; + } + if (ns) { + std::ostringstream oss; + oss << name << " " << ns; + print_name(oss.str().c_str()); + } else { + print_name(name); + } if (is_array) m_ss << '['; else @@ -201,30 +210,30 @@ void JSONFormatter::open_section(const char *name, bool is_array) void JSONFormatter::open_array_section(const char *name) { - open_section(name, true); + open_section(name, nullptr, true); } void JSONFormatter::open_array_section_in_ns(const char *name, const char *ns) { - std::ostringstream oss; - oss << name << " " << ns; - open_section(oss.str().c_str(), true); + open_section(name, ns, true); } void JSONFormatter::open_object_section(const char *name) { - open_section(name, false); + open_section(name, nullptr, false); } void JSONFormatter::open_object_section_in_ns(const char *name, const char *ns) { - std::ostringstream oss; - oss << name << " " << ns; - open_section(oss.str().c_str(), false); + open_section(name, ns, false); } void JSONFormatter::close_section() { + + if (handle_close_section()) { + return; + } ceph_assert(!m_stack.empty()); finish_pending_string(); @@ -243,41 +252,57 @@ void JSONFormatter::close_section() void JSONFormatter::finish_pending_string() { if (m_is_pending_string) { - print_quoted_string(m_pending_string.str()); - m_pending_string.str(std::string()); m_is_pending_string = false; + add_value(m_pending_name.c_str(), m_pending_string.str(), true); } } -void JSONFormatter::dump_unsigned(const char *name, uint64_t u) +template +void JSONFormatter::add_value(const char *name, T val) +{ + std::stringstream ss; + ss << val; + add_value(name, ss.str(), false); +} + +void JSONFormatter::add_value(const char *name, std::string_view val, bool quoted) { + if (handle_value(name, val, quoted)) { + return; + } print_name(name); - m_ss << u; + if (!quoted) { + m_ss << val; + } else { + print_quoted_string(val); + } +} + +void JSONFormatter::dump_unsigned(const char *name, uint64_t u) +{ + add_value(name, u); } void JSONFormatter::dump_int(const char *name, int64_t s) { - print_name(name); - m_ss << s; + add_value(name, s); } void JSONFormatter::dump_float(const char *name, double d) { - print_name(name); char foo[30]; snprintf(foo, sizeof(foo), "%lf", d); - m_ss << foo; + add_value(name, foo, false); } void JSONFormatter::dump_string(const char *name, std::string_view s) { - print_name(name); - print_quoted_string(s); + add_value(name, s, true); } std::ostream& JSONFormatter::dump_stream(const char *name) { - print_name(name); + m_pending_name = name; m_is_pending_string = true; return m_pending_string; } @@ -287,12 +312,7 @@ void JSONFormatter::dump_format_va(const char *name, const char *ns, bool quoted char buf[LARGE_SIZE]; vsnprintf(buf, LARGE_SIZE, fmt, ap); - print_name(name); - if (quoted) { - print_quoted_string(std::string(buf)); - } else { - m_ss << std::string(buf); - } + add_value(name, buf, quoted); } int JSONFormatter::get_len() const diff --git a/src/common/Formatter.h b/src/common/Formatter.h index 4a9ac3210e0..9d984c11f87 100644 --- a/src/common/Formatter.h +++ b/src/common/Formatter.h @@ -110,6 +110,18 @@ namespace ceph { } }; + class copyable_sstream : public std::stringstream { + public: + copyable_sstream() {} + copyable_sstream(const copyable_sstream& rhs) { + str(rhs.str()); + } + copyable_sstream& operator=(const copyable_sstream& rhs) { + str(rhs.str()); + return *this; + } + }; + class JSONFormatter : public Formatter { public: explicit JSONFormatter(bool p = false); @@ -135,6 +147,19 @@ namespace ceph { int get_len() const override; void write_raw_data(const char *data) override; + protected: + virtual bool handle_value(const char *name, std::string_view s, bool quoted) { + return false; /* is handling done? */ + } + + virtual bool handle_open_section(const char *name, const char *ns, bool is_array) { + return false; /* is handling done? */ + } + + virtual bool handle_close_section() { + return false; /* is handling done? */ + } + private: struct json_formatter_stack_entry_d { @@ -144,18 +169,27 @@ namespace ceph { }; bool m_pretty; - void open_section(const char *name, bool is_array); + void open_section(const char *name, const char *ns, bool is_array); void print_quoted_string(std::string_view s); void print_name(const char *name); void print_comma(json_formatter_stack_entry_d& entry); void finish_pending_string(); - std::stringstream m_ss, m_pending_string; + template + void add_value(const char *name, T val); + void add_value(const char *name, std::string_view val, bool quoted); + + copyable_sstream m_ss; + copyable_sstream m_pending_string; + std::string m_pending_name; std::list m_stack; bool m_is_pending_string; bool m_line_break_enabled = false; }; + template + void add_value(const char *name, T val); + class XMLFormatter : public Formatter { public: static const char *XML_1_DTD; @@ -254,6 +288,5 @@ namespace ceph { std::vector< std::string > m_column_name; }; - } #endif diff --git a/src/common/ceph_json.cc b/src/common/ceph_json.cc index 612ecccbfba..0203f51f988 100644 --- a/src/common/ceph_json.cc +++ b/src/common/ceph_json.cc @@ -7,6 +7,9 @@ #include #include +#include + +#include "json_spirit/json_spirit_writer_template.h" using namespace json_spirit; @@ -16,6 +19,15 @@ using namespace json_spirit; static JSONFormattable default_formattable; +void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f) +{ + if (v.quoted) { + encode_json(name, v.str, f); + } else { + f->dump_format_unquoted(name, "%s", v.str.c_str()); + } +} + JSONObjIter::JSONObjIter() { } @@ -43,7 +55,7 @@ JSONObj *JSONObjIter::operator*() // does not work, FIXME ostream& operator<<(ostream &out, const JSONObj &obj) { - out << obj.name << ": " << obj.data_string; + out << obj.name << ": " << obj.val; return out; } @@ -62,9 +74,9 @@ void JSONObj::add_child(string el, JSONObj *obj) children.insert(pair(el, obj)); } -bool JSONObj::get_attr(string name, string& attr) +bool JSONObj::get_attr(string name, data_val& attr) { - map::iterator iter = attr_map.find(name); + auto iter = attr_map.find(name); if (iter == attr_map.end()) return false; attr = iter->second; @@ -109,13 +121,13 @@ JSONObj *JSONObj::find_obj(const string& name) return *iter; } -bool JSONObj::get_data(const string& key, string *dest) +bool JSONObj::get_data(const string& key, data_val *dest) { JSONObj *obj = find_obj(key); if (!obj) return false; - *dest = obj->get_data(); + *dest = obj->get_data_val(); return true; } @@ -158,11 +170,15 @@ void JSONObj::init(JSONObj *p, Value v, string n) data = v; handle_value(v); - if (v.type() == str_type) - data_string = v.get_str(); - else - data_string = write(v, raw_utf8); - attr_map.insert(pair(name, data_string)); + if (v.type() != obj_type && + v.type() != array_type) { + if (v.type() == str_type) { + val.set(v.get_str(), true); + } else { + val.set(json_spirit::write_string(v), false); + } + } + attr_map.insert(pair(name, val)); } JSONObj *JSONObj::get_parent() @@ -230,10 +246,11 @@ bool JSONParser::parse(const char *buf_, int len) handle_value(data); if (data.type() != obj_type && data.type() != array_type) { - if (data.type() == str_type) - data_string = data.get_str(); - else - data_string = write(data, raw_utf8); + if (data.type() == str_type) { + val.set(data.get_str(), true); + } else { + val.set(json_spirit::write_string(data), false); + } } } else { set_failure(); @@ -590,14 +607,14 @@ bool JSONFormattable::find(const string& name, string *val) const } int JSONFormattable::val_int() const { - return atoi(str.c_str()); + return atoi(value.str.c_str()); } bool JSONFormattable::val_bool() const { - return (boost::iequals(str, "true") || - boost::iequals(str, "on") || - boost::iequals(str, "yes") || - boost::iequals(str, "1")); + return (boost::iequals(value.str, "true") || + boost::iequals(value.str, "on") || + boost::iequals(value.str, "yes") || + boost::iequals(value.str, "1")); } string JSONFormattable::def(const string& def_val) const { @@ -685,6 +702,16 @@ static int parse_entity(const string& s, vector *result) return 0; } +static bool is_numeric(const string& val) +{ + try { + boost::lexical_cast(val); + } catch (const boost::bad_lexical_cast& e) { + return false; + } + return true; +} + int JSONFormattable::set(const string& name, const string& val) { boost::escaped_list_separator els('\\', '.', '"'); @@ -740,8 +767,8 @@ int JSONFormattable::set(const string& name, const string& val) if (is_valid_json) { f->decode_json(&jp); } else { - f->type = FMT_STRING; - f->str = val; + f->type = FMT_VALUE; + f->value.set(val, !is_numeric(val)); } return 0; @@ -764,7 +791,7 @@ int JSONFormattable::erase(const string& name) } for (const auto& vi : v) { if (f->type == FMT_NONE || - f->type == FMT_STRING) { + f->type == FMT_VALUE) { if (vi.is_obj) { f->type = FMT_OBJ; } else { @@ -831,17 +858,22 @@ void JSONFormattable::derive_from(const JSONFormattable& parent) void encode_json(const char *name, const JSONFormattable& v, Formatter *f) { - switch (v.type) { - case JSONFormattable::FMT_STRING: - encode_json(name, v.str, f); + v.encode_json(name, f); +} + +void JSONFormattable::encode_json(const char *name, Formatter *f) const +{ + switch (type) { + case JSONFormattable::FMT_VALUE: + ::encode_json(name, value, f); break; case JSONFormattable::FMT_ARRAY: - encode_json(name, v.arr, f); + ::encode_json(name, arr, f); break; case JSONFormattable::FMT_OBJ: f->open_object_section(name); - for (auto iter : v.obj) { - encode_json(iter.first.c_str(), iter.second, f); + for (auto iter : obj) { + ::encode_json(iter.first.c_str(), iter.second, f); } f->close_section(); break; diff --git a/src/common/ceph_json.h b/src/common/ceph_json.h index d021f888b91..195284c60d3 100644 --- a/src/common/ceph_json.h +++ b/src/common/ceph_json.h @@ -43,12 +43,23 @@ public: class JSONObj { JSONObj *parent; +public: + struct data_val { + string str; + bool quoted{false}; + + void set(std::string_view s, bool q) { + str = s; + quoted = q; + } + }; protected: string name; // corresponds to obj_type in XMLObj Value data; - string data_string; + struct data_val val; + bool data_quoted{false}; multimap children; - map attr_map; + map attr_map; void handle_value(Value v); public: @@ -60,11 +71,12 @@ public: void init(JSONObj *p, Value v, string n); string& get_name() { return name; } - string& get_data() { return data_string; } - bool get_data(const string& key, string *dest); + data_val& get_data_val() { return val; } + const string& get_data() { return val.str; } + bool get_data(const string& key, data_val *dest); JSONObj *get_parent(); void add_child(string el, JSONObj *child); - bool get_attr(string name, string& attr); + bool get_attr(string name, data_val& attr); JSONObjIter find(const string& name); JSONObjIter find_first(); JSONObjIter find_first(const string& name); @@ -78,6 +90,12 @@ public: vector get_array_elements(); }; +static inline ostream& operator<<(ostream &out, const JSONObj::data_val& dv) { + const char *q = (dv.quoted ? "\"" : ""); + out << q << dv.str << q; + return out; +} + class JSONParser : public JSONObj { int buf_len; @@ -97,6 +115,7 @@ public: void set_failure() { success = false; } }; +void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f); class JSONDecoder { public: @@ -136,6 +155,11 @@ static inline void decode_json_obj(string& val, JSONObj *obj) val = obj->get_data(); } +static inline void decode_json_obj(JSONObj::data_val& val, JSONObj *obj) +{ + val = obj->get_data_val(); +} + void decode_json_obj(unsigned long long& val, JSONObj *obj); void decode_json_obj(long long& val, JSONObj *obj); void decode_json_obj(unsigned long& val, JSONObj *obj); @@ -467,56 +491,117 @@ void encode_json_map(const char *name, const char *index_name, const char *value encode_json_map(name, index_name, NULL, value_name, NULL, NULL, m, f); } -struct JSONFormattable { +class JSONFormattable : public ceph::JSONFormatter { + JSONObj::data_val value; + vector arr; + map obj; + + vector enc_stack; + JSONFormattable *cur_enc; + +protected: + bool handle_value(const char *name, std::string_view s, bool quoted) override { + JSONFormattable *new_val; + if (cur_enc->is_array()) { + cur_enc->arr.push_back(JSONFormattable()); + new_val = &cur_enc->arr.back(); + } else { + new_val = &cur_enc->obj[name]; + } + new_val->set_type(JSONFormattable::FMT_VALUE); + new_val->value.set(s, quoted); + + return false; + } + bool handle_open_section(const char *name, const char *ns, bool section_is_array) override { + if (cur_enc->is_array()) { + cur_enc->arr.push_back(JSONFormattable()); + cur_enc = &cur_enc->arr.back(); + } else { + cur_enc = &obj[name]; + } + enc_stack.push_back(cur_enc); + + if (section_is_array) { + cur_enc->set_type(JSONFormattable::FMT_ARRAY); + } else { + cur_enc->set_type(JSONFormattable::FMT_OBJ); + } + + return false; /* continue processing */ + } + bool handle_close_section() override { + if (enc_stack.size() <= 1) { + return false; + } + + enc_stack.pop_back(); + cur_enc = enc_stack.back(); + return false; /* continue processing */ + } + +public: + JSONFormattable(bool p = false) : JSONFormatter(p) { + cur_enc = this; + enc_stack.push_back(cur_enc); + } + enum Type { FMT_NONE, - FMT_STRING, + FMT_VALUE, FMT_ARRAY, FMT_OBJ, } type{FMT_NONE}; - string str; - vector arr; - map obj; + void set_type(Type t) { + type = t; + } + void decode_json(JSONObj *jo) { if (jo->is_array()) { - type = JSONFormattable::FMT_ARRAY; + set_type(JSONFormattable::FMT_ARRAY); decode_json_obj(arr, jo); } else if (jo->is_object()) { - type = JSONFormattable::FMT_OBJ; + set_type(JSONFormattable::FMT_OBJ); auto iter = jo->find_first(); for (;!iter.end(); ++iter) { JSONObj *field = *iter; decode_json_obj(obj[field->get_name()], field); } } else { - type = JSONFormattable::FMT_STRING; - decode_json_obj(str, jo); + set_type(JSONFormattable::FMT_VALUE); + decode_json_obj(value, jo); } } void encode(bufferlist& bl) const { - ENCODE_START(1, 1, bl); + ENCODE_START(2, 1, bl); encode((uint8_t)type, bl); - encode(str, bl); + encode(value.str, bl); encode(arr, bl); encode(obj, bl); + encode(value.quoted, bl); ENCODE_FINISH(bl); } void decode(bufferlist::const_iterator& bl) { - DECODE_START(1, bl); + DECODE_START(2, bl); uint8_t t; decode(t, bl); type = (Type)t; - decode(str, bl); + decode(value.str, bl); decode(arr, bl); decode(obj, bl); + if (struct_v >= 2) { + decode(value.quoted, bl); + } else { + value.quoted = true; + } DECODE_FINISH(bl); } const std::string& val() const { - return str; + return value.str; } int val_int() const; @@ -533,7 +618,7 @@ struct JSONFormattable { JSONFormattable& operator[](size_t index); operator std::string() const { - return str; + return value.str; } explicit operator int() const { @@ -558,12 +643,6 @@ struct JSONFormattable { return def(string(def_val)); } -#if 0 - string operator ()(const string& def_val) const { - return def(def_val); - } -#endif - int operator()(int def_val) const { return def(def_val); } @@ -590,6 +669,12 @@ struct JSONFormattable { int erase(const string& name); void derive_from(const JSONFormattable& jf); + + void encode_json(const char *name, Formatter *f) const; + + bool is_array() const { + return (type == FMT_ARRAY); + } }; WRITE_CLASS_ENCODER(JSONFormattable) diff --git a/src/test/common/test_json_formattable.cc b/src/test/common/test_json_formattable.cc index 5cb6505300d..b6a7d37b286 100644 --- a/src/test/common/test_json_formattable.cc +++ b/src/test/common/test_json_formattable.cc @@ -26,6 +26,9 @@ static void get_jf(const string& s, JSONFormattable *f) { JSONParser p; bool result = p.parse(s.c_str(), s.size()); + if (!result) { + cout << "Failed to parse: '" << s << "'" << std::endl; + } ASSERT_EQ(true, result); try { decode_json_obj(*f, &p); -- 2.39.5