]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Consolidate codecs; views, buffer::list, etc..
authorJesse F. Williamson <1643380+chardan@users.noreply.github.com>
Sat, 4 Jan 2025 01:41:24 +0000 (17:41 -0800)
committerJesse F. Williamson <jfw@ibm.com>
Mon, 17 Mar 2025 17:41:27 +0000 (10:41 -0700)
Move JSON encoding from source file into inline header functions;
reorganize. Consolidate mostly-similar implementations with templates.

Allow string_view, buffer::list to ceph_json parse(); drive-by
cleanups. Assorted extensions (string_view for most search functions,
etc.)

Signed-off-by: Jesse F. Williamson <jfw@ibm.com>
21 files changed:
src/common/ceph_json.cc
src/common/ceph_json.h
src/mgr/Mgr.cc
src/mgr/MgrContext.h
src/rgw/rgw_acl_swift.cc
src/rgw/rgw_auth_keystone.cc
src/rgw/rgw_common.cc
src/rgw/rgw_keystone.cc
src/rgw/rgw_metadata.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_opa.cc
src/rgw/rgw_period_puller.cc
src/rgw/rgw_policy_s3.cc
src/rgw/rgw_rest.h
src/rgw/rgw_rest_conn.h
src/rgw/rgw_rest_role.cc
src/rgw/rgw_rest_sts.cc
src/rgw/rgw_token.h
src/test/common/test_json_formattable.cc
src/test/common/test_json_formatter.cc

index 1b54d6f71cecaca79e5b6eeca61ecfcd1ad7de11..af29226a87a191fb42c4f6856d7e86100ec0c51d 100644 (file)
@@ -6,51 +6,28 @@
 #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) {
@@ -58,68 +35,6 @@ 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
@@ -127,106 +42,68 @@ bool JSONObj::get_data(const string& key, data_val *dest)
 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:
@@ -243,7 +120,8 @@ bool JSONParser::parse(const char *buf_, int len)
   // 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;
    }
@@ -252,35 +130,6 @@ bool JSONParser::parse(const char *buf_, int len)
   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)
 {
@@ -289,7 +138,7 @@ 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);
@@ -297,418 +146,13 @@ bool JSONParser::parse(const char *file_name)
  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 {
@@ -717,7 +161,8 @@ 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) {}
 };
@@ -732,12 +177,14 @@ static int parse_entity(const string& s, vector<field_entity> *result)
       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);
@@ -745,16 +192,22 @@ static int parse_entity(const string& s, vector<field_entity> *result)
       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;
@@ -779,7 +232,7 @@ int JSONFormattable::set(const string& name, const string& val)
 
   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;
@@ -914,36 +367,10 @@ void JSONFormattable::derive_from(const JSONFormattable& parent)
   }
 }
 
-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);
@@ -959,7 +386,7 @@ bool JSONFormattable::handle_open_section(std::string_view name,
                                           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,
@@ -967,7 +394,7 @@ bool JSONFormattable::handle_open_section(std::string_view name,
        */
     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);
index dcdc9b995fb4f37e4923e457c7d5f5d43c35ed77..a52a602e036dd284b6c60f452a0392944cbed547 100644 (file)
 #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;
@@ -71,41 +171,90 @@ protected:
 
   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();
 };
@@ -116,25 +265,38 @@ inline std::ostream& operator<<(std::ostream &out, const JSONObj::data_val& dv)
    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;
@@ -143,29 +305,47 @@ public:
   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)
 {
@@ -177,183 +357,149 @@ inline void decode_json_obj(std::string& 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()) {
@@ -379,7 +525,7 @@ bool JSONDecoder::decode_json(const char *name, T& val, JSONObj *obj, bool manda
 }
 
 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();
 
@@ -404,7 +550,7 @@ bool JSONDecoder::decode_json(const char *name, C& container, void (*cb)(C&, JSO
 }
 
 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()) {
@@ -423,7 +569,7 @@ void JSONDecoder::decode_json(const char *name, T& val, const T& default_val, JS
 }
 
 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()) {
@@ -449,7 +595,7 @@ bool JSONDecoder::decode_json(const char *name, boost::optional<T>& val, JSONObj
 }
 
 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()) {
@@ -479,7 +625,7 @@ class JSONEncodeFilter
 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;
@@ -488,8 +634,6 @@ public:
   template <class T>
   class Handler : public HandlerBase {
   public:
-    virtual ~Handler() {}
-
     std::type_index get_type() override {
       return std::type_index(typeid(const T&));
     }
@@ -515,8 +659,19 @@ public:
   }
 };
 
+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);
@@ -524,7 +679,8 @@ static void encode_json_impl(const char *name, const T& val, ceph::Formatter *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"));
 
@@ -534,150 +690,82 @@ static void encode_json(const char *name, const T& val, ceph::Formatter *f)
   }
 }
 
