| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- #include <array>
- #include <map>
- #include <string>
- #include "memtable/stl_wrappers.h"
- #include "rocksdb/db.h"
- #include "rocksdb/env.h"
- #include "test_util/testharness.h"
- #include "test_util/testutil.h"
- #include "util/hash.h"
- #include "util/kv_map.h"
- #include "util/string_util.h"
- #include "utilities/merge_operators.h"
- using std::unique_ptr;
- namespace ROCKSDB_NAMESPACE {
- namespace {
- static const Comparator* kTestComparator = nullptr;
- class KVIter : public Iterator {
- public:
- explicit KVIter(const stl_wrappers::KVMap* map)
- : map_(map), iter_(map_->end()) {}
- bool Valid() const override { return iter_ != map_->end(); }
- void SeekToFirst() override { iter_ = map_->begin(); }
- void SeekToLast() override {
- if (map_->empty()) {
- iter_ = map_->end();
- } else {
- iter_ = map_->find(map_->rbegin()->first);
- }
- }
- void Seek(const Slice& k) override {
- iter_ = map_->lower_bound(k.ToString());
- }
- void SeekForPrev(const Slice& k) override {
- iter_ = map_->upper_bound(k.ToString());
- Prev();
- }
- void Next() override { ++iter_; }
- void Prev() override {
- if (iter_ == map_->begin()) {
- iter_ = map_->end();
- return;
- }
- --iter_;
- }
- Slice key() const override { return iter_->first; }
- Slice value() const override { return iter_->second; }
- Status status() const override { return Status::OK(); }
- private:
- const stl_wrappers::KVMap* const map_;
- stl_wrappers::KVMap::const_iterator iter_;
- };
- void AssertItersEqual(Iterator* iter1, Iterator* iter2) {
- ASSERT_EQ(iter1->Valid(), iter2->Valid());
- if (iter1->Valid()) {
- ASSERT_EQ(iter1->key().ToString(), iter2->key().ToString());
- ASSERT_EQ(iter1->value().ToString(), iter2->value().ToString());
- }
- }
- // Measuring operations on DB (expect to be empty).
- // source_strings are candidate keys
- void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings,
- Random* rnd, int num_writes, int num_iter_ops,
- int num_trigger_flush) {
- stl_wrappers::KVMap map((stl_wrappers::LessOfComparator(kTestComparator)));
- for (int i = 0; i < num_writes; i++) {
- if (num_trigger_flush > 0 && i != 0 && i % num_trigger_flush == 0) {
- db->Flush(FlushOptions());
- }
- int type = rnd->Uniform(2);
- int index = rnd->Uniform(static_cast<int>(source_strings.size()));
- auto& key = source_strings[index];
- switch (type) {
- case 0:
- // put
- map[key] = key;
- ASSERT_OK(db->Put(WriteOptions(), key, key));
- break;
- case 1:
- // delete
- if (map.find(key) != map.end()) {
- map.erase(key);
- }
- ASSERT_OK(db->Delete(WriteOptions(), key));
- break;
- default:
- assert(false);
- }
- }
- std::unique_ptr<Iterator> iter(db->NewIterator(ReadOptions()));
- std::unique_ptr<Iterator> result_iter(new KVIter(&map));
- bool is_valid = false;
- for (int i = 0; i < num_iter_ops; i++) {
- // Random walk and make sure iter and result_iter returns the
- // same key and value
- int type = rnd->Uniform(6);
- ASSERT_OK(iter->status());
- switch (type) {
- case 0:
- // Seek to First
- iter->SeekToFirst();
- result_iter->SeekToFirst();
- break;
- case 1:
- // Seek to last
- iter->SeekToLast();
- result_iter->SeekToLast();
- break;
- case 2: {
- // Seek to random key
- auto key_idx = rnd->Uniform(static_cast<int>(source_strings.size()));
- auto key = source_strings[key_idx];
- iter->Seek(key);
- result_iter->Seek(key);
- break;
- }
- case 3:
- // Next
- if (is_valid) {
- iter->Next();
- result_iter->Next();
- } else {
- continue;
- }
- break;
- case 4:
- // Prev
- if (is_valid) {
- iter->Prev();
- result_iter->Prev();
- } else {
- continue;
- }
- break;
- default: {
- assert(type == 5);
- auto key_idx = rnd->Uniform(static_cast<int>(source_strings.size()));
- auto key = source_strings[key_idx];
- std::string result;
- auto status = db->Get(ReadOptions(), key, &result);
- if (map.find(key) == map.end()) {
- ASSERT_TRUE(status.IsNotFound());
- } else {
- ASSERT_EQ(map[key], result);
- }
- break;
- }
- }
- AssertItersEqual(iter.get(), result_iter.get());
- is_valid = iter->Valid();
- }
- }
- class DoubleComparator : public Comparator {
- public:
- DoubleComparator() {}
- const char* Name() const override { return "DoubleComparator"; }
- int Compare(const Slice& a, const Slice& b) const override {
- #ifndef CYGWIN
- double da = std::stod(a.ToString());
- double db = std::stod(b.ToString());
- #else
- double da = std::strtod(a.ToString().c_str(), 0 /* endptr */);
- double db = std::strtod(a.ToString().c_str(), 0 /* endptr */);
- #endif
- if (da == db) {
- return a.compare(b);
- } else if (da > db) {
- return 1;
- } else {
- return -1;
- }
- }
- void FindShortestSeparator(std::string* /*start*/,
- const Slice& /*limit*/) const override {}
- void FindShortSuccessor(std::string* /*key*/) const override {}
- };
- class HashComparator : public Comparator {
- public:
- HashComparator() {}
- const char* Name() const override { return "HashComparator"; }
- int Compare(const Slice& a, const Slice& b) const override {
- uint32_t ha = Hash(a.data(), a.size(), 66);
- uint32_t hb = Hash(b.data(), b.size(), 66);
- if (ha == hb) {
- return a.compare(b);
- } else if (ha > hb) {
- return 1;
- } else {
- return -1;
- }
- }
- void FindShortestSeparator(std::string* /*start*/,
- const Slice& /*limit*/) const override {}
- void FindShortSuccessor(std::string* /*key*/) const override {}
- };
- class TwoStrComparator : public Comparator {
- public:
- TwoStrComparator() {}
- const char* Name() const override { return "TwoStrComparator"; }
- int Compare(const Slice& a, const Slice& b) const override {
- assert(a.size() >= 2);
- assert(b.size() >= 2);
- size_t size_a1 = static_cast<size_t>(a[0]);
- size_t size_b1 = static_cast<size_t>(b[0]);
- size_t size_a2 = static_cast<size_t>(a[1]);
- size_t size_b2 = static_cast<size_t>(b[1]);
- assert(size_a1 + size_a2 + 2 == a.size());
- assert(size_b1 + size_b2 + 2 == b.size());
- Slice a1 = Slice(a.data() + 2, size_a1);
- Slice b1 = Slice(b.data() + 2, size_b1);
- Slice a2 = Slice(a.data() + 2 + size_a1, size_a2);
- Slice b2 = Slice(b.data() + 2 + size_b1, size_b2);
- if (a1 != b1) {
- return a1.compare(b1);
- }
- return a2.compare(b2);
- }
- void FindShortestSeparator(std::string* /*start*/,
- const Slice& /*limit*/) const override {}
- void FindShortSuccessor(std::string* /*key*/) const override {}
- };
- } // namespace
- class ComparatorDBTest
- : public testing::Test,
- virtual public ::testing::WithParamInterface<uint32_t> {
- private:
- std::string dbname_;
- Env* env_;
- DB* db_;
- Options last_options_;
- std::unique_ptr<const Comparator> comparator_guard;
- public:
- ComparatorDBTest() : env_(Env::Default()), db_(nullptr) {
- kTestComparator = BytewiseComparator();
- dbname_ = test::PerThreadDBPath("comparator_db_test");
- BlockBasedTableOptions toptions;
- toptions.format_version = GetParam();
- last_options_.table_factory.reset(
- ROCKSDB_NAMESPACE::NewBlockBasedTableFactory(toptions));
- EXPECT_OK(DestroyDB(dbname_, last_options_));
- }
- ~ComparatorDBTest() override {
- delete db_;
- EXPECT_OK(DestroyDB(dbname_, last_options_));
- kTestComparator = BytewiseComparator();
- }
- DB* GetDB() { return db_; }
- void SetOwnedComparator(const Comparator* cmp, bool owner = true) {
- if (owner) {
- comparator_guard.reset(cmp);
- } else {
- comparator_guard.reset();
- }
- kTestComparator = cmp;
- last_options_.comparator = cmp;
- }
- // Return the current option configuration.
- Options* GetOptions() { return &last_options_; }
- void DestroyAndReopen() {
- // Destroy using last options
- Destroy();
- ASSERT_OK(TryReopen());
- }
- void Destroy() {
- delete db_;
- db_ = nullptr;
- ASSERT_OK(DestroyDB(dbname_, last_options_));
- }
- Status TryReopen() {
- delete db_;
- db_ = nullptr;
- last_options_.create_if_missing = true;
- return DB::Open(last_options_, dbname_, &db_);
- }
- };
- INSTANTIATE_TEST_CASE_P(FormatDef, ComparatorDBTest,
- testing::Values(test::kDefaultFormatVersion));
- INSTANTIATE_TEST_CASE_P(FormatLatest, ComparatorDBTest,
- testing::Values(test::kLatestFormatVersion));
- TEST_P(ComparatorDBTest, Bytewise) {
- for (int rand_seed = 301; rand_seed < 306; rand_seed++) {
- DestroyAndReopen();
- Random rnd(rand_seed);
- DoRandomIteraratorTest(GetDB(),
- {"a", "b", "c", "d", "e", "f", "g", "h", "i"}, &rnd,
- 8, 100, 3);
- }
- }
- TEST_P(ComparatorDBTest, SimpleSuffixReverseComparator) {
- SetOwnedComparator(new test::SimpleSuffixReverseComparator());
- for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
- Options* opt = GetOptions();
- opt->comparator = kTestComparator;
- DestroyAndReopen();
- Random rnd(rnd_seed);
- std::vector<std::string> source_strings;
- std::vector<std::string> source_prefixes;
- // Randomly generate 5 prefixes
- for (int i = 0; i < 5; i++) {
- source_prefixes.push_back(test::RandomHumanReadableString(&rnd, 8));
- }
- for (int j = 0; j < 20; j++) {
- int prefix_index = rnd.Uniform(static_cast<int>(source_prefixes.size()));
- std::string key = source_prefixes[prefix_index] +
- test::RandomHumanReadableString(&rnd, rnd.Uniform(8));
- source_strings.push_back(key);
- }
- DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 30, 600, 66);
- }
- }
- TEST_P(ComparatorDBTest, Uint64Comparator) {
- SetOwnedComparator(test::Uint64Comparator(), false /* owner */);
- for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
- Options* opt = GetOptions();
- opt->comparator = kTestComparator;
- DestroyAndReopen();
- Random rnd(rnd_seed);
- Random64 rnd64(rnd_seed);
- std::vector<std::string> source_strings;
- // Randomly generate source keys
- for (int i = 0; i < 100; i++) {
- uint64_t r = rnd64.Next();
- std::string str;
- str.resize(8);
- memcpy(&str[0], static_cast<void*>(&r), 8);
- source_strings.push_back(str);
- }
- DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
- }
- }
- TEST_P(ComparatorDBTest, DoubleComparator) {
- SetOwnedComparator(new DoubleComparator());
- for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
- Options* opt = GetOptions();
- opt->comparator = kTestComparator;
- DestroyAndReopen();
- Random rnd(rnd_seed);
- std::vector<std::string> source_strings;
- // Randomly generate source keys
- for (int i = 0; i < 100; i++) {
- uint32_t r = rnd.Next();
- uint32_t divide_order = rnd.Uniform(8);
- double to_divide = 1.0;
- for (uint32_t j = 0; j < divide_order; j++) {
- to_divide *= 10.0;
- }
- source_strings.push_back(ToString(r / to_divide));
- }
- DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
- }
- }
- TEST_P(ComparatorDBTest, HashComparator) {
- SetOwnedComparator(new HashComparator());
- for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
- Options* opt = GetOptions();
- opt->comparator = kTestComparator;
- DestroyAndReopen();
- Random rnd(rnd_seed);
- std::vector<std::string> source_strings;
- // Randomly generate source keys
- for (int i = 0; i < 100; i++) {
- source_strings.push_back(test::RandomKey(&rnd, 8));
- }
- DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
- }
- }
- TEST_P(ComparatorDBTest, TwoStrComparator) {
- SetOwnedComparator(new TwoStrComparator());
- for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
- Options* opt = GetOptions();
- opt->comparator = kTestComparator;
- DestroyAndReopen();
- Random rnd(rnd_seed);
- std::vector<std::string> source_strings;
- // Randomly generate source keys
- for (int i = 0; i < 100; i++) {
- std::string str;
- uint32_t size1 = rnd.Uniform(8);
- uint32_t size2 = rnd.Uniform(8);
- str.append(1, static_cast<char>(size1));
- str.append(1, static_cast<char>(size2));
- str.append(test::RandomKey(&rnd, size1));
- str.append(test::RandomKey(&rnd, size2));
- source_strings.push_back(str);
- }
- DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 200, 1000, 66);
- }
- }
- TEST_P(ComparatorDBTest, IsSameLengthImmediateSuccessor) {
- {
- // different length
- Slice s("abcxy");
- Slice t("abcxyz");
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- Slice s("abcxyz");
- Slice t("abcxy");
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- // not last byte different
- Slice s("abc1xyz");
- Slice t("abc2xyz");
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- // same string
- Slice s("abcxyz");
- Slice t("abcxyz");
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- Slice s("abcxy");
- Slice t("abcxz");
- ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- Slice s("abcxz");
- Slice t("abcxy");
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- const char s_array[] = "\x50\x8a\xac";
- const char t_array[] = "\x50\x8a\xad";
- Slice s(s_array);
- Slice t(t_array);
- ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- const char s_array[] = "\x50\x8a\xff";
- const char t_array[] = "\x50\x8b\x00";
- Slice s(s_array, 3);
- Slice t(t_array, 3);
- ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- const char s_array[] = "\x50\x8a\xff\xff";
- const char t_array[] = "\x50\x8b\x00\x00";
- Slice s(s_array, 4);
- Slice t(t_array, 4);
- ASSERT_TRUE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- {
- const char s_array[] = "\x50\x8a\xff\xff";
- const char t_array[] = "\x50\x8b\x00\x01";
- Slice s(s_array, 4);
- Slice t(t_array, 4);
- ASSERT_FALSE(BytewiseComparator()->IsSameLengthImmediateSuccessor(s, t));
- }
- }
- TEST_P(ComparatorDBTest, FindShortestSeparator) {
- std::string s1 = "abc1xyz";
- std::string s2 = "abc3xy";
- BytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_EQ("abc2", s1);
- s1 = "abc5xyztt";
- ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_EQ("abc5", s1);
- s1 = "abc3";
- s2 = "abc2xy";
- ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_EQ("abc3", s1);
- s1 = "abc3xyz";
- s2 = "abc2xy";
- ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_EQ("abc3", s1);
- s1 = "abc3xyz";
- s2 = "abc2";
- ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_EQ("abc3", s1);
- std::string old_s1 = s1 = "abc2xy";
- s2 = "abc2";
- ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
- ASSERT_TRUE(old_s1 >= s1);
- ASSERT_TRUE(s1 > s2);
- }
- TEST_P(ComparatorDBTest, SeparatorSuccessorRandomizeTest) {
- // Char list for boundary cases.
- std::array<unsigned char, 6> char_list{{0, 1, 2, 253, 254, 255}};
- Random rnd(301);
- for (int attempts = 0; attempts < 1000; attempts++) {
- uint32_t size1 = rnd.Skewed(4);
- uint32_t size2;
- if (rnd.OneIn(2)) {
- // size2 to be random size
- size2 = rnd.Skewed(4);
- } else {
- // size1 is within [-2, +2] of size1
- int diff = static_cast<int>(rnd.Uniform(5)) - 2;
- int tmp_size2 = static_cast<int>(size1) + diff;
- if (tmp_size2 < 0) {
- tmp_size2 = 0;
- }
- size2 = static_cast<uint32_t>(tmp_size2);
- }
- std::string s1;
- std::string s2;
- for (uint32_t i = 0; i < size1; i++) {
- if (rnd.OneIn(2)) {
- // Use random byte
- s1 += static_cast<char>(rnd.Uniform(256));
- } else {
- // Use one byte in char_list
- char c = static_cast<char>(char_list[rnd.Uniform(sizeof(char_list))]);
- s1 += c;
- }
- }
- // First set s2 to be the same as s1, and then modify s2.
- s2 = s1;
- s2.resize(size2);
- // We start from the back of the string
- if (size2 > 0) {
- uint32_t pos = size2 - 1;
- do {
- if (pos >= size1 || rnd.OneIn(4)) {
- // For 1/4 chance, use random byte
- s2[pos] = static_cast<char>(rnd.Uniform(256));
- } else if (rnd.OneIn(4)) {
- // In 1/4 chance, stop here.
- break;
- } else {
- // Create a char within [-2, +2] of the matching char of s1.
- int diff = static_cast<int>(rnd.Uniform(5)) - 2;
- // char may be signed or unsigned based on platform.
- int s1_char = static_cast<int>(static_cast<unsigned char>(s1[pos]));
- int s2_char = s1_char + diff;
- if (s2_char < 0) {
- s2_char = 0;
- }
- if (s2_char > 255) {
- s2_char = 255;
- }
- s2[pos] = static_cast<char>(s2_char);
- }
- } while (pos-- != 0);
- }
- // Test separators
- for (int rev = 0; rev < 2; rev++) {
- if (rev == 1) {
- // switch s1 and s2
- std::string t = s1;
- s1 = s2;
- s2 = t;
- }
- std::string separator = s1;
- BytewiseComparator()->FindShortestSeparator(&separator, s2);
- std::string rev_separator = s1;
- ReverseBytewiseComparator()->FindShortestSeparator(&rev_separator, s2);
- if (s1 == s2) {
- ASSERT_EQ(s1, separator);
- ASSERT_EQ(s2, rev_separator);
- } else if (s1 < s2) {
- ASSERT_TRUE(s1 <= separator);
- ASSERT_TRUE(s2 > separator);
- ASSERT_LE(separator.size(), std::max(s1.size(), s2.size()));
- ASSERT_EQ(s1, rev_separator);
- } else {
- ASSERT_TRUE(s1 >= rev_separator);
- ASSERT_TRUE(s2 < rev_separator);
- ASSERT_LE(rev_separator.size(), std::max(s1.size(), s2.size()));
- ASSERT_EQ(s1, separator);
- }
- }
- // Test successors
- std::string succ = s1;
- BytewiseComparator()->FindShortSuccessor(&succ);
- ASSERT_TRUE(succ >= s1);
- succ = s1;
- ReverseBytewiseComparator()->FindShortSuccessor(&succ);
- ASSERT_TRUE(succ <= s1);
- }
- }
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|