Files
scylladb/test/boost/bptree_validation.hh

311 lines
9.5 KiB
C++

/*
* Copyright (C) 2020-present ScyllaDB
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#pragma once
#include <stddef.h>
#include <iostream>
#include <fmt/format.h>
#include "utils/bptree.hh"
namespace bplus {
template <typename K, typename T, typename Less, size_t NodeSize>
class validator {
using tree = class tree<K, T, Less, NodeSize, key_search::both, with_debug::yes>;
using node = typename tree::node;
void validate_node(const tree& t, const node& n, int& prev, int& min, bool is_root);
void validate_list(const tree& t);
public:
void print_tree(const tree& t, char pfx) const {
fmt::print("/ {} <- | {} | -> {}\n", t._left->id(), t._root->id(), t._right->id());
print_node(*t._root, pfx, 2);
fmt::print("\\\n");
}
void print_node(const node& n, char pfx, int indent) const {
int i;
fmt::print("{:<{}c}{:s} {:d} ({:d} keys, {:x} flags):", pfx, indent,
n.is_leaf() ? "leaf" : "node", n.id(), n._num_keys, n._flags);
if (n.is_leaf()) {
for (i = 0; i < n._num_keys; i++) {
fmt::print(" {}", (int)n._keys[i].v);
}
fmt::print("\n");
return;
}
fmt::print("\n");
if (n._kids[0].n != nullptr) {
print_node(*n._kids[0].n, pfx, indent + 2);
}
for (i = 0; i < n._num_keys; i++) {
fmt::print("{:<{}c}---{}---\n", pfx, indent, (int)n._keys[i].v);
print_node(*n._kids[i + 1].n, pfx, indent + 2);
}
}
void validate(const tree& t);
};
template <typename K, typename T, typename L, size_t NS>
void validator<K, T, L, NS>::validate_node(const tree& t, const node& n, int& prev_key, int& min_key, bool is_root) {
int i;
if (n.is_root() != is_root) {
fmt::print("node {} needs to {} root, but {}\n", n.id(), is_root ? "be" : "be not", n._flags);
throw "root broken";
}
for (i = 0; i < n._num_keys; i++) {
if (!n._keys[i].v.is_alive()) {
fmt::print("node {} key {} is not alive\n", n.id(), i);
throw "key dead";
}
}
if (n.is_leaf()) {
for (i = 0; i < n._num_keys; i++) {
if (t._less(n._keys[i].v, K(prev_key))) {
fmt::print("node misordered @{} (prev {})\n", (int)n._keys[i].v, prev_key);
throw "misorder";
}
if (n._kids[i + 1].d->_leaf != &n) {
fmt::print("data mispoint\n");
throw "data backlink";
}
prev_key = n._keys[i].v;
if (!n._kids[i + 1].d->value.match_key(n._keys[i].v)) {
fmt::print("node value corrupted @{:d}.{:d}\n", n.id(), i);
throw "data corruption";
}
}
if (n._num_keys > 0) {
min_key = (int)n._keys[0].v;
}
} else if (n._num_keys > 0) {
node* k = n._kids[0].n;
if (k->_parent != &n) {
fmt::print("node {:d} -parent-> {:d}, expect {:d}\n", k->id(), k->_parent->id(), n.id());
throw "mis-parented node";
}
validate_node(t, *k, prev_key, min_key, false);
for (i = 0; i < n._num_keys; i++) {
k = n._kids[i + 1].n;
if (k->_parent != &n) {
fmt::print("node {:d} -parent-> {:d}, expect {:d}\n",
k->id(), k->_parent ? k->_parent->id() : -1, n.id());
throw "mis-parented node";
}
if (t._less(k->_keys[0].v, n._keys[i].v)) {
fmt::print("node {:d}.{:d}, separation key {}, kid has {}\n", n.id(), k->id(),
(int)n._keys[i].v, (int)k->_keys[0].v);
throw "separation key mismatch";
}
int min = 0;
validate_node(t, *k, prev_key, min, false);
if (t._less(n._keys[i].v, K(min)) || t._less(K(min), n._keys[i].v)) {
fmt::print("node {:d}.[{:d}]{:d}, separation key {}, min {}\n",
n.id(), i, k->id(), (int)n._keys[i].v, min);
if (strict_separation_key || t._less(K(min), n._keys[i].v)) {
throw "separation key screw";
}
}
}
}
}
template <typename K, typename T, typename L, size_t NS>
void validator<K, T, L, NS>::validate_list(const tree& t) {
int prev = 0;
node* lh = t.left_leaf_slow();
node* rh = t.right_leaf_slow();
if (lh != t._left) {
fmt::print("left {:d}, slow {:d}\n", t._left->id(), lh->id());
throw "list broken";
}
if (!(lh->_flags & node::NODE_LEFTMOST)) {
fmt::print("left {:d} is not marked as such {}\n", t._left->id(), t._left->_flags);;
throw "list broken";
}
if (rh != t._right) {
fmt::print("right {:d}, slow {:d}\n", t._right->id(), rh->id());
throw "list broken";
}
if (!(rh->_flags & node::NODE_RIGHTMOST)) {
fmt::print("right {:d} is not marked as such {}\n", t._right->id(), t._right->_flags);;
throw "list broken";
}
node* r = lh;
while (1) {
node *ln;
if (!r->is_rightmost()) {
ln = r->get_next();
if (ln->get_prev() != r) {
fmt::print("next leaf {:d} points to {:d}, expect {:d}\n", ln->id(), ln->get_prev()->id(), r->id());
throw "list broken";
}
} else if (r->_rightmost_tree != &t) {
fmt::print("right leaf doesn't point to tree\n");
throw "list broken";
}
if (!r->is_leftmost()) {
ln = r->get_prev();
if (ln->get_next() != r) {
fmt::print("prev leaf {:d} points to {:d}, expect {:d}\n", ln->id(), ln->get_next()->id(), r->id());
throw "list broken";
}
} else if (r->_kids[0]._leftmost_tree != &t) {
fmt::print("left leaf doesn't point to tree\n");
throw "list broken";
}
if (r->_num_keys > 0 && t._less(r->_keys[0].v, K(prev))) {
fmt::print("list misorder on element {:d}, keys {}..., prev {:d}\n", r->id(), (int)r->_keys[0].v, prev);
throw "list broken";
}
if (!r->is_root() && r->_parent != nullptr) {
const auto p = r->_parent;
int i = p->index_for(r->_keys[0].v, t._less);
if (i > 0) {
if (p->_kids[i - 1].n != r->get_prev()) {
fmt::print("list misorder on parent check: node {:d}.{:d}, parent prev {:d}, list prev {:d}\n",
p->id(), r->id(), p->_kids[i - 1].n->id(), r->get_prev()->id());
throw "list broken";
}
}
if (i < p->_num_keys - 1) {
if (p->_kids[i + 1].n != r->get_next()) {
fmt::print("list misorder on parent check: node {:d}.{:d}, parent next {:d}, list next {:d}\n",
p->id(), r->id(), p->_kids[i + 1].n->id(), r->get_next()->id());
throw "list broken";
}
}
}
if (r->_num_keys > 0) {
prev = (int)r->_keys[r->_num_keys - 1].v;
}
if (r != t._left && r != t._right && (r->_flags & (node::NODE_LEFTMOST | node::NODE_RIGHTMOST))) {
fmt::print("middle {:d} is marked as left/right {}\n", r->id(), r->_flags);;
throw "list broken";
}
if (r->is_rightmost()) {
break;
}
r = r->get_next();
}
}
template <typename K, typename T, typename L, size_t NS>
void validator<K, T, L, NS>::validate(const tree& t) {
try {
validate_list(t);
int min = 0, prev = 0;
if (t._root->_root_tree != &t) {
fmt::print("root doesn't point to tree\n");
throw "root broken";
}
validate_node(t, *t._root, prev, min, true);
} catch (...) {
print_tree(t, '|');
fmt::print("[ ");
node* lh = t._left;
while (1) {
fmt::print(" {:d}", lh->id());
if (lh->is_rightmost()) {
break;
}
lh = lh->get_next();
}
fmt::print("]\n");
throw;
}
}
template <typename K, typename T, typename Less, size_t NodeSize>
class iterator_checker {
using tree = class tree<K, T, Less, NodeSize, key_search::both, with_debug::yes>;
validator<K, T, Less, NodeSize>& _tv;
tree& _t;
typename tree::iterator _fwd, _fend;
T _fprev;
public:
iterator_checker(validator<K, T, Less, NodeSize>& tv, tree& t) : _tv(tv), _t(t),
_fwd(t.begin()), _fend(t.end()) {
}
bool step() {
try {
return forward_check();
} catch(...) {
_tv.print_tree(_t, ':');
throw;
}
}
bool here(const K& k) {
return _fwd != _fend && _fwd->match_key(k);
}
private:
bool forward_check() {
if (_fwd == _fend) {
return false;
}
_fwd++;
if (_fwd == _fend) {
return false;
}
T val = *_fwd;
_fwd++;
if (_fwd == _fend) {
return false;
}
_fwd--;
if (val != *_fwd) {
fmt::print(std::cout, "Iterator broken, {} != {}\n", val, *_fwd);
throw "iterator";
}
if (val < _fprev) {
fmt::print(std::cout, "Iterator broken, {} < {}\n", val, _fprev);
throw "iterator";
}
_fprev = val;
return true;
}
};
} // namespace