-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,
@@ -716,27 +804,17 @@ void encode_json_map(const char *name, const char *index_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);
@@ -746,7 +824,6 @@ void encode_json_map(const char *name, const boost::container::flat_map<K, V>& m
   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,
@@ -785,21 +862,42 @@ void encode_json_map(const char *name, const char *index_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;
@@ -810,7 +908,8 @@ protected:
   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);
   }
@@ -896,6 +995,61 @@ public:
         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);
@@ -924,98 +1078,54 @@ public:
     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
index 7e0b4c26f1c39e9c6bbf3907668d3aa0ff64e9d0..ccd35375a9b96ff4f747a2bb20fe3b66b81324cc 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "osdc/Objecter.h"
 #include "common/errno.h"
+#include "common/ceph_json.h"
 #include "mon/MonClient.h"
 #include "include/stringify.h"
 #include "global/global_context.h"
@@ -79,7 +80,6 @@ Mgr::~Mgr()
 }
 
 void MetadataUpdate::finish(int r)
-try
 {
   daemon_state.clear_updating(key);
   if (r == 0) {
@@ -87,8 +87,8 @@ try
         key.type == "mgr" || key.type == "mon") {
 
       std::error_code ec;
-
-      boost::json::value json_result = boost::json::parse(outbl.to_str(), ec);
+      auto json_result = boost::json::parse(outbl.to_str(), ec, boost::json::storage_ptr(), 
+                                           { .allow_invalid_utf8 = true });
      
       if (ec) {
         dout(1) << "mon returned invalid JSON for " << key << dendl;
@@ -162,10 +162,6 @@ try
            << ": " << cpp_strerror(r) << dendl;
   }
 }
-catch(const std::exception& e)
-{
-    dout(1) << "!!JFW: mon caught: " << e.what() << dendl;
-}
 
 void Mgr::background_init(Context *completion)
 {
@@ -197,7 +193,7 @@ std::map<std::string, std::string> Mgr::load_store()
   std::map<std::string, std::string> loaded;
   
   for (auto &key_str : cmd.json_result.get_array()) {
-    std::string const key = key_str.get_string().c_str();
+    boost::json::string_view key = key_str.get_string();
     
     dout(20) << "saw key '" << key << "'" << dendl;
 
@@ -401,7 +397,6 @@ void Mgr::init()
 }
 
 void Mgr::load_all_metadata()
-try
 {
   ceph_assert(ceph_mutex_is_locked_by_me(lock));
 
@@ -430,15 +425,15 @@ try
     }
 
     DaemonStatePtr dm = std::make_shared<DaemonState>(daemon_state.types);
-    dm->key = DaemonKey{"mds",
-                        daemon_meta.at("name").get_string().c_str()};
-    dm->hostname = daemon_meta.at("hostname").get_string().c_str();
 
+    dm->key = DaemonKey{"mds", boost::json::value_to<std::string>(daemon_meta.at("name")) }; 
+    dm->hostname = boost::json::value_to<std::string>(daemon_meta.at("hostname")); 
+   
     daemon_meta.erase("name");
     daemon_meta.erase("hostname");
 
     for (const auto &[key, val] : daemon_meta) {
-      dm->metadata.emplace(key, val.get_string());
+      dm->metadata.emplace(key, boost::json::value_to<std::string>(val)); 
     }
 
     daemon_state.insert(dm);
@@ -452,9 +447,8 @@ try
     }
 
     DaemonStatePtr dm = std::make_shared<DaemonState>(daemon_state.types);
-    dm->key = DaemonKey{"mon",
-                        daemon_meta.at("name").get_string().c_str()};
-    dm->hostname = daemon_meta.at("hostname").get_string().c_str();
+    dm->key = DaemonKey{"mon", boost::json::value_to<std::string>(daemon_meta.at("name")) };
+    dm->hostname = daemon_meta.at("hostname").get_string();
 
     daemon_meta.erase("name");
     daemon_meta.erase("hostname");
@@ -493,10 +487,6 @@ try
     daemon_state.insert(dm);
   }
 }
