table_test.cc
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 5 #include "leveldb/table.h" 6 7 #include <map> 8 #include <string> 9 10 #include "db/dbformat.h" 11 #include "db/memtable.h" 12 #include "db/write_batch_internal.h" 13 #include "leveldb/db.h" 14 #include "leveldb/env.h" 15 #include "leveldb/iterator.h" 16 #include "leveldb/table_builder.h" 17 #include "table/block.h" 18 #include "table/block_builder.h" 19 #include "table/format.h" 20 #include "util/random.h" 21 #include "util/testharness.h" 22 #include "util/testutil.h" 23 24 namespace leveldb { 25 26 // Return reverse of "key". 27 // Used to test non-lexicographic comparators. 28 static std::string Reverse(const Slice& key) { 29 std::string str(key.ToString()); 30 std::string rev(""); 31 for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); 32 ++rit) { 33 rev.push_back(*rit); 34 } 35 return rev; 36 } 37 38 namespace { 39 class ReverseKeyComparator : public Comparator { 40 public: 41 const char* Name() const override { 42 return "leveldb.ReverseBytewiseComparator"; 43 } 44 45 int Compare(const Slice& a, const Slice& b) const override { 46 return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); 47 } 48 49 void FindShortestSeparator(std::string* start, 50 const Slice& limit) const override { 51 std::string s = Reverse(*start); 52 std::string l = Reverse(limit); 53 BytewiseComparator()->FindShortestSeparator(&s, l); 54 *start = Reverse(s); 55 } 56 57 void FindShortSuccessor(std::string* key) const override { 58 std::string s = Reverse(*key); 59 BytewiseComparator()->FindShortSuccessor(&s); 60 *key = Reverse(s); 61 } 62 }; 63 } // namespace 64 static ReverseKeyComparator reverse_key_comparator; 65 66 static void Increment(const Comparator* cmp, std::string* key) { 67 if (cmp == BytewiseComparator()) { 68 key->push_back('\0'); 69 } else { 70 assert(cmp == &reverse_key_comparator); 71 std::string rev = Reverse(*key); 72 rev.push_back('\0'); 73 *key = Reverse(rev); 74 } 75 } 76 77 // An STL comparator that uses a Comparator 78 namespace { 79 struct STLLessThan { 80 const Comparator* cmp; 81 82 STLLessThan() : cmp(BytewiseComparator()) {} 83 STLLessThan(const Comparator* c) : cmp(c) {} 84 bool operator()(const std::string& a, const std::string& b) const { 85 return cmp->Compare(Slice(a), Slice(b)) < 0; 86 } 87 }; 88 } // namespace 89 90 class StringSink : public WritableFile { 91 public: 92 ~StringSink() override = default; 93 94 const std::string& contents() const { return contents_; } 95 96 Status Close() override { return Status::OK(); } 97 Status Flush() override { return Status::OK(); } 98 Status Sync() override { return Status::OK(); } 99 100 Status Append(const Slice& data) override { 101 contents_.append(data.data(), data.size()); 102 return Status::OK(); 103 } 104 105 std::string GetName() const override { return ""; } 106 private: 107 std::string contents_; 108 }; 109 110 class StringSource : public RandomAccessFile { 111 public: 112 StringSource(const Slice& contents) 113 : contents_(contents.data(), contents.size()) {} 114 115 ~StringSource() override = default; 116 117 uint64_t Size() const { return contents_.size(); } 118 119 Status Read(uint64_t offset, size_t n, Slice* result, 120 char* scratch) const override { 121 if (offset >= contents_.size()) { 122 return Status::InvalidArgument("invalid Read offset"); 123 } 124 if (offset + n > contents_.size()) { 125 n = contents_.size() - offset; 126 } 127 memcpy(scratch, &contents_[offset], n); 128 *result = Slice(scratch, n); 129 return Status::OK(); 130 } 131 132 std::string GetName() const override { return ""; } 133 private: 134 std::string contents_; 135 }; 136 137 typedef std::map<std::string, std::string, STLLessThan> KVMap; 138 139 // Helper class for tests to unify the interface between 140 // BlockBuilder/TableBuilder and Block/Table. 141 class Constructor { 142 public: 143 explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) {} 144 virtual ~Constructor() = default; 145 146 void Add(const std::string& key, const Slice& value) { 147 data_[key] = value.ToString(); 148 } 149 150 // Finish constructing the data structure with all the keys that have 151 // been added so far. Returns the keys in sorted order in "*keys" 152 // and stores the key/value pairs in "*kvmap" 153 void Finish(const Options& options, std::vector<std::string>* keys, 154 KVMap* kvmap) { 155 *kvmap = data_; 156 keys->clear(); 157 for (const auto& kvp : data_) { 158 keys->push_back(kvp.first); 159 } 160 data_.clear(); 161 Status s = FinishImpl(options, *kvmap); 162 ASSERT_TRUE(s.ok()) << s.ToString(); 163 } 164 165 // Construct the data structure from the data in "data" 166 virtual Status FinishImpl(const Options& options, const KVMap& data) = 0; 167 168 virtual Iterator* NewIterator() const = 0; 169 170 const KVMap& data() const { return data_; } 171 172 virtual DB* db() const { return nullptr; } // Overridden in DBConstructor 173 174 private: 175 KVMap data_; 176 }; 177 178 class BlockConstructor : public Constructor { 179 public: 180 explicit BlockConstructor(const Comparator* cmp) 181 : Constructor(cmp), comparator_(cmp), block_(nullptr) {} 182 ~BlockConstructor() override { delete block_; } 183 Status FinishImpl(const Options& options, const KVMap& data) override { 184 delete block_; 185 block_ = nullptr; 186 BlockBuilder builder(&options); 187 188 for (const auto& kvp : data) { 189 builder.Add(kvp.first, kvp.second); 190 } 191 // Open the block 192 data_ = builder.Finish().ToString(); 193 BlockContents contents; 194 contents.data = data_; 195 contents.cachable = false; 196 contents.heap_allocated = false; 197 block_ = new Block(contents); 198 return Status::OK(); 199 } 200 Iterator* NewIterator() const override { 201 return block_->NewIterator(comparator_); 202 } 203 204 private: 205 const Comparator* const comparator_; 206 std::string data_; 207 Block* block_; 208 209 BlockConstructor(); 210 }; 211 212 class TableConstructor : public Constructor { 213 public: 214 TableConstructor(const Comparator* cmp) 215 : Constructor(cmp), source_(nullptr), table_(nullptr) {} 216 ~TableConstructor() override { Reset(); } 217 Status FinishImpl(const Options& options, const KVMap& data) override { 218 Reset(); 219 StringSink sink; 220 TableBuilder builder(options, &sink); 221 222 for (const auto& kvp : data) { 223 builder.Add(kvp.first, kvp.second); 224 ASSERT_TRUE(builder.status().ok()); 225 } 226 Status s = builder.Finish(); 227 ASSERT_TRUE(s.ok()) << s.ToString(); 228 229 ASSERT_EQ(sink.contents().size(), builder.FileSize()); 230 231 // Open the table 232 source_ = new StringSource(sink.contents()); 233 Options table_options; 234 table_options.comparator = options.comparator; 235 return Table::Open(table_options, source_, sink.contents().size(), &table_); 236 } 237 238 Iterator* NewIterator() const override { 239 return table_->NewIterator(ReadOptions()); 240 } 241 242 uint64_t ApproximateOffsetOf(const Slice& key) const { 243 return table_->ApproximateOffsetOf(key); 244 } 245 246 private: 247 void Reset() { 248 delete table_; 249 delete source_; 250 table_ = nullptr; 251 source_ = nullptr; 252 } 253 254 StringSource* source_; 255 Table* table_; 256 257 TableConstructor(); 258 }; 259 260 // A helper class that converts internal format keys into user keys 261 class KeyConvertingIterator : public Iterator { 262 public: 263 explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) {} 264 265 KeyConvertingIterator(const KeyConvertingIterator&) = delete; 266 KeyConvertingIterator& operator=(const KeyConvertingIterator&) = delete; 267 268 ~KeyConvertingIterator() override { delete iter_; } 269 270 bool Valid() const override { return iter_->Valid(); } 271 void Seek(const Slice& target) override { 272 ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); 273 std::string encoded; 274 AppendInternalKey(&encoded, ikey); 275 iter_->Seek(encoded); 276 } 277 void SeekToFirst() override { iter_->SeekToFirst(); } 278 void SeekToLast() override { iter_->SeekToLast(); } 279 void Next() override { iter_->Next(); } 280 void Prev() override { iter_->Prev(); } 281 282 Slice key() const override { 283 assert(Valid()); 284 ParsedInternalKey key; 285 if (!ParseInternalKey(iter_->key(), &key)) { 286 status_ = Status::Corruption("malformed internal key"); 287 return Slice("corrupted key"); 288 } 289 return key.user_key; 290 } 291 292 Slice value() const override { return iter_->value(); } 293 Status status() const override { 294 return status_.ok() ? iter_->status() : status_; 295 } 296 297 private: 298 mutable Status status_; 299 Iterator* iter_; 300 }; 301 302 class MemTableConstructor : public Constructor { 303 public: 304 explicit MemTableConstructor(const Comparator* cmp) 305 : Constructor(cmp), internal_comparator_(cmp) { 306 memtable_ = new MemTable(internal_comparator_); 307 memtable_->Ref(); 308 } 309 ~MemTableConstructor() override { memtable_->Unref(); } 310 Status FinishImpl(const Options& options, const KVMap& data) override { 311 memtable_->Unref(); 312 memtable_ = new MemTable(internal_comparator_); 313 memtable_->Ref(); 314 int seq = 1; 315 for (const auto& kvp : data) { 316 memtable_->Add(seq, kTypeValue, kvp.first, kvp.second); 317 seq++; 318 } 319 return Status::OK(); 320 } 321 Iterator* NewIterator() const override { 322 return new KeyConvertingIterator(memtable_->NewIterator()); 323 } 324 325 private: 326 const InternalKeyComparator internal_comparator_; 327 MemTable* memtable_; 328 }; 329 330 class DBConstructor : public Constructor { 331 public: 332 explicit DBConstructor(const Comparator* cmp) 333 : Constructor(cmp), comparator_(cmp) { 334 db_ = nullptr; 335 NewDB(); 336 } 337 ~DBConstructor() override { delete db_; } 338 Status FinishImpl(const Options& options, const KVMap& data) override { 339 delete db_; 340 db_ = nullptr; 341 NewDB(); 342 for (const auto& kvp : data) { 343 WriteBatch batch; 344 batch.Put(kvp.first, kvp.second); 345 ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); 346 } 347 return Status::OK(); 348 } 349 Iterator* NewIterator() const override { 350 return db_->NewIterator(ReadOptions()); 351 } 352 353 DB* db() const override { return db_; } 354 355 private: 356 void NewDB() { 357 std::string name = test::TmpDir() + "/table_testdb"; 358 359 Options options; 360 options.comparator = comparator_; 361 Status status = DestroyDB(name, options); 362 ASSERT_TRUE(status.ok()) << status.ToString(); 363 364 options.create_if_missing = true; 365 options.error_if_exists = true; 366 options.write_buffer_size = 10000; // Something small to force merging 367 status = DB::Open(options, name, &db_); 368 ASSERT_TRUE(status.ok()) << status.ToString(); 369 } 370 371 const Comparator* const comparator_; 372 DB* db_; 373 }; 374 375 enum TestType { TABLE_TEST, BLOCK_TEST, MEMTABLE_TEST, DB_TEST }; 376 377 struct TestArgs { 378 TestType type; 379 bool reverse_compare; 380 int restart_interval; 381 }; 382 383 static const TestArgs kTestArgList[] = { 384 {TABLE_TEST, false, 16}, 385 {TABLE_TEST, false, 1}, 386 {TABLE_TEST, false, 1024}, 387 {TABLE_TEST, true, 16}, 388 {TABLE_TEST, true, 1}, 389 {TABLE_TEST, true, 1024}, 390 391 {BLOCK_TEST, false, 16}, 392 {BLOCK_TEST, false, 1}, 393 {BLOCK_TEST, false, 1024}, 394 {BLOCK_TEST, true, 16}, 395 {BLOCK_TEST, true, 1}, 396 {BLOCK_TEST, true, 1024}, 397 398 // Restart interval does not matter for memtables 399 {MEMTABLE_TEST, false, 16}, 400 {MEMTABLE_TEST, true, 16}, 401 402 // Do not bother with restart interval variations for DB 403 {DB_TEST, false, 16}, 404 {DB_TEST, true, 16}, 405 }; 406 static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); 407 408 class Harness { 409 public: 410 Harness() : constructor_(nullptr) {} 411 412 void Init(const TestArgs& args) { 413 delete constructor_; 414 constructor_ = nullptr; 415 options_ = Options(); 416 417 options_.block_restart_interval = args.restart_interval; 418 // Use shorter block size for tests to exercise block boundary 419 // conditions more. 420 options_.block_size = 256; 421 if (args.reverse_compare) { 422 options_.comparator = &reverse_key_comparator; 423 } 424 switch (args.type) { 425 case TABLE_TEST: 426 constructor_ = new TableConstructor(options_.comparator); 427 break; 428 case BLOCK_TEST: 429 constructor_ = new BlockConstructor(options_.comparator); 430 break; 431 case MEMTABLE_TEST: 432 constructor_ = new MemTableConstructor(options_.comparator); 433 break; 434 case DB_TEST: 435 constructor_ = new DBConstructor(options_.comparator); 436 break; 437 } 438 } 439 440 ~Harness() { delete constructor_; } 441 442 void Add(const std::string& key, const std::string& value) { 443 constructor_->Add(key, value); 444 } 445 446 void Test(Random* rnd) { 447 std::vector<std::string> keys; 448 KVMap data; 449 constructor_->Finish(options_, &keys, &data); 450 451 TestForwardScan(keys, data); 452 TestBackwardScan(keys, data); 453 TestRandomAccess(rnd, keys, data); 454 } 455 456 void TestForwardScan(const std::vector<std::string>& keys, 457 const KVMap& data) { 458 Iterator* iter = constructor_->NewIterator(); 459 ASSERT_TRUE(!iter->Valid()); 460 iter->SeekToFirst(); 461 for (KVMap::const_iterator model_iter = data.begin(); 462 model_iter != data.end(); ++model_iter) { 463 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 464 iter->Next(); 465 } 466 ASSERT_TRUE(!iter->Valid()); 467 delete iter; 468 } 469 470 void TestBackwardScan(const std::vector<std::string>& keys, 471 const KVMap& data) { 472 Iterator* iter = constructor_->NewIterator(); 473 ASSERT_TRUE(!iter->Valid()); 474 iter->SeekToLast(); 475 for (KVMap::const_reverse_iterator model_iter = data.rbegin(); 476 model_iter != data.rend(); ++model_iter) { 477 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 478 iter->Prev(); 479 } 480 ASSERT_TRUE(!iter->Valid()); 481 delete iter; 482 } 483 484 void TestRandomAccess(Random* rnd, const std::vector<std::string>& keys, 485 const KVMap& data) { 486 static const bool kVerbose = false; 487 Iterator* iter = constructor_->NewIterator(); 488 ASSERT_TRUE(!iter->Valid()); 489 KVMap::const_iterator model_iter = data.begin(); 490 if (kVerbose) fprintf(stderr, "---\n"); 491 for (int i = 0; i < 200; i++) { 492 const int toss = rnd->Uniform(5); 493 switch (toss) { 494 case 0: { 495 if (iter->Valid()) { 496 if (kVerbose) fprintf(stderr, "Next\n"); 497 iter->Next(); 498 ++model_iter; 499 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 500 } 501 break; 502 } 503 504 case 1: { 505 if (kVerbose) fprintf(stderr, "SeekToFirst\n"); 506 iter->SeekToFirst(); 507 model_iter = data.begin(); 508 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 509 break; 510 } 511 512 case 2: { 513 std::string key = PickRandomKey(rnd, keys); 514 model_iter = data.lower_bound(key); 515 if (kVerbose) 516 fprintf(stderr, "Seek '%s'\n", EscapeString(key).c_str()); 517 iter->Seek(Slice(key)); 518 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 519 break; 520 } 521 522 case 3: { 523 if (iter->Valid()) { 524 if (kVerbose) fprintf(stderr, "Prev\n"); 525 iter->Prev(); 526 if (model_iter == data.begin()) { 527 model_iter = data.end(); // Wrap around to invalid value 528 } else { 529 --model_iter; 530 } 531 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 532 } 533 break; 534 } 535 536 case 4: { 537 if (kVerbose) fprintf(stderr, "SeekToLast\n"); 538 iter->SeekToLast(); 539 if (keys.empty()) { 540 model_iter = data.end(); 541 } else { 542 std::string last = data.rbegin()->first; 543 model_iter = data.lower_bound(last); 544 } 545 ASSERT_EQ(ToString(data, model_iter), ToString(iter)); 546 break; 547 } 548 } 549 } 550 delete iter; 551 } 552 553 std::string ToString(const KVMap& data, const KVMap::const_iterator& it) { 554 if (it == data.end()) { 555 return "END"; 556 } else { 557 return "'" + it->first + "->" + it->second + "'"; 558 } 559 } 560 561 std::string ToString(const KVMap& data, 562 const KVMap::const_reverse_iterator& it) { 563 if (it == data.rend()) { 564 return "END"; 565 } else { 566 return "'" + it->first + "->" + it->second + "'"; 567 } 568 } 569 570 std::string ToString(const Iterator* it) { 571 if (!it->Valid()) { 572 return "END"; 573 } else { 574 return "'" + it->key().ToString() + "->" + it->value().ToString() + "'"; 575 } 576 } 577 578 std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) { 579 if (keys.empty()) { 580 return "foo"; 581 } else { 582 const int index = rnd->Uniform(keys.size()); 583 std::string result = keys[index]; 584 switch (rnd->Uniform(3)) { 585 case 0: 586 // Return an existing key 587 break; 588 case 1: { 589 // Attempt to return something smaller than an existing key 590 if (!result.empty() && result[result.size() - 1] > '\0') { 591 result[result.size() - 1]--; 592 } 593 break; 594 } 595 case 2: { 596 // Return something larger than an existing key 597 Increment(options_.comparator, &result); 598 break; 599 } 600 } 601 return result; 602 } 603 } 604 605 // Returns nullptr if not running against a DB 606 DB* db() const { return constructor_->db(); } 607 608 private: 609 Options options_; 610 Constructor* constructor_; 611 }; 612 613 // Test empty table/block. 614 TEST(Harness, Empty) { 615 for (int i = 0; i < kNumTestArgs; i++) { 616 Init(kTestArgList[i]); 617 Random rnd(test::RandomSeed() + 1); 618 Test(&rnd); 619 } 620 } 621 622 // Special test for a block with no restart entries. The C++ leveldb 623 // code never generates such blocks, but the Java version of leveldb 624 // seems to. 625 TEST(Harness, ZeroRestartPointsInBlock) { 626 char data[sizeof(uint32_t)]; 627 memset(data, 0, sizeof(data)); 628 BlockContents contents; 629 contents.data = Slice(data, sizeof(data)); 630 contents.cachable = false; 631 contents.heap_allocated = false; 632 Block block(contents); 633 Iterator* iter = block.NewIterator(BytewiseComparator()); 634 iter->SeekToFirst(); 635 ASSERT_TRUE(!iter->Valid()); 636 iter->SeekToLast(); 637 ASSERT_TRUE(!iter->Valid()); 638 iter->Seek("foo"); 639 ASSERT_TRUE(!iter->Valid()); 640 delete iter; 641 } 642 643 // Test the empty key 644 TEST(Harness, SimpleEmptyKey) { 645 for (int i = 0; i < kNumTestArgs; i++) { 646 Init(kTestArgList[i]); 647 Random rnd(test::RandomSeed() + 1); 648 Add("", "v"); 649 Test(&rnd); 650 } 651 } 652 653 TEST(Harness, SimpleSingle) { 654 for (int i = 0; i < kNumTestArgs; i++) { 655 Init(kTestArgList[i]); 656 Random rnd(test::RandomSeed() + 2); 657 Add("abc", "v"); 658 Test(&rnd); 659 } 660 } 661 662 TEST(Harness, SimpleMulti) { 663 for (int i = 0; i < kNumTestArgs; i++) { 664 Init(kTestArgList[i]); 665 Random rnd(test::RandomSeed() + 3); 666 Add("abc", "v"); 667 Add("abcd", "v"); 668 Add("ac", "v2"); 669 Test(&rnd); 670 } 671 } 672 673 TEST(Harness, SimpleSpecialKey) { 674 for (int i = 0; i < kNumTestArgs; i++) { 675 Init(kTestArgList[i]); 676 Random rnd(test::RandomSeed() + 4); 677 Add("\xff\xff", "v3"); 678 Test(&rnd); 679 } 680 } 681 682 TEST(Harness, Randomized) { 683 for (int i = 0; i < kNumTestArgs; i++) { 684 Init(kTestArgList[i]); 685 Random rnd(test::RandomSeed() + 5); 686 for (int num_entries = 0; num_entries < 2000; 687 num_entries += (num_entries < 50 ? 1 : 200)) { 688 if ((num_entries % 10) == 0) { 689 fprintf(stderr, "case %d of %d: num_entries = %d\n", (i + 1), 690 int(kNumTestArgs), num_entries); 691 } 692 for (int e = 0; e < num_entries; e++) { 693 std::string v; 694 Add(test::RandomKey(&rnd, rnd.Skewed(4)), 695 test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); 696 } 697 Test(&rnd); 698 } 699 } 700 } 701 702 TEST(Harness, RandomizedLongDB) { 703 Random rnd(test::RandomSeed()); 704 TestArgs args = {DB_TEST, false, 16}; 705 Init(args); 706 int num_entries = 100000; 707 for (int e = 0; e < num_entries; e++) { 708 std::string v; 709 Add(test::RandomKey(&rnd, rnd.Skewed(4)), 710 test::RandomString(&rnd, rnd.Skewed(5), &v).ToString()); 711 } 712 Test(&rnd); 713 714 // We must have created enough data to force merging 715 int files = 0; 716 for (int level = 0; level < config::kNumLevels; level++) { 717 std::string value; 718 char name[100]; 719 snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level); 720 ASSERT_TRUE(db()->GetProperty(name, &value)); 721 files += atoi(value.c_str()); 722 } 723 ASSERT_GT(files, 0); 724 } 725 726 class MemTableTest {}; 727 728 TEST(MemTableTest, Simple) { 729 InternalKeyComparator cmp(BytewiseComparator()); 730 MemTable* memtable = new MemTable(cmp); 731 memtable->Ref(); 732 WriteBatch batch; 733 WriteBatchInternal::SetSequence(&batch, 100); 734 batch.Put(std::string("k1"), std::string("v1")); 735 batch.Put(std::string("k2"), std::string("v2")); 736 batch.Put(std::string("k3"), std::string("v3")); 737 batch.Put(std::string("largekey"), std::string("vlarge")); 738 ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok()); 739 740 Iterator* iter = memtable->NewIterator(); 741 iter->SeekToFirst(); 742 while (iter->Valid()) { 743 fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(), 744 iter->value().ToString().c_str()); 745 iter->Next(); 746 } 747 748 delete iter; 749 memtable->Unref(); 750 } 751 752 static bool Between(uint64_t val, uint64_t low, uint64_t high) { 753 bool result = (val >= low) && (val <= high); 754 if (!result) { 755 fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", 756 (unsigned long long)(val), (unsigned long long)(low), 757 (unsigned long long)(high)); 758 } 759 return result; 760 } 761 762 class TableTest {}; 763 764 TEST(TableTest, ApproximateOffsetOfPlain) { 765 TableConstructor c(BytewiseComparator()); 766 c.Add("k01", "hello"); 767 c.Add("k02", "hello2"); 768 c.Add("k03", std::string(10000, 'x')); 769 c.Add("k04", std::string(200000, 'x')); 770 c.Add("k05", std::string(300000, 'x')); 771 c.Add("k06", "hello3"); 772 c.Add("k07", std::string(100000, 'x')); 773 std::vector<std::string> keys; 774 KVMap kvmap; 775 Options options; 776 options.block_size = 1024; 777 options.compression = kNoCompression; 778 c.Finish(options, &keys, &kvmap); 779 780 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); 781 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); 782 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); 783 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); 784 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); 785 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); 786 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); 787 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); 788 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); 789 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); 790 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); 791 } 792 793 static bool SnappyCompressionSupported() { 794 std::string out; 795 Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 796 return port::Snappy_Compress(in.data(), in.size(), &out); 797 } 798 799 TEST(TableTest, ApproximateOffsetOfCompressed) { 800 if (!SnappyCompressionSupported()) { 801 fprintf(stderr, "skipping compression tests\n"); 802 return; 803 } 804 805 Random rnd(301); 806 TableConstructor c(BytewiseComparator()); 807 std::string tmp; 808 c.Add("k01", "hello"); 809 c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); 810 c.Add("k03", "hello3"); 811 c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp)); 812 std::vector<std::string> keys; 813 KVMap kvmap; 814 Options options; 815 options.block_size = 1024; 816 options.compression = kSnappyCompression; 817 c.Finish(options, &keys, &kvmap); 818 819 // Expected upper and lower bounds of space used by compressible strings. 820 static const int kSlop = 1000; // Compressor effectiveness varies. 821 const int expected = 2500; // 10000 * compression ratio (0.25) 822 const int min_z = expected - kSlop; 823 const int max_z = expected + kSlop; 824 825 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop)); 826 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop)); 827 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop)); 828 // Have now emitted a large compressible string, so adjust expected offset. 829 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z)); 830 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z)); 831 // Have now emitted two large compressible strings, so adjust expected offset. 832 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z)); 833 } 834 835 } // namespace leveldb 836 837 int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }