summaryrefslogtreecommitdiffstats
path: root/src/common/hobject.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/hobject.cc611
1 files changed, 611 insertions, 0 deletions
diff --git a/src/common/hobject.cc b/src/common/hobject.cc
new file mode 100644
index 000000000..1aee4cc42
--- /dev/null
+++ b/src/common/hobject.cc
@@ -0,0 +1,611 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <charconv>
+
+#include "hobject.h"
+#include "common/Formatter.h"
+
+using std::list;
+using std::ostream;
+using std::set;
+using std::string;
+
+using ceph::bufferlist;
+using ceph::Formatter;
+
+static void append_escaped(const string &in, string *out)
+{
+ for (string::const_iterator i = in.begin(); i != in.end(); ++i) {
+ if (*i == '%') {
+ out->push_back('%');
+ out->push_back('p');
+ } else if (*i == '.') {
+ out->push_back('%');
+ out->push_back('e');
+ } else if (*i == '_') {
+ out->push_back('%');
+ out->push_back('u');
+ } else {
+ out->push_back(*i);
+ }
+ }
+}
+
+set<string> hobject_t::get_prefixes(
+ uint32_t bits,
+ uint32_t mask,
+ int64_t pool)
+{
+ uint32_t len = bits;
+ while (len % 4 /* nibbles */) len++;
+
+ set<uint32_t> from;
+ if (bits < 32)
+ from.insert(mask & ~((uint32_t)(~0) << bits));
+ else if (bits == 32)
+ from.insert(mask);
+ else
+ ceph_abort();
+
+
+ set<uint32_t> to;
+ for (uint32_t i = bits; i < len; ++i) {
+ for (set<uint32_t>::iterator j = from.begin();
+ j != from.end();
+ ++j) {
+ to.insert(*j | (1U << i));
+ to.insert(*j);
+ }
+ to.swap(from);
+ to.clear();
+ }
+
+ char buf[20];
+ char *t = buf;
+ uint64_t poolid(pool);
+ t += snprintf(t, sizeof(buf), "%.*llX", 16, (long long unsigned)poolid);
+ *(t++) = '.';
+ string poolstr(buf, t - buf);
+ set<string> ret;
+ for (set<uint32_t>::iterator i = from.begin();
+ i != from.end();
+ ++i) {
+ uint32_t revhash(hobject_t::_reverse_nibbles(*i));
+ snprintf(buf, sizeof(buf), "%.*X", (int)(sizeof(revhash))*2, revhash);
+ ret.insert(poolstr + string(buf, len/4));
+ }
+ return ret;
+}
+
+string hobject_t::to_str() const
+{
+ string out;
+
+ char snap_with_hash[1000];
+ char *t = snap_with_hash;
+ const char *end = t + sizeof(snap_with_hash);
+
+ uint64_t poolid(pool);
+ t += snprintf(t, end - t, "%.*llX", 16, (long long unsigned)poolid);
+
+ uint32_t revhash(get_nibblewise_key_u32());
+ t += snprintf(t, end - t, ".%.*X", 8, revhash);
+
+ if (snap == CEPH_NOSNAP)
+ t += snprintf(t, end - t, ".head");
+ else if (snap == CEPH_SNAPDIR)
+ t += snprintf(t, end - t, ".snapdir");
+ else
+ t += snprintf(t, end - t, ".%llx", (long long unsigned)snap);
+
+ out.append(snap_with_hash, t);
+
+ out.push_back('.');
+ append_escaped(oid.name, &out);
+ out.push_back('.');
+ append_escaped(get_key(), &out);
+ out.push_back('.');
+ append_escaped(nspace, &out);
+
+ return out;
+}
+
+void hobject_t::encode(bufferlist& bl) const
+{
+ ENCODE_START(4, 3, bl);
+ encode(key, bl);
+ encode(oid, bl);
+ encode(snap, bl);
+ encode(hash, bl);
+ encode(max, bl);
+ encode(nspace, bl);
+ encode(pool, bl);
+ ceph_assert(!max || (*this == hobject_t(hobject_t::get_max())));
+ ENCODE_FINISH(bl);
+}
+
+void hobject_t::decode(bufferlist::const_iterator& bl)
+{
+ DECODE_START_LEGACY_COMPAT_LEN(4, 3, 3, bl);
+ if (struct_v >= 1)
+ decode(key, bl);
+ decode(oid, bl);
+ decode(snap, bl);
+ decode(hash, bl);
+ if (struct_v >= 2)
+ decode(max, bl);
+ else
+ max = false;
+ if (struct_v >= 4) {
+ decode(nspace, bl);
+ decode(pool, bl);
+ // for compat with hammer, which did not handle the transition
+ // from pool -1 -> pool INT64_MIN for MIN properly. this object
+ // name looks a bit like a pgmeta object for the meta collection,
+ // but those do not ever exist (and is_pgmeta() pool >= 0).
+ if (pool == -1 &&
+ snap == 0 &&
+ hash == 0 &&
+ !max &&
+ oid.name.empty()) {
+ pool = INT64_MIN;
+ ceph_assert(is_min());
+ }
+
+ // for compatibility with some earlier verisons which might encoded
+ // a non-canonical max object
+ if (max) {
+ *this = hobject_t::get_max();
+ }
+ }
+ DECODE_FINISH(bl);
+ build_hash_cache();
+}
+
+void hobject_t::decode(json_spirit::Value& v)
+{
+ using namespace json_spirit;
+ Object& o = v.get_obj();
+ for (Object::size_type i=0; i<o.size(); i++) {
+ Pair& p = o[i];
+ if (p.name_ == "oid")
+ oid.name = p.value_.get_str();
+ else if (p.name_ == "key")
+ key = p.value_.get_str();
+ else if (p.name_ == "snapid")
+ snap = p.value_.get_uint64();
+ else if (p.name_ == "hash")
+ hash = p.value_.get_int();
+ else if (p.name_ == "max")
+ max = p.value_.get_int();
+ else if (p.name_ == "pool")
+ pool = p.value_.get_int();
+ else if (p.name_ == "namespace")
+ nspace = p.value_.get_str();
+ }
+ build_hash_cache();
+}
+
+void hobject_t::dump(Formatter *f) const
+{
+ f->dump_string("oid", oid.name);
+ f->dump_string("key", key);
+ f->dump_int("snapid", snap);
+ f->dump_int("hash", hash);
+ f->dump_int("max", (int)max);
+ f->dump_int("pool", pool);
+ f->dump_string("namespace", nspace);
+}
+
+void hobject_t::generate_test_instances(list<hobject_t*>& o)
+{
+ o.push_back(new hobject_t);
+ o.push_back(new hobject_t);
+ o.back()->max = true;
+ o.push_back(new hobject_t(object_t("oname"), string(), 1, 234, -1, ""));
+ o.push_back(new hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
+ 67, 0, "n1"));
+ o.push_back(new hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"));
+}
+
+static void append_out_escaped(const string &in, string *out)
+{
+ for (auto c : in) {
+ int i = (int)(unsigned char)(c);
+ if (i <= 0x0f) {
+ char buf[4] = {'%', '0'};
+ std::to_chars(buf + 2, buf + 3, i, 16);
+ out->append(buf);
+ } else if (i < 32 || i >= 127 || i == '%' || i == ':' || i == '/') {
+ char buf[4] = {'%'};
+ std::to_chars(buf + 1, buf + 3, i, 16);
+ out->append(buf);
+ } else {
+ out->push_back(c);
+ }
+ }
+}
+
+static const char *decode_out_escaped(const char *in, string *out)
+{
+ while (*in && *in != ':') {
+ if (*in == '%') {
+ ++in;
+ char buf[3];
+ buf[0] = *in;
+ ++in;
+ buf[1] = *in;
+ buf[2] = 0;
+ int v = strtol(buf, NULL, 16);
+ out->push_back(v);
+ } else {
+ out->push_back(*in);
+ }
+ ++in;
+ }
+ return in;
+}
+
+ostream& operator<<(ostream& out, const hobject_t& o)
+{
+ if (o == hobject_t())
+ return out << "MIN";
+ if (o.is_max())
+ return out << "MAX";
+ out << o.pool << ':';
+ out << std::hex;
+ out.width(8);
+ out.fill('0');
+ out << o.get_bitwise_key_u32(); // << '~' << o.get_hash();
+ out.width(0);
+ out.fill(' ');
+ out << std::dec;
+ out << ':';
+ string v;
+ append_out_escaped(o.nspace, &v);
+ v.push_back(':');
+ append_out_escaped(o.get_key(), &v);
+ v.push_back(':');
+ append_out_escaped(o.oid.name, &v);
+ out << v << ':' << o.snap;
+ return out;
+}
+
+bool hobject_t::parse(const string &s)
+{
+ if (s == "MIN") {
+ *this = hobject_t();
+ return true;
+ }
+ if (s == "MAX") {
+ *this = hobject_t::get_max();
+ return true;
+ }
+
+ const char *start = s.c_str();
+ long long po;
+ unsigned h;
+ int r = sscanf(start, "%lld:%x:", &po, &h);
+ if (r != 2)
+ return false;
+ for (; *start && *start != ':'; ++start) ;
+ for (++start; *start && isxdigit(*start); ++start) ;
+ if (*start != ':')
+ return false;
+
+ string ns, k, name;
+ const char *p = decode_out_escaped(start + 1, &ns);
+ if (*p != ':')
+ return false;
+ p = decode_out_escaped(p + 1, &k);
+ if (*p != ':')
+ return false;
+ p = decode_out_escaped(p + 1, &name);
+ if (*p != ':')
+ return false;
+ start = p + 1;
+
+ unsigned long long sn;
+ if (strncmp(start, "head", 4) == 0) {
+ sn = CEPH_NOSNAP;
+ start += 4;
+ if (*start != 0)
+ return false;
+ } else {
+ r = sscanf(start, "%llx", &sn);
+ if (r != 1)
+ return false;
+ for (++start; *start && isxdigit(*start); ++start) ;
+ if (*start)
+ return false;
+ }
+
+ max = false;
+ pool = po;
+ set_hash(_reverse_bits(h));
+ nspace = ns;
+ oid.name = name;
+ set_key(k);
+ snap = sn;
+ return true;
+}
+
+int cmp(const hobject_t& l, const hobject_t& r)
+{
+ if (l.max < r.max)
+ return -1;
+ if (l.max > r.max)
+ return 1;
+ if (l.pool < r.pool)
+ return -1;
+ if (l.pool > r.pool)
+ return 1;
+ if (l.get_bitwise_key() < r.get_bitwise_key())
+ return -1;
+ if (l.get_bitwise_key() > r.get_bitwise_key())
+ return 1;
+ if (l.nspace < r.nspace)
+ return -1;
+ if (l.nspace > r.nspace)
+ return 1;
+ if (!(l.get_key().empty() && r.get_key().empty())) {
+ if (l.get_effective_key() < r.get_effective_key()) {
+ return -1;
+ }
+ if (l.get_effective_key() > r.get_effective_key()) {
+ return 1;
+ }
+ }
+ if (l.oid < r.oid)
+ return -1;
+ if (l.oid > r.oid)
+ return 1;
+ if (l.snap < r.snap)
+ return -1;
+ if (l.snap > r.snap)
+ return 1;
+ return 0;
+}
+
+
+
+// This is compatible with decode for hobject_t prior to
+// version 5.
+void ghobject_t::encode(bufferlist& bl) const
+{
+ // when changing this, remember to update encoded_size() too.
+ ENCODE_START(6, 3, bl);
+ encode(hobj.key, bl);
+ encode(hobj.oid, bl);
+ encode(hobj.snap, bl);
+ encode(hobj.hash, bl);
+ encode(hobj.max, bl);
+ encode(hobj.nspace, bl);
+ encode(hobj.pool, bl);
+ encode(generation, bl);
+ encode(shard_id, bl);
+ encode(max, bl);
+ ENCODE_FINISH(bl);
+}
+
+size_t ghobject_t::encoded_size() const
+{
+ // this is not in order of encoding or appearance, but rather
+ // in order of known constants first, so it can be (mostly) computed
+ // at compile time.
+ // - encoding header + 3 string lengths
+ size_t r = sizeof(ceph_le32) + 2 * sizeof(__u8) + 3 * sizeof(__u32);
+
+ // hobj.snap
+ r += sizeof(uint64_t);
+
+ // hobj.hash
+ r += sizeof(uint32_t);
+
+ // hobj.max
+ r += sizeof(bool);
+
+ // hobj.pool
+ r += sizeof(uint64_t);
+
+ // hobj.generation
+ r += sizeof(uint64_t);
+
+ // hobj.shard_id
+ r += sizeof(int8_t);
+
+ // max
+ r += sizeof(bool);
+
+ // hobj.key
+ r += hobj.key.size();
+
+ // hobj.oid
+ r += hobj.oid.name.size();
+
+ // hobj.nspace
+ r += hobj.nspace.size();
+
+ return r;
+}
+
+void ghobject_t::decode(bufferlist::const_iterator& bl)
+{
+ DECODE_START_LEGACY_COMPAT_LEN(6, 3, 3, bl);
+ if (struct_v >= 1)
+ decode(hobj.key, bl);
+ decode(hobj.oid, bl);
+ decode(hobj.snap, bl);
+ decode(hobj.hash, bl);
+ if (struct_v >= 2)
+ decode(hobj.max, bl);
+ else
+ hobj.max = false;
+ if (struct_v >= 4) {
+ decode(hobj.nspace, bl);
+ decode(hobj.pool, bl);
+ // for compat with hammer, which did not handle the transition from
+ // pool -1 -> pool INT64_MIN for MIN properly (see hobject_t::decode()).
+ if (hobj.pool == -1 &&
+ hobj.snap == 0 &&
+ hobj.hash == 0 &&
+ !hobj.max &&
+ hobj.oid.name.empty()) {
+ hobj.pool = INT64_MIN;
+ ceph_assert(hobj.is_min());
+ }
+ }
+ if (struct_v >= 5) {
+ decode(generation, bl);
+ decode(shard_id, bl);
+ } else {
+ generation = ghobject_t::NO_GEN;
+ shard_id = shard_id_t::NO_SHARD;
+ }
+ if (struct_v >= 6) {
+ decode(max, bl);
+ } else {
+ max = false;
+ }
+ DECODE_FINISH(bl);
+ hobj.build_hash_cache();
+}
+
+void ghobject_t::decode(json_spirit::Value& v)
+{
+ hobj.decode(v);
+ using namespace json_spirit;
+ Object& o = v.get_obj();
+ for (Object::size_type i=0; i<o.size(); i++) {
+ Pair& p = o[i];
+ if (p.name_ == "generation")
+ generation = p.value_.get_uint64();
+ else if (p.name_ == "shard_id")
+ shard_id.id = p.value_.get_int();
+ else if (p.name_ == "max")
+ max = p.value_.get_int();
+ }
+}
+
+void ghobject_t::dump(Formatter *f) const
+{
+ hobj.dump(f);
+ if (generation != NO_GEN)
+ f->dump_int("generation", generation);
+ if (shard_id != shard_id_t::NO_SHARD)
+ f->dump_int("shard_id", shard_id);
+ f->dump_int("max", (int)max);
+}
+
+void ghobject_t::generate_test_instances(list<ghobject_t*>& o)
+{
+ o.push_back(new ghobject_t);
+ o.push_back(new ghobject_t);
+ o.back()->hobj.max = true;
+ o.push_back(new ghobject_t(hobject_t(object_t("oname"), string(), 1, 234, -1, "")));
+
+ o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
+ 67, 0, "n1"), 1, shard_id_t(0)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
+ 67, 0, "n1"), 1, shard_id_t(1)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
+ 67, 0, "n1"), 1, shard_id_t(2)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"), 1, shard_id_t(0)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"), 2, shard_id_t(0)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(0)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(1)));
+ o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
+ CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(2)));
+}
+
+ostream& operator<<(ostream& out, const ghobject_t& o)
+{
+ if (o == ghobject_t())
+ return out << "GHMIN";
+ if (o.is_max())
+ return out << "GHMAX";
+ if (o.shard_id != shard_id_t::NO_SHARD)
+ out << std::hex << o.shard_id << std::dec;
+ out << '#' << o.hobj << '#';
+ if (o.generation != ghobject_t::NO_GEN)
+ out << std::hex << (unsigned long long)(o.generation) << std::dec;
+ return out;
+}
+
+bool ghobject_t::parse(const string& s)
+{
+ if (s == "GHMIN") {
+ *this = ghobject_t();
+ return true;
+ }
+ if (s == "GHMAX") {
+ *this = ghobject_t::get_max();
+ return true;
+ }
+
+ // look for shard# prefix
+ const char *start = s.c_str();
+ const char *p;
+ int sh = shard_id_t::NO_SHARD;
+ for (p = start; *p && isxdigit(*p); ++p) ;
+ if (!*p && *p != '#')
+ return false;
+ if (p > start) {
+ int r = sscanf(s.c_str(), "%x", &sh);
+ if (r < 1)
+ return false;
+ start = p + 1;
+ } else {
+ ++start;
+ }
+
+ // look for #generation suffix
+ long long unsigned g = NO_GEN;
+ const char *last = start + strlen(start) - 1;
+ p = last;
+ while (isxdigit(*p))
+ p--;
+ if (*p != '#')
+ return false;
+ if (p < last) {
+ sscanf(p + 1, "%llx", &g);
+ }
+
+ string inner(start, p - start);
+ hobject_t h;
+ if (!h.parse(inner)) {
+ return false;
+ }
+
+ shard_id = shard_id_t(sh);
+ hobj = h;
+ generation = g;
+ max = false;
+ return true;
+}
+
+int cmp(const ghobject_t& l, const ghobject_t& r)
+{
+ if (l.max < r.max)
+ return -1;
+ if (l.max > r.max)
+ return 1;
+ if (l.shard_id < r.shard_id)
+ return -1;
+ if (l.shard_id > r.shard_id)
+ return 1;
+ int ret = cmp(l.hobj, r.hobj);
+ if (ret != 0)
+ return ret;
+ if (l.generation < r.generation)
+ return -1;
+ if (l.generation > r.generation)
+ return 1;
+ return 0;
+}