#include <boost/lexical_cast.hpp>
#include <include/types.h>
-// Enable boost.json's header-only mode ("https://github.com/boostorg/json?tab=readme-ov-file#header-only"):
+/* Enable boost.json's header-only mode:
+ (see: "https://github.com/boostorg/json?tab=readme-ov-file#header-only"): */
#include <boost/json/src.hpp>
#include <memory>
+#include <string>
#include <fstream>
using std::ifstream;
-using std::pair;
using std::ostream;
-using std::string;
-using std::vector;
-
-using ceph::bufferlist;
-using ceph::Formatter;
-
-#define dout_subsys ceph_subsys_rgw
-
-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());
- }
-}
+using std::begin;
+using std::end;
-void JSONObjIter::set(const JSONObjIter::map_iter_t &_cur, const JSONObjIter::map_iter_t &_last)
-{
- cur = _cur;
- last = _last;
-}
+using std::pair;
+using std::vector;
-void JSONObjIter::operator++()
-{
- if (cur != last)
- ++cur;
-}
+using std::string;
+using std::string_view;
-// IMPORTANT: The returned pointer is intended as NON-OWNING (i.e. the JSONObjIter is responsible for it):
-JSONObj *JSONObjIter::operator*()
-{
- return cur->second.get();
-}
+using ceph::bufferlist;
+using ceph::Formatter;
// does not work, FIXME
ostream& operator<<(ostream &out, const JSONObj &obj) {
return out;
}
-void JSONObj::add_child(string el, JSONObj *obj)
-{
- auto p = std::unique_ptr<JSONObj>(obj);
-
- children.insert({el, std::move(p)});
-}
-
-bool JSONObj::get_attr(string name, data_val& attr)
-{
- auto iter = attr_map.find(name);
- if (iter == attr_map.end())
- return false;
- attr = iter->second;
- return true;
-}
-
-JSONObjIter JSONObj::find(const string& name)
-{
- JSONObjIter iter;
- auto first = children.find(name);
- if (first != children.end()) {
- auto last = children.upper_bound(name);
- iter.set(first, last);
- }
- return iter;
-}
-
-JSONObjIter JSONObj::find_first()
-{
- JSONObjIter iter;
- iter.set(children.begin(), children.end());
- return iter;
-}
-
-JSONObjIter JSONObj::find_first(const string& name)
-{
- JSONObjIter iter;
- auto first = children.find(name);
- iter.set(first, children.end());
- return iter;
-}
-
-JSONObj *JSONObj::find_obj(const string& name)
-{
- JSONObjIter iter = find(name);
- if (iter.end())
- return NULL;
-
- return *iter;
-}
-
-bool JSONObj::get_data(const string& key, data_val *dest)
-{
- JSONObj *obj = find_obj(key);
- if (!obj)
- return false;
-
- *dest = obj->get_data_val();
-
- return true;
-}
-
/* accepts a JSON Array or JSON Object contained in
* a Boost.JSON value, v, and creates a Ceph JSONObj for each
* child contained in v
void JSONObj::handle_value(boost::json::value v)
{
if (auto op = v.if_object()) {
-
- for (const auto& kvp : *op)
- {
- JSONObj *child = new JSONObj;
-
- child->init(this, kvp.value(), kvp.key());
-
- add_child(kvp.key(), child);
+ for (const auto& kvp : *op) {
+ auto child = std::make_unique<JSONObj>(this, kvp.key(), kvp.value());
+ children.insert(std::pair { kvp.key(), std::move(child) });
}
return;
}
if (auto ap = v.if_array()) {
- for (const auto& kvp : *ap)
- {
- JSONObj *child = new JSONObj;
- child->init(this, kvp, "");
-
- add_child(child->get_name(), child);
+ for (const auto& kvp : *ap) {
+ auto child = std::make_unique<JSONObj>(this, "", kvp);
+ children.insert(std::pair { child->get_name(), std::move(child) });
}
}
// unknown type is not-an-error
}
-void JSONObj::init(JSONObj *parent_node, boost::json::value data_in, std::string name_in)
+vector<string> JSONObj::get_array_elements()
{
- parent = parent_node;
- data = data_in;
- name = name_in;
+ vector<string> elements;
- handle_value(data_in);
+ if (!data.is_array())
+ return elements;
- if (auto vp = data_in.if_string())
- val.set(*vp, true);
- else {
- val.set(boost::json::serialize(data_in), false);
- }
+ std::ranges::for_each(data.as_array(), [&elements](const auto& i) {
+ elements.emplace_back(boost::json::serialize(i));
+ });
- attr_map.insert({name, val});
+ return elements;
}
-JSONObj *JSONObj::get_parent()
+// parse the complete internal json_buffer
+bool JSONParser::parse()
{
- return parent;
-}
+ if (!parse_json(json_buffer, data))
+ return false;
-bool JSONObj::is_object()
-{
- return data.is_object();
-}
+ handle_value(data);
-bool JSONObj::is_array()
-{
- return data.is_array();
+ return true;
}
-vector<string> JSONObj::get_array_elements()
+// parse the internal json_buffer up to len
+bool JSONParser::parse(int len)
{
- vector<string> elements;
-
- boost::json::array temp_array;
-
- if (auto ap = data.if_array())
- temp_array = *ap;
-
- auto array_size = temp_array.size();
-
- if (array_size > 0) {
- for (boost::json::array::size_type i = 0; i < array_size; i++) {
- boost::json::value temp_value = temp_array[i];
- string temp_string;
- temp_string = boost::json::serialize(temp_value);
- elements.push_back(temp_string);
- }
- }
+ if (!parse_json(std::string_view { std::begin(json_buffer), len + std::begin(json_buffer) }, data))
+ return false;
- return elements;
-}
+ handle_value(data);
-void JSONParser::handle_data(const char *s, int len)
-{
- json_buffer.append(s, len); // check for problems with null termination FIXME
- buf_len += len;
+ return true;
}
-// parse a supplied JSON fragment
-bool JSONParser::parse(const char *buf_, int len)
+// parse a supplied JSON fragment:
+bool JSONParser::parse(std::string_view json_string_view)
{
- if (!buf_ || 0 >= len) {
- return false;
- }
-
- std::string_view json_string_view(buf_, len);
+ // The original implementation checked this, I'm not sure we have to but we're doing it for now:
+ if (json_string_view.empty())
+ return false;
- std::error_code ec;
- data = boost::json::parse(json_string_view, ec);
-
- if(ec)
+ if (!parse_json(json_string_view, data))
return false;
// recursively evaluate the result:
// For any other kind of value:
std::string s = boost::json::serialize(data);
- if (s.size() == static_cast<uint64_t>(len)) { // entire string was read
+ // Was the entire string read?
+ if (s.size() == static_cast<uint64_t>(json_string_view.length())) {
val.set(s, false);
return true;
}
return false;
}
-// parse the internal json_buffer up to len
-bool JSONParser::parse(int len)
-{
- std::string_view json_string(std::begin(json_buffer), len + std::begin(json_buffer));
-
- std::error_code ec;
- if(data = boost::json::parse(json_string, ec); ec)
- return false;
-
- handle_value(data);
-
- return true;
-}
-
-// parse the complete internal json_buffer
-bool JSONParser::parse()
-{
- std::error_code ec;
-
- data = boost::json::parse(json_buffer, ec);
-
- if(ec)
- return false;
-
- handle_value(data);
-
- return true;
-}
-
// parse a supplied ifstream:
bool JSONParser::parse(const char *file_name)
{
std::error_code ec;
data = boost::json::parse(is, ec);
- if(ec)
+ if (ec)
return false;
handle_value(data);
return true;
}
-
-void decode_json_obj(long& val, JSONObj *obj)
-{
- string s = obj->get_data();
- const char *start = s.c_str();
- char *p;
-
- errno = 0;
- val = strtol(start, &p, 10);
-
- /* Check for various possible errors */
-
- if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) ||
- (errno != 0 && val == 0)) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- if (p == start) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- while (*p != '\0') {
- if (!isspace(*p)) {
- throw JSONDecoder::err("failed to parse number");
- }
- p++;
- }
-}
-
-void decode_json_obj(unsigned long& val, JSONObj *obj)
-{
- string s = obj->get_data();
- const char *start = s.c_str();
- char *p;
-
- errno = 0;
- val = strtoul(start, &p, 10);
-
- /* Check for various possible errors */
-
- if ((errno == ERANGE && val == ULONG_MAX) ||
- (errno != 0 && val == 0)) {
- throw JSONDecoder::err("failed to number");
- }
-
- if (p == start) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- while (*p != '\0') {
- if (!isspace(*p)) {
- throw JSONDecoder::err("failed to parse number");
- }
- p++;
- }
-}
-
-void decode_json_obj(long long& val, JSONObj *obj)
-{
- string s = obj->get_data();
- const char *start = s.c_str();
- char *p;
-
- errno = 0;
- val = strtoll(start, &p, 10);
-
- /* Check for various possible errors */
-
- if ((errno == ERANGE && (val == LLONG_MAX || val == LLONG_MIN)) ||
- (errno != 0 && val == 0)) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- if (p == start) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- while (*p != '\0') {
- if (!isspace(*p)) {
- throw JSONDecoder::err("failed to parse number");
- }
- p++;
- }
-}
-
-void decode_json_obj(unsigned long long& val, JSONObj *obj)
-{
- string s = obj->get_data();
- const char *start = s.c_str();
- char *p;
-
- errno = 0;
- val = strtoull(start, &p, 10);
-
- /* Check for various possible errors */
-
- if ((errno == ERANGE && val == ULLONG_MAX) ||
- (errno != 0 && val == 0)) {
- throw JSONDecoder::err("failed to number");
- }
-
- if (p == start) {
- throw JSONDecoder::err("failed to parse number");
- }
-
- while (*p != '\0') {
- if (!isspace(*p)) {
- throw JSONDecoder::err("failed to parse number");
- }
- p++;
- }
-}
-
-void decode_json_obj(int& val, JSONObj *obj)
-{
- long l;
- decode_json_obj(l, obj);
-#if LONG_MAX > INT_MAX
- if (l > INT_MAX || l < INT_MIN) {
- throw JSONDecoder::err("integer out of range");
- }
-#endif
-
- val = (int)l;
-}
-
-void decode_json_obj(unsigned& val, JSONObj *obj)
-{
- unsigned long l;
- decode_json_obj(l, obj);
-#if ULONG_MAX > UINT_MAX
- if (l > UINT_MAX) {
- throw JSONDecoder::err("unsigned integer out of range");
- }
-#endif
-
- val = (unsigned)l;
-}
-
-void decode_json_obj(bool& val, JSONObj *obj)
-{
- string s = obj->get_data();
- if (strcasecmp(s.c_str(), "true") == 0) {
- val = true;
- return;
- }
- if (strcasecmp(s.c_str(), "false") == 0) {
- val = false;
- return;
- }
- int i;
- decode_json_obj(i, obj);
- val = (bool)i;
-}
-
-void decode_json_obj(bufferlist& val, JSONObj *obj)
-{
- string s = obj->get_data();
-
- bufferlist bl;
- bl.append(s.c_str(), s.size());
- try {
- val.decode_base64(bl);
- } catch (ceph::buffer::error& err) {
- throw JSONDecoder::err("failed to decode base64");
- }
-}
-
-void decode_json_obj(utime_t& val, JSONObj *obj)
-{
- string s = obj->get_data();
- uint64_t epoch;
- uint64_t nsec;
- int r = utime_t::parse_date(s, &epoch, &nsec);
- if (r == 0) {
- val = utime_t(epoch, nsec);
- } else {
- throw JSONDecoder::err("failed to decode utime_t");
- }
-}
-
-void decode_json_obj(ceph::real_time& val, JSONObj *obj)
-{
- const std::string& s = obj->get_data();
- uint64_t epoch;
- uint64_t nsec;
- int r = utime_t::parse_date(s, &epoch, &nsec);
- if (r == 0) {
- using namespace std::chrono;
- val = real_time{seconds(epoch) + nanoseconds(nsec)};
- } else {
- throw JSONDecoder::err("failed to decode real_time");
- }
-}
-
-void decode_json_obj(ceph::coarse_real_time& val, JSONObj *obj)
-{
- const std::string& s = obj->get_data();
- uint64_t epoch;
- uint64_t nsec;
- int r = utime_t::parse_date(s, &epoch, &nsec);
- if (r == 0) {
- using namespace std::chrono;
- val = coarse_real_time{seconds(epoch) + nanoseconds(nsec)};
- } else {
- throw JSONDecoder::err("failed to decode coarse_real_time");
- }
-}
-
-void decode_json_obj(ceph_dir_layout& i, JSONObj *obj){
-
- unsigned tmp;
- JSONDecoder::decode_json("dir_hash", tmp, obj, true);
- i.dl_dir_hash = tmp;
- JSONDecoder::decode_json("unused1", tmp, obj, true);
- i.dl_unused1 = tmp;
- JSONDecoder::decode_json("unused2", tmp, obj, true);
- i.dl_unused2 = tmp;
- JSONDecoder::decode_json("unused3", tmp, obj, true);
- i.dl_unused3 = tmp;
-}
-
-void encode_json(const char *name, std::string_view val, Formatter *f)
-{
- f->dump_string(name, val);
-}
-
-void encode_json(const char *name, const string& val, Formatter *f)
-{
- f->dump_string(name, val);
-}
-
-void encode_json(const char *name, const char *val, Formatter *f)
-{
- f->dump_string(name, val);
-}
-
-void encode_json(const char *name, bool val, Formatter *f)
-{
- f->dump_bool(name, val);
-}
-
-void encode_json(const char *name, int val, Formatter *f)
-{
- f->dump_int(name, val);
-}
-
-void encode_json(const char *name, long val, Formatter *f)
-{
- f->dump_int(name, val);
-}
-
-void encode_json(const char *name, unsigned val, Formatter *f)
-{
- f->dump_unsigned(name, val);
-}
-
-void encode_json(const char *name, unsigned long val, Formatter *f)
-{
- f->dump_unsigned(name, val);
-}
-
-void encode_json(const char *name, unsigned long long val, Formatter *f)
-{
- f->dump_unsigned(name, val);
-}
-
-void encode_json(const char *name, long long val, Formatter *f)
-{
- f->dump_int(name, val);
-}
-
-void encode_json(const char *name, const utime_t& val, Formatter *f)
-{
- val.gmtime(f->dump_stream(name));
-}
-
-void encode_json(const char *name, const ceph::real_time& val, Formatter *f)
-{
- encode_json(name, utime_t{val}, f);
-}
-
-void encode_json(const char *name, const ceph::coarse_real_time& val, Formatter *f)
-{
- encode_json(name, utime_t{val}, f);
-}
-
-void encode_json(const char *name, const bufferlist& bl, Formatter *f)
-{
- /* need to copy data from bl, as it is const bufferlist */
- bufferlist src = bl;
-
- bufferlist b64;
- src.encode_base64(b64);
-
- string s(b64.c_str(), b64.length());
-
- encode_json(name, s, f);
-}
-
-
-
/* JSONFormattable */
-const JSONFormattable& JSONFormattable::operator[](const string& name) const
-{
- auto i = obj.find(name);
- if (i == obj.end()) {
- return default_formattable;
- }
- return i->second;
-}
-
-const JSONFormattable& JSONFormattable::operator[](size_t index) const
-{
- if (index >= arr.size()) {
- return default_formattable;
- }
- return arr[index];
-}
-
-JSONFormattable& JSONFormattable::operator[](const string& name)
-{
- auto i = obj.find(name);
- if (i == obj.end()) {
- return default_formattable;
- }
- return i->second;
-}
-
-JSONFormattable& JSONFormattable::operator[](size_t index)
-{
- if (index >= arr.size()) {
- return default_formattable;
- }
- return arr[index];
-}
-
-bool JSONFormattable::exists(const string& name) const
-{
- auto i = obj.find(name);
- return (i != obj.end());
-}
-
-bool JSONFormattable::exists(size_t index) const
-{
- return (index < arr.size());
-}
-
-bool JSONFormattable::find(const string& name, string *val) const
-{
- auto i = obj.find(name);
- if (i == obj.end()) {
- return false;
- }
- *val = i->second.val();
- return true;
-}
-
-int JSONFormattable::val_int() const {
- return atoi(value.str.c_str());
-}
-
-long JSONFormattable::val_long() const {
- return atol(value.str.c_str());
-}
-
-long long JSONFormattable::val_long_long() const {
- return atoll(value.str.c_str());
-}
-
bool JSONFormattable::val_bool() const {
return (boost::iequals(value.str, "true") ||
+ boost::iequals(value.str, "1") ||
boost::iequals(value.str, "on") ||
- boost::iequals(value.str, "yes") ||
- boost::iequals(value.str, "1"));
-}
-
-string JSONFormattable::def(const string& def_val) const {
- if (type == FMT_NONE) {
- return def_val;
- }
- return val();
-}
-
-int JSONFormattable::def(int def_val) const {
- if (type == FMT_NONE) {
- return def_val;
- }
- return val_int();
-}
-
-bool JSONFormattable::def(bool def_val) const {
- if (type == FMT_NONE) {
- return def_val;
- }
- return val_bool();
-}
-
-string JSONFormattable::get(const string& name, const string& def_val) const
-{
- return (*this)[name].def(def_val);
-}
-
-int JSONFormattable::get_int(const string& name, int def_val) const
-{
- return (*this)[name].def(def_val);
-}
-
-bool JSONFormattable::get_bool(const string& name, bool def_val) const
-{
- return (*this)[name].def(def_val);
+ boost::iequals(value.str, "yes"));
}
struct field_entity {
int index{0}; /* if array */
bool append{false};
- field_entity() {}
+ field_entity() = default;
+ explicit field_entity(std::string_view sv) : is_obj(true), name(sv) {}
explicit field_entity(const string& n) : is_obj(true), name(n) {}
explicit field_entity(int i) : is_obj(false), index(i) {}
};
if (ofs != 0) {
return -EINVAL;
}
- result->push_back(field_entity(s));
+ result->emplace_back(field_entity(s));
return 0;
}
if (next_arr > ofs) {
- string field = s.substr(ofs, next_arr - ofs);
- result->push_back(field_entity(field));
+
+ auto field = string_view(ofs + begin(s), next_arr - ofs + begin(s));
+
+ result->emplace_back(field_entity(field));
ofs = next_arr;
}
size_t end_arr = s.find(']', next_arr + 1);
return -EINVAL;
}
- string index_str = s.substr(next_arr + 1, end_arr - next_arr - 1);
+ auto index_str = string_view(s).substr(1 + next_arr, end_arr - next_arr - 1);
ofs = end_arr + 1;
if (!index_str.empty()) {
- result->push_back(field_entity(atoi(index_str.c_str())));
+
+ int x;
+ if (auto [_, ec] = std::from_chars(begin(index_str), end(index_str), x); std::errc() == ec)
+ result->emplace_back(field_entity(x));
+ else
+ throw std::invalid_argument(fmt::format("{}", index_str));
+
} else {
field_entity f;
f.append = true;
- result->push_back(f);
+ result->emplace_back(f);
}
}
return 0;
JSONParser jp;
- bool is_valid_json = jp.parse(val.c_str(), val.size());
+ bool is_valid_json = jp.parse(val);
for (const auto& i : tok) {
vector<field_entity> v;
}
}
-void encode_json(const char *name, const JSONFormattable& v, Formatter *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, arr, f);
- break;
- case JSONFormattable::FMT_OBJ:
- f->open_object_section(name);
- for (auto iter : obj) {
- ::encode_json(iter.first.c_str(), iter.second, f);
- }
- f->close_section();
- break;
- case JSONFormattable::FMT_NONE:
- break;
- }
-}
-
bool JSONFormattable::handle_value(std::string_view name, std::string_view s, bool quoted) {
JSONFormattable *new_val;
if (cur_enc->is_array()) {
- cur_enc->arr.push_back(JSONFormattable());
+ cur_enc->arr.emplace_back(JSONFormattable());
new_val = &cur_enc->arr.back();
} else {
cur_enc->set_type(JSONFormattable::FMT_OBJ);
const char *ns,
bool section_is_array) {
if (cur_enc->is_array()) {
- cur_enc->arr.push_back(JSONFormattable());
+ cur_enc->arr.emplace_back(JSONFormattable());
cur_enc = &cur_enc->arr.back();
} else if (enc_stack.size() > 1) {
/* only open a new section if already nested,
*/
cur_enc = &cur_enc->obj[string{name}];
}
- enc_stack.push_back(cur_enc);
+ enc_stack.emplace_back(cur_enc);
if (section_is_array) {
cur_enc->set_type(JSONFormattable::FMT_ARRAY);
#ifndef CEPH_JSON_H
#define CEPH_JSON_H
+#include <strings.h>
+
#include <map>
#include <set>
#include <deque>
#include <string>
+
#include <iostream>
-#include <stdexcept>
+
+#include <ranges>
+#include <concepts>
+
#include <typeindex>
+#include <stdexcept>
+
+#include <include/types.h>
+#include <include/utime.h>
#include <boost/json.hpp>
+#include <boost/json/value_to.hpp>
+
#include <boost/optional.hpp>
+
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
#include <include/types.h>
#include <include/ceph_fs.h>
+#include "include/buffer.h"
+
#include "common/ceph_time.h"
+#include <fmt/format.h>
+
#include "Formatter.h"
+class utime_t;
+
class JSONObj;
+class JSONFormattable;
+
+namespace ceph_json::detail {
+
+template <typename T, typename ...Ts>
+consteval bool is_any_of()
+{
+ return (std::is_same_v<T, Ts> || ...);
+}
+
+/* Note that std::is_integer<> will also pick up bool, which in our
+case we need to count as a non-integer type as the JSON codecs treat
+it differently ("https://en.cppreference.com/w/cpp/types/is_integral").
+
+From what I can see in the extant code, it looks like the same rules
+should be applied to char, etc., so I've also done so here:
+*/
+template <typename T>
+concept json_integer = requires
+{
+ requires std::is_integral_v<T>;
+ requires !std::is_same_v<T, bool>;
+
+ requires !is_any_of<T, char, char8_t, char16_t, char32_t, wchar_t>();
+};
+
+template <typename T>
+concept json_signed_integer = requires
+{
+ requires json_integer<T> && std::signed_integral<T>;
+};
+
+template <typename T>
+concept json_unsigned_integer = requires
+{
+ requires json_integer<T> && std::unsigned_integral<T>;
+};
+
+/* Distinguish between containers with a value that's an associative kv-pair (a mapped type) and
+those which are a "single" value. Note that this is not the same as the AssociativeContainer
+named concept, as the rule there is that the container is key-indexed, and it does not necessarily
+have to be a pair (e.g. std::set<> is an AssociativeContainer). Similarly, for sequence types
+we don't want to capture standard strings and the like, even if we otherwise could consider them
+a value sequence:
+*/
+template <typename ContainerT>
+concept json_mapped_kv_seq = requires
+{
+ typename ContainerT::key_type;
+ typename ContainerT::key_compare;
+
+ typename ContainerT::value_type;
+ typename ContainerT::mapped_type;
+};
+
+template <typename ContainerT>
+concept json_val_seq = requires
+{
+ typename ContainerT::value_type;
+
+ requires !json_mapped_kv_seq<ContainerT>;
+ requires !std::convertible_to<ContainerT, std::string>;
+};
-class JSONObjIter {
+} // namespace ceph_json
- using map_iter_t = std::map<std::string, std::unique_ptr<JSONObj>>::iterator;
+class JSONObjIter final {
+
+ using map_iter_t = std::map<std::string, std::unique_ptr<JSONObj>, std::less<>>::iterator;
map_iter_t cur;
map_iter_t last;
+private:
+ JSONObjIter(const JSONObjIter::map_iter_t& cur, const JSONObjIter::map_iter_t& end)
+ : cur(cur),
+ last(end)
+ {}
+
+public:
+ JSONObjIter() = default;
+
public:
- void set(const JSONObjIter::map_iter_t &_cur, const JSONObjIter::map_iter_t &_end);
+ void set(const JSONObjIter::map_iter_t &_cur, const JSONObjIter::map_iter_t &_end) {
+ cur = _cur;
+ last = _end;
+ }
- void operator++();
+ void operator++() { if (cur != last) ++cur; }
// IMPORTANT: The returned pointer is intended as NON-OWNING (i.e. JSONObjIter
// is responsible for it):
- JSONObj *operator*();
+ JSONObj *operator*() { return cur->second.get(); }
- bool end() const {
- return (cur == last);
- }
+ bool end() const { return (cur == last); }
+
+private:
+ friend JSONObj;
};
class JSONObj
{
- using children_multimap_t = std::multimap<std::string, std::unique_ptr<JSONObj>>;
- using children_multimap_value_type = typename children_multimap_t::value_type;
-
JSONObj *parent = nullptr;
+protected:
+ using children_multimap_t = std::multimap<std::string, std::unique_ptr<JSONObj>, std::less<>>;
+ using children_multimap_value_type = typename children_multimap_t::value_type;
+
public:
struct data_val {
std::string str;
bool data_quoted{false};
- std::multimap<std::string, std::unique_ptr<JSONObj>> children;
-
- std::map<std::string, data_val> attr_map;
+ children_multimap_t children;
+ std::map<std::string, data_val, std::less<>> attr_map;
void handle_value(boost::json::value v);
+protected:
+ /* Too bad that we lose the information from the error code, but it's actually already
+ junked anyway. */
+ static bool parse_json(std::string_view input, boost::json::value& data_out)
+ {
+ std::error_code ec;
+
+ data_out = boost::json::parse(input, ec, boost::json::storage_ptr(),
+ { .allow_invalid_utf8 = true });
+
+ return ec ? false : true;
+ }
+
public:
+ JSONObj() = default;
+
+ JSONObj(JSONObj *parent_node, std::string_view name_in, boost::json::value data_in)
+ : parent { parent_node },
+ name { name_in },
+ data { data_in }
+ {
+ handle_value(data);
+
+ if (auto vp = data_in.if_string())
+ val.set(*vp, true);
+ else
+ val.set(boost::json::serialize(data), false);
+ }
+
virtual ~JSONObj() = default;
public:
- void init(JSONObj *parent_node, boost::json::value data_in, std::string name_in);
+ std::string& get_name() noexcept { return name; }
+ data_val& get_data_val() noexcept { return val; }
- std::string& get_name() { return name; }
- data_val& get_data_val() { return val; }
+ const std::string& get_data() const noexcept { return val.str; }
- const std::string& get_data() { return val.str; }
- bool get_data(const std::string& key, data_val *dest);
+ bool get_data(std::string_view key, data_val *dest) {
+
+ JSONObj *obj = find_obj(key);
+ if (!obj)
+ return false;
+
+ *dest = obj->get_data_val();
+
+ return true;
+ }
- JSONObj *get_parent();
+ JSONObj *get_parent() const noexcept { return parent; };
+
+ bool get_attr(std::string_view name, data_val& attr) {
+ if (auto i = attr_map.find(name); end(attr_map) != i)
+ return (attr = i->second), true;
+
+ return false;
+ }
+
+ JSONObjIter find(std::string_view name) {
+ return { children.find(name), children.upper_bound(name) };
+ }
- // Note: takes ownership of child:
- void add_child(std::string el, JSONObj *child);
+ JSONObjIter find_first() {
+ return { children.begin(), children.end() };
+ }
- bool get_attr(std::string name, data_val& attr);
+ JSONObjIter find_first(std::string_view name) {
+ return { children.find(name), children.end() };
+ }
- JSONObjIter find(const std::string& name);
- JSONObjIter find_first();
- JSONObjIter find_first(const std::string& name);
- JSONObj *find_obj(const std::string& name);
+ JSONObj *find_obj(std::string_view name) {
+ JSONObjIter i = this->find(name);
+ return i.end() ? nullptr : *i;
+ }
friend std::ostream& operator<<(std::ostream &out,
const JSONObj &obj); // does not work, FIXME
- bool is_array();
- bool is_object();
+ bool is_array() const noexcept { return data.is_array(); }
+ bool is_object() const noexcept { return data.is_object(); }
std::vector<std::string> get_array_elements();
};
return out;
}
-class JSONParser : public JSONObj
+class JSONParser final : public JSONObj
{
int buf_len = 0;
std::string json_buffer;
public:
- void handle_data(const char *s, int len);
+ ~JSONParser() override = default;
- bool parse(const char *buf_, int len);
- bool parse(int len);
+public:
+ // operate on the internal buffer:
bool parse();
+ bool parse(int len);
+
+ // operate on a string/stringlike range or object:
+ bool parse(std::string_view sv);
+
+ bool parse(const char *buf_, int len) {
+ return buf_ ?
+ parse(std::string_view { buf_, static_cast<std::string_view::size_type>(len) })
+ : false;
+ }
+
+ bool parse(ceph::buffer::list& bl) { return parse(std::string_view { bl.c_str(), bl.length() }); }
+
+ // operate on a data file:
bool parse(const char *file_name);
- const char *get_json() { return json_buffer.c_str(); }
+public:
+ const char *get_json() const noexcept{ return json_buffer.c_str(); }
};
-void encode_json(const char *name, const JSONObj::data_val& v, ceph::Formatter *f);
-
-class JSONDecoder {
+class JSONDecoder final {
public:
struct err : std::runtime_error {
using runtime_error::runtime_error;
JSONParser parser;
JSONDecoder(ceph::buffer::list& bl) {
- if (!parser.parse(bl.c_str(), bl.length())) {
- std::cout << "JSONDecoder::err()" << std::endl; // JFW: do we still want this here?
+ if (!parser.parse(bl))
throw JSONDecoder::err("failed to parse JSON input");
- }
}
template<class T>
- static bool decode_json(const char *name, T& val, JSONObj *obj, bool mandatory = false);
+ static bool decode_json(std::string_view name, T& val, JSONObj *obj, bool mandatory = false);
template<class C>
- static bool decode_json(const char *name, C& container, void (*cb)(C&, JSONObj *obj), JSONObj *obj, bool mandatory = false);
+ static bool decode_json(std::string_view name, C& container, void (*cb)(C&, JSONObj *obj), JSONObj *obj, bool mandatory = false);
template<class T>
- static void decode_json(const char *name, T& val, const T& default_val, JSONObj *obj);
+ static void decode_json(std::string_view name, T& val, const T& default_val, JSONObj *obj);
template<class T>
- static bool decode_json(const char *name, boost::optional<T>& val, JSONObj *obj, bool mandatory = false);
+ static bool decode_json(std::string_view name, boost::optional<T>& val, JSONObj *obj, bool mandatory = false);
template<class T>
- static bool decode_json(const char *name, std::optional<T>& val, JSONObj *obj, bool mandatory = false);
-
+ static bool decode_json(std::string_view name, std::optional<T>& val, JSONObj *obj, bool mandatory = false);
};
+template <typename IntegerT>
+requires ceph_json::detail::json_signed_integer<IntegerT> ||
+ ceph_json::detail::json_unsigned_integer<IntegerT>
+void decode_json_obj(IntegerT& val, JSONObj *obj)
+try
+{
+ if constexpr (ceph_json::detail::json_signed_integer<IntegerT>)
+ val = stol(obj->get_data());
+
+ if constexpr (ceph_json::detail::json_unsigned_integer<IntegerT>)
+ val = stoul(obj->get_data());
+}
+catch(const std::out_of_range& e)
+{
+ throw JSONDecoder::err(fmt::format("failed to parse number: {}", e.what()));
+}
+catch(const std::invalid_argument& e)
+{
+ throw JSONDecoder::err(fmt::format("failed to parse number: {}", e.what()));
+}
+
template<class T>
void decode_json_obj(T& val, JSONObj *obj)
{
val = obj->get_data();
}
-static inline void decode_json_obj(JSONObj::data_val& val, JSONObj *obj)
+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);
-void decode_json_obj(long& val, JSONObj *obj);
-void decode_json_obj(unsigned& val, JSONObj *obj);
-void decode_json_obj(int& val, JSONObj *obj);
-void decode_json_obj(bool& val, JSONObj *obj);
-void decode_json_obj(ceph::buffer::list& val, JSONObj *obj);
-class utime_t;
-void decode_json_obj(utime_t& val, JSONObj *obj);
-void decode_json_obj(ceph_dir_layout& i, JSONObj *obj);
-
-void decode_json_obj(ceph::real_time& val, JSONObj *obj);
-void decode_json_obj(ceph::coarse_real_time& val, JSONObj *obj);
-
-template<class T>
-void decode_json_obj(std::list<T>& l, JSONObj *obj)
+inline void decode_json_obj(bool& val, JSONObj *obj)
{
- l.clear();
+ std::string_view sv(obj->get_data());
- JSONObjIter iter = obj->find_first();
+ if (boost::iequals(sv, "true"))
+ {
+ val = true;
+ return;
+ }
- for (; !iter.end(); ++iter) {
- T val;
- JSONObj *o = *iter;
- decode_json_obj(val, o);
- l.push_back(val);
+ if (boost::iequals(sv, "false"))
+ {
+ val = false;
+ return;
}
+
+ // For 1, 0, anything else:
+ int i;
+ decode_json_obj(i, obj);
+ val = static_cast<bool>(i);
}
-template<class T>
-void decode_json_obj(std::deque<T>& l, JSONObj *obj)
+inline void decode_json_obj(bufferlist& val, JSONObj *obj)
{
- l.clear();
+ bufferlist bl;
- JSONObjIter iter = obj->find_first();
+ std::string_view sv = obj->get_data();
- for (; !iter.end(); ++iter) {
- T val;
- JSONObj *o = *iter;
- decode_json_obj(val, o);
- l.push_back(val);
- }
-}
+ bl.append(sv);
-template<class T>
-void decode_json_obj(std::set<T>& l, JSONObj *obj)
-{
- l.clear();
-
- JSONObjIter iter = obj->find_first();
+ try {
+ val.decode_base64(bl);
- for (; !iter.end(); ++iter) {
- T val;
- JSONObj *o = *iter;
- decode_json_obj(val, o);
- l.insert(val);
+ } catch (ceph::buffer::error& err) {
+ throw JSONDecoder::err("failed to decode base64");
}
}
-template<class T, class Compare, class Alloc>
-void decode_json_obj(boost::container::flat_set<T, Compare, Alloc>& l, JSONObj *obj)
+inline void decode_json_obj(utime_t& val, JSONObj *obj)
{
- l.clear();
-
- JSONObjIter iter = obj->find_first();
-
- for (; !iter.end(); ++iter) {
- T val;
- JSONObj *o = *iter;
- decode_json_obj(val, o);
- l.insert(val);
+ uint64_t epoch;
+ uint64_t nsec;
+ int r = utime_t::parse_date(obj->get_data(), &epoch, &nsec);
+ if (r == 0) {
+ val = utime_t(epoch, nsec);
+ } else {
+ throw JSONDecoder::err("failed to decode utime_t");
}
}
-template<class T>
-void decode_json_obj(std::vector<T>& l, JSONObj *obj)
+inline void decode_json_obj(ceph::real_time& val, JSONObj *obj)
{
- l.clear();
-
- JSONObjIter iter = obj->find_first();
-
- for (; !iter.end(); ++iter) {
- T val;
- JSONObj *o = *iter;
- decode_json_obj(val, o);
- l.push_back(val);
+ uint64_t epoch;
+ uint64_t nsec;
+ int r = utime_t::parse_date(obj->get_data(), &epoch, &nsec);
+ if (r == 0) {
+ using namespace std::chrono;
+ val = real_time{seconds(epoch) + nanoseconds(nsec)};
+ } else {
+ throw JSONDecoder::err("failed to decode real_time");
}
}
-template<class K, class V, class C = std::less<K> >
-void decode_json_obj(std::map<K, V, C>& m, JSONObj *obj)
+inline void decode_json_obj(ceph::coarse_real_time& val, JSONObj *obj)
{
- m.clear();
-
- JSONObjIter iter = obj->find_first();
-
- for (; !iter.end(); ++iter) {
- K key;
- V val;
- JSONObj *o = *iter;
- JSONDecoder::decode_json("key", key, o);
- JSONDecoder::decode_json("val", val, o);
- m[key] = val;
+ uint64_t epoch;
+ uint64_t nsec;
+ int r = utime_t::parse_date(obj->get_data(), &epoch, &nsec);
+ if (r == 0) {
+ using namespace std::chrono;
+ val = coarse_real_time{seconds(epoch) + nanoseconds(nsec)};
+ } else {
+ throw JSONDecoder::err("failed to decode coarse_real_time");
}
}
-template<class K, class V, class C = std::less<K> >
-void decode_json_obj(boost::container::flat_map<K, V, C>& m, JSONObj *obj)
+inline void decode_json_obj(ceph_dir_layout& i, JSONObj *obj)
{
- m.clear();
-
- JSONObjIter iter = obj->find_first();
-
- for (; !iter.end(); ++iter) {
- K key;
- V val;
- JSONObj *o = *iter;
- JSONDecoder::decode_json("key", key, o);
- JSONDecoder::decode_json("val", val, o);
- m[key] = val;
- }
+ unsigned tmp;
+ JSONDecoder::decode_json("dir_hash", tmp, obj, true);
+ i.dl_dir_hash = tmp;
+ JSONDecoder::decode_json("unused1", tmp, obj, true);
+ i.dl_unused1 = tmp;
+ JSONDecoder::decode_json("unused2", tmp, obj, true);
+ i.dl_unused2 = tmp;
+ JSONDecoder::decode_json("unused3", tmp, obj, true);
+ i.dl_unused3 = tmp;
}
-template<class K, class V>
-void decode_json_obj(std::multimap<K, V>& m, JSONObj *obj)
+template <ceph_json::detail::json_val_seq SeqT>
+void decode_json_obj(SeqT& seq, JSONObj *obj)
{
- m.clear();
-
- JSONObjIter iter = obj->find_first();
+ seq.clear();
- for (; !iter.end(); ++iter) {
- K key;
- V val;
+ for (auto iter = obj->find_first(); !iter.end(); ++iter) {
+ typename SeqT::value_type val;
JSONObj *o = *iter;
- JSONDecoder::decode_json("key", key, o);
- JSONDecoder::decode_json("val", val, o);
- m.insert(make_pair(key, val));
- }
+ decode_json_obj(val, o);
+
+ if constexpr (requires { seq.emplace_back(val); })
+ seq.emplace_back(val);
+ else
+ seq.emplace(val);
+ }
}
-template<class K, class V>
-void decode_json_obj(boost::container::flat_map<K, V>& m, JSONObj *obj)
+template <ceph_json::detail::json_mapped_kv_seq KVSeqT>
+void decode_json_obj(KVSeqT& kvs, JSONObj *obj)
{
- m.clear();
-
- JSONObjIter iter = obj->find_first();
+ kvs.clear();
- for (; !iter.end(); ++iter) {
- K key;
- V val;
+ for (auto iter = obj->find_first(); !iter.end(); ++iter) {
+ typename KVSeqT::key_type key;
+ typename KVSeqT::mapped_type val;
JSONObj *o = *iter;
JSONDecoder::decode_json("key", key, o);
JSONDecoder::decode_json("val", val, o);
- m[key] = val;
+
+ if constexpr(requires { kvs[key] = val; })
+ kvs[key] = val; // i.e. insert_or_assign()
+ else
+ kvs.insert({key, val});
}
}
+
template<class C>
void decode_json_obj(C& container, void (*cb)(C&, JSONObj *obj), JSONObj *obj)
{
container.clear();
- JSONObjIter iter = obj->find_first();
-
- for (; !iter.end(); ++iter) {
+ for (auto iter = obj->find_first(); !iter.end(); ++iter) {
JSONObj *o = *iter;
cb(container, o);
}
}
template<class T>
-bool JSONDecoder::decode_json(const char *name, T& val, JSONObj *obj, bool mandatory)
+bool JSONDecoder::decode_json(std::string_view name, T& val, JSONObj *obj, bool mandatory)
{
JSONObjIter iter = obj->find_first(name);
if (iter.end()) {
}
template<class C>
-bool JSONDecoder::decode_json(const char *name, C& container, void (*cb)(C&, JSONObj *), JSONObj *obj, bool mandatory)
+bool JSONDecoder::decode_json(std::string_view name, C& container, void (*cb)(C&, JSONObj *), JSONObj *obj, bool mandatory)
{
container.clear();
}
template<class T>
-void JSONDecoder::decode_json(const char *name, T& val, const T& default_val, JSONObj *obj)
+void JSONDecoder::decode_json(std::string_view name, T& val, const T& default_val, JSONObj *obj)
{
JSONObjIter iter = obj->find_first(name);
if (iter.end()) {
}
template<class T>
-bool JSONDecoder::decode_json(const char *name, boost::optional<T>& val, JSONObj *obj, bool mandatory)
+bool JSONDecoder::decode_json(std::string_view name, boost::optional<T>& val, JSONObj *obj, bool mandatory)
{
JSONObjIter iter = obj->find_first(name);
if (iter.end()) {
}
template<class T>
-bool JSONDecoder::decode_json(const char *name, std::optional<T>& val, JSONObj *obj, bool mandatory)
+bool JSONDecoder::decode_json(std::string_view name, std::optional<T>& val, JSONObj *obj, bool mandatory)
{
JSONObjIter iter = obj->find_first(name);
if (iter.end()) {
public:
class HandlerBase {
public:
- virtual ~HandlerBase() {}
+ virtual ~HandlerBase() = default;
virtual std::type_index get_type() = 0;
virtual void encode_json(const char *name, const void *pval, ceph::Formatter *) const = 0;
template <class T>
class Handler : public HandlerBase {
public:
- virtual ~Handler() {}
-
std::type_index get_type() override {
return std::type_index(typeid(const T&));
}
}
};
+void encode_json(const char *name, ceph_json::detail::json_signed_integer auto val, Formatter *f)
+{
+ f->dump_int(name, val);
+}
+
+void encode_json(const char *name, ceph_json::detail::json_unsigned_integer auto val, Formatter *f)
+{
+ f->dump_unsigned(name, val);
+}
+
template<class T>
-static void encode_json_impl(const char *name, const T& val, ceph::Formatter *f)
+requires requires(const T& val, ceph::Formatter *f) { val.dump(f); }
+void encode_json_impl(const char *name, const T& val, ceph::Formatter *f)
{
f->open_object_section(name);
val.dump(f);
}
template<class T>
-static void encode_json(const char *name, const T& val, ceph::Formatter *f)
+requires requires(const T& val, ceph::Formatter *f) { encode_json_impl("", val, f); }
+void encode_json(const char *name, const T& val, ceph::Formatter *f)
{
JSONEncodeFilter *filter = static_cast<JSONEncodeFilter *>(f->get_external_feature_handler("JSONEncodeFilter"));
}
}
-class utime_t;
-
-void encode_json(const char *name, std::string_view val, ceph::Formatter *f);
-void encode_json(const char *name, const std::string& val, ceph::Formatter *f);
-void encode_json(const char *name, const char *val, ceph::Formatter *f);
-void encode_json(const char *name, bool val, ceph::Formatter *f);
-void encode_json(const char *name, int val, ceph::Formatter *f);
-void encode_json(const char *name, unsigned val, ceph::Formatter *f);
-void encode_json(const char *name, long val, ceph::Formatter *f);
-void encode_json(const char *name, unsigned long val, ceph::Formatter *f);
-void encode_json(const char *name, long long val, ceph::Formatter *f);
-void encode_json(const char *name, const utime_t& val, ceph::Formatter *f);
-void encode_json(const char *name, const ceph::buffer::list& bl, ceph::Formatter *f);
-void encode_json(const char *name, long long unsigned val, ceph::Formatter *f);
-
-void encode_json(const char *name, const ceph::real_time& val, ceph::Formatter *f);
-void encode_json(const char *name, const ceph::coarse_real_time& val, ceph::Formatter *f);
-
-template<class T>
-static void encode_json(const char *name, const std::list<T>& l, ceph::Formatter *f)
+inline void encode_json(const char *name, std::string_view val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ f->dump_string(name, val);
}
-template<class T>
-static void encode_json(const char *name, const std::deque<T>& l, ceph::Formatter *f)
+inline void encode_json(const char *name, const std::string& val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ f->dump_string(name, val);
}
-template<class T, class Compare = std::less<T> >
-static void encode_json(const char *name, const std::set<T, Compare>& l, ceph::Formatter *f)
+inline void encode_json(const char *name, const char *val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ f->dump_string(name, val);
}
-template<class T, class Compare, class Alloc>
-static void encode_json(const char *name,
- const boost::container::flat_set<T, Compare, Alloc>& l,
- ceph::Formatter *f)
+inline void encode_json(const char *name, bool val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ f->dump_bool(name, val);
}
-template<class T>
-static void encode_json(const char *name, const std::vector<T>& l, ceph::Formatter *f)
+inline void encode_json(const char *name, const utime_t& val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ val.gmtime(f->dump_stream(name));
}
-template<class T, std::size_t N>
-static void encode_json(const char *name, const std::array<T, N>& l,
- ceph::Formatter *f)
+inline void encode_json(const char *name, const ceph::real_time& val, Formatter *f)
{
- f->open_array_section(name);
- for (auto iter = l.cbegin(); iter != l.cend(); ++iter) {
- encode_json("obj", *iter, f);
- }
- f->close_section();
+ encode_json(name, utime_t{val}, f);
}
-template<class K, class V, class C = std::less<K>>
-static void encode_json(const char *name, const std::map<K, V, C>& m, ceph::Formatter *f)
+inline void encode_json(const char *name, const ceph::coarse_real_time& val, Formatter *f)
{
- f->open_array_section(name);
- for (auto i = m.cbegin(); i != m.cend(); ++i) {
- f->open_object_section("entry");
- encode_json("key", i->first, f);
- encode_json("val", i->second, f);
- f->close_section();
- }
- f->close_section();
+ encode_json(name, utime_t{val}, f);
}
-template<class K, class V, class C = std::less<K> >
-static void encode_json(const char *name, const boost::container::flat_map<K, V, C>& m, ceph::Formatter *f)
+inline void encode_json(const char *name, const bufferlist& bl, Formatter *f)
{
- f->open_array_section(name);
- for (auto i = m.cbegin(); i != m.cend(); ++i) {
- f->open_object_section("entry");
- encode_json("key", i->first, f);
- encode_json("val", i->second, f);
- f->close_section();
- }
- f->close_section();
+ /* need to copy data from bl, as it is const bufferlist */
+ bufferlist src = bl;
+
+ bufferlist b64;
+ src.encode_base64(b64);
+
+ std::string_view sv(b64.c_str(), b64.length());
+
+ encode_json(name, sv, f);
}
-template<class K, class V>
-static void encode_json(const char *name, const std::multimap<K, V>& m, ceph::Formatter *f)
+template <class T>
+void encode_json(const char *name, const std::optional<T>& o, ceph::Formatter *f)
{
- f->open_array_section(name);
- for (auto i = m.begin(); i != m.end(); ++i) {
- f->open_object_section("entry");
- encode_json("key", i->first, f);
- encode_json("val", i->second, f);
- f->close_section();
+ if (!o) {
+ return;
}
- f->close_section();
+ encode_json(name, *o, f);
}
-template<class K, class V>
-static void encode_json(const char *name, const boost::container::flat_map<K, V>& m, ceph::Formatter *f)
+inline void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f)
{
- f->open_array_section(name);
- for (auto i = m.begin(); i != m.end(); ++i) {
- f->open_object_section("entry");
- encode_json("key", i->first, f);
- encode_json("val", i->second, f);
- f->close_section();
+ if (v.quoted) {
+ encode_json(name, v.str, f);
+ } else {
+ f->dump_format_unquoted(name, "%s", v.str.c_str());
}
- f->close_section();
}
+inline void encode_json(const char *name, const JSONFormattable& v, Formatter *f);
+
template<class K, class V>
void encode_json_map(const char *name, const std::map<K, V>& m, ceph::Formatter *f)
{
f->open_array_section(name);
- for (auto iter = m.cbegin(); iter != m.cend(); ++iter) {
- encode_json("obj", iter->second, f);
- }
+ std::ranges::for_each(m, [&f](const auto& kv) { encode_json("obj", kv.second, f); });
f->close_section();
}
-
template<class K, class V>
void encode_json_map(const char *name, const char *index_name,
const char *object_name, const char *value_name,
const char *object_name, const char *value_name,
const std::map<K, V>& m, ceph::Formatter *f)
{
- encode_json_map<K, V>(name, index_name, object_name, value_name, NULL, NULL, m, f);
+ encode_json_map<K, V>(name, index_name, object_name, value_name, nullptr, nullptr, m, f);
}
template<class K, class V>
void encode_json_map(const char *name, const char *index_name, const char *value_name,
const std::map<K, V>& m, ceph::Formatter *f)
{
- encode_json_map<K, V>(name, index_name, NULL, value_name, NULL, NULL, m, f);
+ encode_json_map<K, V>(name, index_name, nullptr, value_name, nullptr, nullptr, m, f);
}
-template <class T>
-static void encode_json(const char *name, const std::optional<T>& o, ceph::Formatter *f)
-{
- if (!o) {
- return;
- }
- encode_json(name, *o, f);
-}
-
-
-template<class K, class V>
+template <class K, class V>
void encode_json_map(const char *name, const boost::container::flat_map<K, V>& m, ceph::Formatter *f)
{
f->open_array_section(name);
f->close_section();
}
-
template<class K, class V>
void encode_json_map(const char *name, const char *index_name,
const char *object_name, const char *value_name,
const char *object_name, const char *value_name,
const boost::container::flat_map<K, V>& m, ceph::Formatter *f)
{
- encode_json_map<K, V>(name, index_name, object_name, value_name, NULL, NULL, m, f);
+ encode_json_map<K, V>(name, index_name, object_name, value_name, nullptr, nullptr, m, f);
}
template<class K, class V>
void encode_json_map(const char *name, const char *index_name, const char *value_name,
const boost::container::flat_map<K, V>& m, ceph::Formatter *f)
{
- encode_json_map<K, V>(name, index_name, NULL, value_name, NULL, NULL, m, f);
+ encode_json_map<K, V>(name, index_name, nullptr, value_name, nullptr, nullptr, m, f);
}
+void encode_json(const char *name, const ceph_json::detail::json_val_seq auto& val, Formatter *f)
+{
+ f->open_array_section(name);
+ std::ranges::for_each(val, [&f](const auto &obj) {
+ ::encode_json("obj", obj, f);
+ });
+ f->close_section();
+}
+
+void encode_json(const char *name, const ceph_json::detail::json_mapped_kv_seq auto& val, Formatter *f)
+{
+ f->open_array_section(name);
+ std::ranges::for_each(val, [&f](const auto& kv) {
+ f->open_object_section("entry");
+ ::encode_json("key", kv.first, f);
+ ::encode_json("val", kv.second, f);
+ f->close_section();
+ });
+ f->close_section();
+}
class JSONFormattable : public ceph::JSONFormatter {
+
JSONObj::data_val value;
std::vector<JSONFormattable> arr;
- std::map<std::string, JSONFormattable> obj;
+ std::map<std::string, JSONFormattable, std::less<>> obj;
std::vector<JSONFormattable *> enc_stack;
JSONFormattable *cur_enc;
bool handle_close_section() override;
public:
- JSONFormattable(bool p = false) : JSONFormatter(p) {
+ JSONFormattable(bool p = false)
+ : JSONFormatter(p) {
cur_enc = this;
enc_stack.push_back(cur_enc);
}
break;
}
}
+
+ const std::map<std::string, JSONFormattable, std::less<>> object() const noexcept { return obj; }
+
+ const std::vector<JSONFormattable>& array() const noexcept { return arr; }
+
+ JSONFormattable& operator[](const std::string& name);
+ const JSONFormattable& operator[](const std::string& name) const;
+
+ JSONFormattable& operator[](size_t index);
+ const JSONFormattable& operator[](size_t index) const;
+
+ const std::string& val() const noexcept { return value.str; }
+ int val_int() const { return atoi(value.str.c_str()); }
+ long val_long() const { return atol(value.str.c_str()); }
+ long long val_long_long() const { return atoll(value.str.c_str()); }
+ bool val_bool() const;
+
+ operator std::string() const noexcept { return value.str; }
+ explicit operator int() const { return val_int(); }
+ explicit operator long() const { return val_long(); }
+ explicit operator long long() const { return val_long_long(); }
+ explicit operator bool() const { return val_bool(); }
+
+ std::string def(const std::string& def_val) const { return FMT_NONE == type ? def_val : val(); }
+ int def(int def_val) const { return FMT_NONE == type ? def_val : val_int(); }
+ bool def(bool def_val) const { return FMT_NONE == type ? def_val : val_bool(); }
+
+ std::string operator ()(const char *def_val) const { return def(std::string(def_val)); }
+ int operator()(int def_val) const { return def(def_val); }
+ bool operator()(bool def_val) const { return def(def_val); }
+
+ bool exists(const std::string& name) const noexcept { return obj.contains(name); }
+ bool exists(size_t index) const noexcept { return (index < arr.size()); }
+
+ bool find(const std::string& name, std::string *val) const noexcept {
+ if (auto i = obj.find(name); end(obj) != i)
+ return (*val = i->second.val()), true;
+
+ return false;
+ }
+
+ std::string get(const std::string& name, const std::string& def_val) const { return (*this)[name].def(def_val); }
+ int get_int(const std::string& name, int def_val) const { return (*this)[name].def(def_val); }
+ bool get_bool(const std::string& name, bool def_val) const { return (*this)[name].def(def_val); }
+
+ int set(const std::string& name, const std::string& val);
+ int erase(const std::string& name);
+
+ void derive_from(const JSONFormattable& jf);
+
+ void encode_json(const char *name, ceph::Formatter *f) const;
+
+ bool is_array() const { return type == FMT_ARRAY; }
+
+public:
static void generate_test_instances(std::list<JSONFormattable*>& o) {
o.push_back(new JSONFormattable);
o.push_back(new JSONFormattable);
o.back()->obj["foo"].value.quoted = true;
}
- const std::string& val() const {
- return value.str;
- }
-
- int val_int() const;
- long val_long() const;
- long long val_long_long() const;
- bool val_bool() const;
-
- const std::map<std::string, JSONFormattable> object() const {
- return obj;
- }
-
- const std::vector<JSONFormattable>& array() const {
- return arr;
- }
-
- const JSONFormattable& operator[](const std::string& name) const;
- const JSONFormattable& operator[](size_t index) const;
-
- JSONFormattable& operator[](const std::string& name);
- JSONFormattable& operator[](size_t index);
-
- operator std::string() const {
- return value.str;
- }
-
- explicit operator int() const {
- return val_int();
- }
-
- explicit operator long() const {
- return val_long();
- }
-
- explicit operator long long() const {
- return val_long_long();
- }
-
- explicit operator bool() const {
- return val_bool();
- }
-
- template<class T>
- T operator[](const std::string& name) const {
- return this->operator[](name)(T());
- }
-
- template<class T>
- T operator[](const std::string& name) {
- return this->operator[](name)(T());
- }
-
- std::string operator ()(const char *def_val) const {
- return def(std::string(def_val));
- }
-
- int operator()(int def_val) const {
- return def(def_val);
- }
-
- bool operator()(bool def_val) const {
- return def(def_val);
- }
-
- bool exists(const std::string& name) const;
- bool exists(size_t index) const;
-
- std::string def(const std::string& def_val) const;
- int def(int def_val) const;
- bool def(bool def_val) const;
+};
+WRITE_CLASS_ENCODER(JSONFormattable)
- bool find(const std::string& name, std::string *val) const;
+static inline JSONFormattable default_formattable;
- std::string get(const std::string& name, const std::string& def_val) const;
+inline JSONFormattable& JSONFormattable::operator[](const std::string& name) {
+ if (const auto i = obj.find(name); end(obj) != i)
+ return i->second;
+
+ return default_formattable;
+}
- int get_int(const std::string& name, int def_val) const;
- bool get_bool(const std::string& name, bool def_val) const;
+inline const JSONFormattable& JSONFormattable::operator[](const std::string& name) const {
+ return const_cast<JSONFormattable *>(this)->operator[](name);
+}
- int set(const std::string& name, const std::string& val);
- int erase(const std::string& name);
+inline JSONFormattable& JSONFormattable::operator[](size_t index) {
+ return index >= arr.size() ? default_formattable : arr[index];
+}
- void derive_from(const JSONFormattable& jf);
+inline const JSONFormattable& JSONFormattable::operator[](size_t index) const {
+ return const_cast<JSONFormattable *>(this)->operator[](index);
+}
- void encode_json(const char *name, ceph::Formatter *f) const;
+inline void encode_json(const char *name, const JSONFormattable& v, Formatter *f)
+{
+ v.encode_json(name, f);
+}
- bool is_array() const {
- return (type == FMT_ARRAY);
+inline 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, arr, f);
+ break;
+ case JSONFormattable::FMT_OBJ:
+ f->open_object_section(name);
+ for (auto iter : obj) {
+ ::encode_json(iter.first.c_str(), iter.second, f);
+ }
+ f->close_section();
+ break;
+ case JSONFormattable::FMT_NONE:
+ break;
}
-};
-WRITE_CLASS_ENCODER(JSONFormattable)
-
-void encode_json(const char *name, const JSONFormattable& v, ceph::Formatter *f);
+}
#endif