-catch(const std::exception& e)
-{
-      dout(1) << "JFW: load_all_metadata(): caught " << e.what() << dendl;
-}
 
 void Mgr::handle_osd_map()
 {
index 5b4f8727fde5e254930f4fe9eff1f2e5d0f53f17..eb17a52a534b16f8d8bd2bbc009f36a456b808c8 100644 (file)
@@ -67,14 +67,6 @@ public:
 
       if(ec)
        r = -EINVAL;
-
-/*JFW:
-      bool read_ok = json_spirit::read(
-          outbl.to_str(), json_result);
-      if (!read_ok) {
-        r = -EINVAL;
-      }
-*/
     }
   }
 };
index 67e0daf5b72e29ca1a533ad24ceea7c73ca797bd..e7ed5a393483389c26f1349fc19fe2804fc4a1df 100644 (file)
@@ -286,7 +286,7 @@ int create_account_policy(const DoutPrefixProvider* dpp,
   auto& acl = policy.get_acl();
 
   JSONParser parser;
-  if (!parser.parse(acl_str.c_str(), acl_str.length())) {
+  if (!parser.parse(acl_str)) {
     ldpp_dout(dpp, 0) << "ERROR: JSONParser::parse returned error=" << dendl;
     return -EINVAL;
   }
index 7f3bd66a1b95cc0d25e78259403860c1792b8efb..33bf75bb347423750ee9a81430b139b52e7d803c 100644 (file)
@@ -546,7 +546,7 @@ auto EC2Engine::get_secret_from_keystone(const DoutPrefixProvider* dpp,
   /* now parse response */
 
   JSONParser parser;
-  if (! parser.parse(token_body_bl.c_str(), token_body_bl.length())) {
+  if (! parser.parse(token_body_bl)) {
     ldpp_dout(dpp, 0) << "Keystone credential parse error: malformed json" << dendl;
     return make_pair(boost::none, -EINVAL);
   }
index 5ecb2a1e17d8326deda8c5601d5b01f36e4b1358..7336c6ae546a0ca5b93903791ede81f989195e9e 100644 (file)
@@ -7,7 +7,6 @@
 #include <string>
 #include <boost/tokenizer.hpp>
 
-#include "json_spirit/json_spirit.h"
 #include "common/ceph_json.h"
 #include "common/Formatter.h"
 #include "common/versioned_variant.h"
index 2767cea06def163ce51b2792a769bad3ec24bc28..aab3b47ba1b820a98757353bf74199ebe7c80353 100644 (file)
@@ -285,7 +285,7 @@ int TokenEnvelope::parse(const DoutPrefixProvider *dpp,
                          ceph::bufferlist& bl)
 {
   JSONParser parser;
-  if (! parser.parse(bl.c_str(), bl.length())) {
+  if (! parser.parse(bl)) {
     ldpp_dout(dpp, 0) << "Keystone token parse error: malformed json" << dendl;
     return -EINVAL;
   }
index 92b03e41bcdc15a15e7c90399e7ffe716850ec51..65ada37dcc06ded38306dc86af2b5bfa2957720a 100644 (file)
@@ -309,7 +309,7 @@ int RGWMetadataManager::put(string& metadata_key, bufferlist& bl,
   }
 
   JSONParser parser;
-  if (!parser.parse(bl.c_str(), bl.length())) {
+  if (!parser.parse(bl)) {
     return -EINVAL;
   }
 
index a96eb3ef6024fdcfd976a73f939a6daae619b509..50ed075d7a8ff8b282dc150eab4b2b31b79352f9 100644 (file)
@@ -159,7 +159,7 @@ int rgw_forward_request_to_master(const DoutPrefixProvider* dpp,
   if (ret < 0) {
     return ret;
   }
-  if (jp && !jp->parse(outdata.c_str(), outdata.length())) {
+  if (jp && !jp->parse(outdata)) {
     ldpp_dout(dpp, 0) << "failed parsing response from master zonegroup" << dendl;
     return -EINVAL;
   }
index cbe441c140bfdf5883812ad6964108a7e367026f..a0314cf1c3e6d86897820feecf699ad4e63a44f9 100644 (file)
@@ -158,7 +158,7 @@ int rgw_rest_get_json_input(CephContext *cct, req_state *s, T& out,
 
   JSONParser parser;
 
-  if (!parser.parse(data.c_str(), data.length())) {
+  if (!parser.parse(data)) {
     return -EINVAL;
   }
 
index 0bda4d62a51add8bd224a9b0a73727d9ea727931..93b8693dbdf580444ce88aa676448b4c76f69910 100644 (file)
@@ -79,7 +79,7 @@ int rgw_opa_authorize(RGWOp *& op,
 
   /* check OPA response */
   JSONParser parser;
-  if (!parser.parse(bl.c_str(), bl.length())) {
+  if (!parser.parse(bl)) {
     ldpp_dout(op, 2) << "OPA parse error: malformed json" << dendl;
     return -EINVAL;
   }
index ea2f28e567b46dfae0a58dc279519767b66494c9..6a8cca0aedc5d6f56e7a7895f10a9bdf23f0e1f5 100644 (file)
@@ -46,7 +46,7 @@ int pull_period(const DoutPrefixProvider *dpp, RGWRESTConn* conn, const std::str
   }
 
   JSONParser parser;
-  r = parser.parse(data.c_str(), data.length());
+  r = parser.parse(data);
   if (r < 0) {
     ldpp_dout(dpp, -1) << "request failed: " << cpp_strerror(-r) << dendl;
     return r;
index 7b8ad0452cbc5618c39b1e4ea74e90a5a0276696..f9d324fc36f6c81b413fe9198dd544919dae9329 100644 (file)
@@ -248,7 +248,7 @@ int RGWPolicy::from_json(bufferlist& bl, string& err_msg)
 {
   JSONParser parser;
 
-  if (!parser.parse(bl.c_str(), bl.length())) {
+  if (!parser.parse(bl)) {  
     err_msg = "Malformed JSON";
     dout(0) << "malformed json" << dendl;
     return -EINVAL;
index a302257e428bc33264f5d5f09b1d8fb02ab6447d..a5e7bdbf56af92c067152a2a287395453f967619 100644 (file)
@@ -62,7 +62,7 @@ std::tuple<int, bufferlist > rgw_rest_get_json_input_keep_data(CephContext *cct,
 
   JSONParser parser;
 
-  if (!parser.parse(data.c_str(), data.length())) {
+  if (!parser.parse(data)) {
     return std::make_tuple(-EINVAL, std::move(data));
   }
 
index 7abf86a3d3f0932abb6d6a5851c2a0cbf3f707e0..13bc607731983d154e2029869a8bf32016ae868b 100644 (file)
@@ -17,7 +17,7 @@ template<class T>
 inline int parse_decode_json(T& t, bufferlist& bl)
 {
   JSONParser p;
-  if (!p.parse(bl.c_str(), bl.length())) {
+  if (!p.parse(bl)) {
     return -EINVAL;
   }
 
index a3733c175cc3c1c0b5d50e9bcef6b2f050883830..7e924c640ffdd2e8831a017bab5074066c685658 100644 (file)
@@ -449,7 +449,7 @@ int RGWModifyRoleTrustPolicy::init_processing(optional_yield y)
   }
 
   JSONParser p;
-  if (!p.parse(trust_policy.c_str(), trust_policy.length())) {
+  if (!p.parse(trust_policy)) {
     ldpp_dout(this, 20) << "ERROR: failed to parse assume role policy doc" << dendl;
     return -ERR_MALFORMED_DOC;
   }
index c0c6b5e89acd2c873f41b926ac896f93796ef9ff..7b36511f0e46ce24a4832ca8863b0f6f162081e3 100644 (file)
@@ -327,7 +327,7 @@ WebTokenEngine::get_cert_url(const string& iss, const DoutPrefixProvider *dpp, o
   ldpp_dout(dpp, 20) << "JSON Response is: " << openidc_resp.c_str() << dendl;
 
   JSONParser parser;
-  if (parser.parse(openidc_resp.c_str(), openidc_resp.length())) {
+  if (parser.parse(openidc_resp)) {
     JSONObj::data_val val;
     if (parser.get_data("jwks_uri", &val)) {
       cert_url = val.str.c_str();
@@ -364,7 +364,7 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
     ldpp_dout(dpp, 20) << "JSON Response is: " << cert_resp.c_str() << dendl;
 
     JSONParser parser;
-    if (parser.parse(cert_resp.c_str(), cert_resp.length())) {
+    if (parser.parse(cert_resp)) {
       JSONObj::data_val val;
       if (parser.get_data("keys", &val)) {
         if (val.str[0] == '[') {
index b2476596bec349efce1286bf22a33143d9b997fc..3bf9adaf00b5bc2a77c020ec7bfa5c5c33f5df44 100644 (file)
@@ -82,13 +82,13 @@ namespace rgw {
 
     explicit RGWToken(const string& json) {
       JSONParser p;
-      p.parse(json.c_str(), json.length());
+      p.parse(json);
       JSONDecoder::decode_json(RGWToken::type_name, *this, &p);
     }
 
     RGWToken& operator=(const std::string& json) {
       JSONParser p;
-      p.parse(json.c_str(), json.length());
+      p.parse(json);
       JSONDecoder::decode_json(RGWToken::type_name, *this, &p);
       return *this;
     }
index 013736f0ec884488f476b57fff03d753bc832266..2da3e67819f2dac182f23bd2638aa4bf14bb2052 100644 (file)
@@ -37,7 +37,7 @@ static void get_jf(const string& s, JSONFormattable *f)
   }
 }
 
-TEST(formatable, str) {
+TEST(formattable, str) {
   JSONFormattable f;
   get_jf("{ \"foo\": \"bar\" }", &f);
   ASSERT_EQ((string)f["foo"], "bar");
@@ -45,7 +45,7 @@ TEST(formatable, str) {
   ASSERT_EQ((string)f["fooz"]("lala"), "lala");
 }
 
-TEST(formatable, str2) {
+TEST(formattable, str2) {
   JSONFormattable f;
   get_jf("{ \"foo\": \"bar\" }", &f);
   ASSERT_EQ((string)f["foo"], "bar");
@@ -61,13 +61,13 @@ TEST(formatable, str2) {
 
 }
 
-TEST(formatable, str3) {
+TEST(formattable, str3) {
   JSONFormattable f;
   get_jf("{ \"foo\": \"1234bar56\" }", &f);
   ASSERT_EQ((string)f["foo"], "1234bar56");
 }
 
-TEST(formatable, int) {
+TEST(formattable, int) {
   JSONFormattable f;
   get_jf("{ \"foo\": 1 }", &f);
   ASSERT_EQ((int)f["foo"], 1);
@@ -82,7 +82,7 @@ TEST(formatable, int) {
   ASSERT_EQ((int)f2["fooz"](111), 123);
 }
 
-TEST(formatable, bool) {
+TEST(formattable, bool) {
   JSONFormattable f;
   get_jf("{ \"foo\": \"true\" }", &f);
   ASSERT_EQ((bool)f["foo"], true);
@@ -94,7 +94,7 @@ TEST(formatable, bool) {
   ASSERT_EQ((bool)f["foo"], false);
 }
 
-TEST(formatable, nested) {
+TEST(formattable, nested) {
   JSONFormattable f;
   get_jf("{ \"obj\": { \"foo\": 1, \"inobj\": { \"foo\": 2 } } }", &f);
   ASSERT_EQ((int)f["foo"], 0);
@@ -102,7 +102,7 @@ TEST(formatable, nested) {
   ASSERT_EQ((int)f["obj"]["inobj"]["foo"], 2);
 }
 
-TEST(formatable, array) {
+TEST(formattable, array) {
   JSONFormattable f;
   get_jf("{ \"arr\": [ { \"foo\": 1, \"inobj\": { \"foo\": 2 } }," 
          "{ \"foo\": 2 } ] }", &f);
@@ -123,7 +123,7 @@ TEST(formatable, array) {
   }
 }
 
-TEST(formatable, bin_encode) {
+TEST(formattable, bin_encode) {
   JSONFormattable f, f2;
   get_jf("{ \"arr\": [ { \"foo\": 1, \"bar\": \"aaa\", \"inobj\": { \"foo\": 2 } }," 
          "{ \"foo\": 2, \"inobj\": { \"foo\": 3 } } ] }", &f);
@@ -155,7 +155,7 @@ TEST(formatable, bin_encode) {
 
 }
 
-TEST(formatable, json_encode) {
+TEST(formattable, json_encode) {
   JSONFormattable f, f2;
   get_jf("{ \"arr\": [ { \"foo\": 1, \"bar\": \"aaa\", \"inobj\": { \"foo\": 2 } }," 
          "{ \"foo\": 2, \"inobj\": { \"foo\": 3 } } ] }", &f);
@@ -180,7 +180,7 @@ TEST(formatable, json_encode) {
 
 }
 
-TEST(formatable, set) {
+TEST(formattable, set) {
   JSONFormattable f, f2;
 
   f.set("", "{ \"abc\": \"xyz\"}");
@@ -199,13 +199,13 @@ TEST(formatable, set) {
   ASSERT_EQ((int)f["obj"]["c"], 30);
 }
 
-TEST(formatable, set2) {
+TEST(formattable, set2) {
   JSONFormattable f;
   f.set("foo", "1234bar56");
   ASSERT_EQ((string)f["foo"], "1234bar56");
 }
 
-TEST(formatable, erase) {
+TEST(formattable, erase) {
   JSONFormattable f, f2;
 
   f.set("", "{ \"abc\": \"xyz\"}");
@@ -239,7 +239,7 @@ static void dumpf(const JSONFormattable& f) {
   dumpt(f, "f");
 }
 
-TEST(formatable, set_array) {
+TEST(formattable, set_array) {
   JSONFormattable f, f2;
 
   f.set("asd[0]", "\"xyz\"");
@@ -271,7 +271,7 @@ TEST(formatable, set_array) {
   ASSERT_EQ((string)f2[0]["field"], "xyz");
 }
 
-TEST(formatable, erase_array) {
+TEST(formattable, erase_array) {
   JSONFormattable f;
 
   f.set("asd[0]", "\"xyz\"");
@@ -314,7 +314,7 @@ void formatter_convert(JSONFormatter& formatter, JSONFormattable *dest)
   get_jf(ss.str(), dest);
 }
 
-TEST(formatable, encode_simple) {
+TEST(formattable, encode_simple) {
   JSONFormattable f;
 
   encode_json("foo", "bar", &f);
@@ -421,7 +421,7 @@ struct struct2 {
 };
 
 
-TEST(formatable, encode_struct) {
+TEST(formattable, encode_struct) {
   JSONFormattable f;
 
   struct2 s2;
index d0ddd262c0a1ab655cfb18bc335ae0b140097341..144a52ef524d02f35a7726817a4480823636eb14 100644 (file)
@@ -3,6 +3,7 @@
 /*
  * Ceph - scalable distributed file system
  *
+ * Copyright (C) 2025 IBM
  * Copyright (C) 2018 Red Hat Inc.
  *
  *  This library is free software; you can redistribute it and/or
 
 using namespace std;
 
+const string outstring = 
+"{\"pg_ready\":true, \"pg_stats\":[ { \"pgid\":\"1.0\", \"version\":\"16'56\",\"reported_seq\":\"62\",\"reported_epoch\":\"20\",\"state\":\"active+clean+inconsistent\",\"last_fresh\":\"2018-12-18 15:21:22.173804\",\"last_change\":\"2018-12-18 15:21:22.173804\",\"last_active\":\"2018-12-18 15:21:22.173804\",\"last_peered\":\"2018-12-18 15:21:22.173804\",\"last_clean\":\"2018-12-18 15:21:22.173804\",\"last_became_active\":\"2018-12-18 15:21:21.347685\",\"last_became_peered\":\"2018-12-18 15:21:21.347685\",\"last_unstale\":\"2018-12-18 15:21:22.173804\",\"last_undegraded\":\"2018-12-18 15:21:22.173804\",\"last_fullsized\":\"2018-12-18 15:21:22.173804\",\"mapping_epoch\":19,\"log_start\":\"0'0\",\"ondisk_log_start\":\"0'0\",\"created\":7,\"last_epoch_clean\":20,\"parent\":\"0.0\",\"parent_split_bits\":0,\"last_scrub\":\"16'56\",\"last_scrub_stamp\":\"2018-12-18 15:21:22.173684\",\"last_deep_scrub\":\"0'0\",\"last_deep_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"last_clean_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"log_size\":56,\"ondisk_log_size\":56,\"stats_invalid\":false,\"dirty_stats_invalid\":false,\"omap_stats_invalid\":false,\"hitset_stats_invalid\":false,\"hitset_bytes_stats_invalid\":false,\"pin_stats_invalid\":false,\"manifest_stats_invalid\":false,\"snaptrimq_len\":0,\"stat_sum\":{\"num_bytes\":24448,\"num_objects\":36,\"num_object_clones\":20,\"num_object_copies\":36,\"num_objects_missing_on_primary\":0,\"num_objects_missing\":0,\"num_objects_degraded\":0,\"num_objects_misplaced\":0,\"num_objects_unfound\":0,\"num_objects_dirty\":36,\"num_whiteouts\":3,\"num_read\":0,\"num_read_kb\":0,\"num_write\":36,\"num_write_kb\":50,\"num_scrub_errors\":20,\"num_shallow_scrub_errors\":20,\"num_deep_scrub_errors\":0,\"num_objects_recovered\":0,\"num_bytes_recovered\":0,\"num_keys_recovered\":0,\"num_objects_omap\":0,\"num_objects_hit_set_archive\":0,\"num_bytes_hit_set_archive\":0,\"num_flush\":0,\"num_flush_kb\":0,\"num_evict\":0,\"num_evict_kb\":0,\"num_promote\":0,\"num_flush_mode_high\":0,\"num_flush_mode_low\":0,\"num_evict_mode_some\":0,\"num_evict_mode_full\":0,\"num_objects_pinned\":0,\"num_legacy_snapsets\":0,\"num_large_omap_objects\":0,\"num_objects_manifest\":0},\"up\":[0],\"acting\":[0],\"blocked_by\":[],\"up_primary\":0,\"acting_primary\":0,\"purged_snaps\":[] }]}";
 
 TEST(formatter, bug_37706) {
   vector<std::string> pgs;
 
-   string outstring = 
-"{\"pg_ready\":true, \"pg_stats\":[ { \"pgid\":\"1.0\", \"version\":\"16'56\",\"reported_seq\":\"62\",\"reported_epoch\":\"20\",\"state\":\"active+clean+inconsistent\",\"last_fresh\":\"2018-12-18 15:21:22.173804\",\"last_change\":\"2018-12-18 15:21:22.173804\",\"last_active\":\"2018-12-18 15:21:22.173804\",\"last_peered\":\"2018-12-18 15:21:22.173804\",\"last_clean\":\"2018-12-18 15:21:22.173804\",\"last_became_active\":\"2018-12-18 15:21:21.347685\",\"last_became_peered\":\"2018-12-18 15:21:21.347685\",\"last_unstale\":\"2018-12-18 15:21:22.173804\",\"last_undegraded\":\"2018-12-18 15:21:22.173804\",\"last_fullsized\":\"2018-12-18 15:21:22.173804\",\"mapping_epoch\":19,\"log_start\":\"0'0\",\"ondisk_log_start\":\"0'0\",\"created\":7,\"last_epoch_clean\":20,\"parent\":\"0.0\",\"parent_split_bits\":0,\"last_scrub\":\"16'56\",\"last_scrub_stamp\":\"2018-12-18 15:21:22.173684\",\"last_deep_scrub\":\"0'0\",\"last_deep_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"last_clean_scrub_stamp\":\"2018-12-18 15:21:06.514438\",\"log_size\":56,\"ondisk_log_size\":56,\"stats_invalid\":false,\"dirty_stats_invalid\":false,\"omap_stats_invalid\":false,\"hitset_stats_invalid\":false,\"hitset_bytes_stats_invalid\":false,\"pin_stats_invalid\":false,\"manifest_stats_invalid\":false,\"snaptrimq_len\":0,\"stat_sum\":{\"num_bytes\":24448,\"num_objects\":36,\"num_object_clones\":20,\"num_object_copies\":36,\"num_objects_missing_on_primary\":0,\"num_objects_missing\":0,\"num_objects_degraded\":0,\"num_objects_misplaced\":0,\"num_objects_unfound\":0,\"num_objects_dirty\":36,\"num_whiteouts\":3,\"num_read\":0,\"num_read_kb\":0,\"num_write\":36,\"num_write_kb\":50,\"num_scrub_errors\":20,\"num_shallow_scrub_errors\":20,\"num_deep_scrub_errors\":0,\"num_objects_recovered\":0,\"num_bytes_recovered\":0,\"num_keys_recovered\":0,\"num_objects_omap\":0,\"num_objects_hit_set_archive\":0,\"num_bytes_hit_set_archive\":0,\"num_flush\":0,\"num_flush_kb\":0,\"num_evict\":0,\"num_evict_kb\":0,\"num_promote\":0,\"num_flush_mode_high\":0,\"num_flush_mode_low\":0,\"num_evict_mode_some\":0,\"num_evict_mode_full\":0,\"num_objects_pinned\":0,\"num_legacy_snapsets\":0,\"num_large_omap_objects\":0,\"num_objects_manifest\":0},\"up\":[0],\"acting\":[0],\"blocked_by\":[],\"up_primary\":0,\"acting_primary\":0,\"purged_snaps\":[] }]}";
-
-
    JSONParser parser;
    ASSERT_TRUE(parser.parse(outstring.c_str(), outstring.size()));
 
@@ -126,3 +125,62 @@ TEST(formatter, dump_large_item) {
   EXPECT_TRUE(parser.parse(bl.c_str(), bl.length()));
   EXPECT_EQ(parser.find_obj("Location")->get_data(), full_url);
 }
+
+TEST(formatter, parse_types) {
+  // Check that JSONParser::parse() works with expected data types 
+  // (otherwise at least indirectly tested elsewhere):
+
+  const auto json_input = R"({"expiration": "2025-01-17T10:26:46Z", "conditions": [{"bucket": "user-hqfadib9zxyj2pygevewzf45t-1"}, ["starts-with", "$key", "foo"], {"acl": "private"}, ["starts-with", "$Content-Type", "text/plain"], ["content-length-range", 0, 1024]]})";
+  
+  { 
+  JSONParser parser;
+
+  ASSERT_TRUE(parser.parse(outstring));
+
+  JSONObj *pgstat_obj = parser.find_obj("pg_stats");
+  EXPECT_TRUE(pgstat_obj);
+  }
+
+  { 
+  JSONParser parser;
+
+  ASSERT_TRUE(parser.parse(std::string_view { outstring }));
+
+  JSONObj *pgstat_obj = parser.find_obj("pg_stats");
+  EXPECT_TRUE(pgstat_obj);
+  }
+
+  {
+  JSONParser parser;
+
+  buffer::list bl;
+  bl.append(outstring);
+  EXPECT_TRUE(parser.parse(bl));
+  
+  JSONObj *pgstat_obj = parser.find_obj("pg_stats");
+  EXPECT_TRUE(pgstat_obj);
+  }
+
+  {
+  JSONParser parser;
+  buffer::list bl;
+  bl.append(json_input);
+
+  ASSERT_TRUE(parser.parse(bl));
+
+  JSONObjIter oi = parser.find_first();
+
+  JSONObj *o0 = *oi;
+  ASSERT_TRUE(nullptr != o0);
+
+  ++oi;
+  JSONObj *o1 = *oi;
+  ASSERT_TRUE(nullptr != o1);
+  JSONObj *conditions = parser.find_obj("conditions");
+  ASSERT_TRUE(nullptr != conditions);
+
+  JSONObj *expiration = parser.find_obj("expiration");
+  ASSERT_TRUE(nullptr != expiration);
+  }
+}