From e11bdf1935bc5a46db790ef414110149009f8c6a Mon Sep 17 00:00:00 2001 From: "jorlow@chromium.org" Date: Fri, 25 Mar 2011 20:27:43 +0000 Subject: [PATCH] Upstream changes git-svn-id: https://leveldb.googlecode.com/svn/trunk@15 62dab493-f737-651d-591e-8d6aee1b9529 --- TODO | 1 - db/db_bench.cc | 55 +++++- db/db_iter.cc | 440 +++++++++++++++++++++----------------------- db/db_test.cc | 184 ++++++++++++++++++ doc/index.html | 3 +- port/sha1_test.cc | 16 -- util/crc32c_test.cc | 14 -- 7 files changed, 447 insertions(+), 266 deletions(-) diff --git a/TODO b/TODO index 7d60b5a84..e17dfdb78 100644 --- a/TODO +++ b/TODO @@ -8,7 +8,6 @@ Maybe afterwards ss - Stats -- Speed up backwards scan (avoid three passes over data) db - Maybe implement DB::BulkDeleteForRange(start_key, end_key) diff --git a/db/db_bench.cc b/db/db_bench.cc index 72e069975..7026ca1ea 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -11,6 +11,8 @@ #include "include/db.h" #include "include/env.h" #include "include/write_batch.h" +#include "port/port.h" +#include "util/crc32c.h" #include "util/histogram.h" #include "util/random.h" #include "util/testutil.h" @@ -25,6 +27,8 @@ // readseq -- read N values sequentially // readreverse -- read N values in reverse order // readrandom -- read N values in random order +// crc32c -- repeated crc32c of 4K of data +// sha1 -- repeated SHA1 computation over 4K of data // Meta operations: // compact -- Compact the entire DB // heapprofile -- Dump a heap profile (if supported by this port) @@ -34,17 +38,21 @@ // normal -- reset N back to its normal value (1000000) static const char* FLAGS_benchmarks = "fillseq," + "fillsync," "fillrandom," "overwrite," - "fillsync," + "readrandom," + "readrandom," // Extra run to allow previous compactions to quiesce "readseq," "readreverse," - "readrandom," "compact," + "readrandom," "readseq," "readreverse," - "readrandom," - "fill100K"; + "fill100K," + "crc32c," + "sha1" + ; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -330,6 +338,10 @@ class Benchmark { ReadRandom(); } else if (name == Slice("compact")) { Compact(); + } else if (name == Slice("crc32c")) { + Crc32c(4096, "(4K per op)"); + } else if (name == Slice("sha1")) { + SHA1(4096, "(4K per op)"); } else if (name == Slice("heapprofile")) { HeapProfile(); } else { @@ -340,6 +352,41 @@ class Benchmark { } private: + void Crc32c(int size, const char* label) { + // Checksum about 500MB of data total + string data(size, 'x'); + int64_t bytes = 0; + uint32_t crc = 0; + while (bytes < 500 * 1048576) { + crc = crc32c::Value(data.data(), size); + FinishedSingleOp(); + bytes += size; + } + // Print so result is not dead + fprintf(stderr, "... crc=0x%x\r", static_cast(crc)); + + bytes_ = bytes; + message_ = label; + } + + void SHA1(int size, const char* label) { + // SHA1 about 100MB of data total + string data(size, 'x'); + int64_t bytes = 0; + char sha1[20]; + while (bytes < 100 * 1048576) { + port::SHA1_Hash(data.data(), size, sha1); + FinishedSingleOp(); + bytes += size; + } + + // Print so result is not dead + fprintf(stderr, "... sha1=%02x...\r", static_cast(sha1[0])); + + bytes_ = bytes; + message_ = label; + } + void Open() { assert(db_ == NULL); Options options; diff --git a/db/db_iter.cc b/db/db_iter.cc index 165d7d49a..6726b513d 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -36,6 +36,16 @@ namespace { // numbers, deletion markers, overwrites, etc. class DBIter: public Iterator { public: + // Which direction is the iterator currently moving? + // (1) When moving forward, the internal iterator is positioned at + // the exact entry that yields this->key(), this->value() + // (2) When moving backwards, the internal iterator is positioned + // just before all entries whose user key == this->key(). + enum Direction { + kForward, + kReverse + }; + DBIter(const std::string* dbname, Env* env, const Comparator* cmp, Iterator* iter, SequenceNumber s) : dbname_(dbname), @@ -44,6 +54,7 @@ class DBIter: public Iterator { iter_(iter), sequence_(s), large_(NULL), + direction_(kForward), valid_(false) { } virtual ~DBIter() { @@ -53,48 +64,21 @@ class DBIter: public Iterator { virtual bool Valid() const { return valid_; } virtual Slice key() const { assert(valid_); - return key_; + return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; } virtual Slice value() const { assert(valid_); + Slice raw_value = (direction_ == kForward) ? iter_->value() : saved_value_; if (large_ == NULL) { - return value_; + return raw_value; } else { MutexLock l(&large_->mutex); if (!large_->produced) { - ReadIndirectValue(); + ReadIndirectValue(raw_value); } return large_->value; } } - - virtual void Next() { - assert(valid_); - // iter_ is already positioned past DBIter::key() - FindNextUserEntry(); - } - - virtual void Prev() { - assert(valid_); - bool ignored; - ScanUntilBeforeCurrentKey(&ignored); - FindPrevUserEntry(); - } - - virtual void Seek(const Slice& target) { - ParsedInternalKey ikey(target, sequence_, kValueTypeForSeek); - std::string tmp; - AppendInternalKey(&tmp, ikey); - iter_->Seek(tmp); - FindNextUserEntry(); - } - virtual void SeekToFirst() { - iter_->SeekToFirst(); - FindNextUserEntry(); - } - - virtual void SeekToLast(); - virtual Status status() const { if (status_.ok()) { if (large_ != NULL && !large_->status.ok()) return large_->status; @@ -104,23 +88,13 @@ class DBIter: public Iterator { } } + virtual void Next(); + virtual void Prev(); + virtual void Seek(const Slice& target); + virtual void SeekToFirst(); + virtual void SeekToLast(); + private: - void FindNextUserEntry(); - void FindPrevUserEntry(); - void SaveKey(const Slice& k) { key_.assign(k.data(), k.size()); } - void SaveValue(const Slice& v) { - if (value_.capacity() > v.size() + 1048576) { - std::string empty; - swap(empty, value_); - } - value_.assign(v.data(), v.size()); - } - bool ParseKey(ParsedInternalKey* key); - void SkipPast(const Slice& k); - void ScanUntilBeforeCurrentKey(bool* found_live); - - void ReadIndirectValue() const; - struct Large { port::Mutex mutex; std::string value; @@ -128,19 +102,42 @@ class DBIter: public Iterator { Status status; }; + void FindNextUserEntry(bool skipping, std::string* skip); + void FindPrevUserEntry(); + bool ParseKey(ParsedInternalKey* key); + void ReadIndirectValue(Slice ref) const; + + inline void SaveKey(const Slice& k, std::string* dst) { + dst->assign(k.data(), k.size()); + } + + inline void ForgetLargeValue() { + if (large_ != NULL) { + delete large_; + large_ = NULL; + } + } + + inline void ClearSavedValue() { + if (saved_value_.capacity() > 1048576) { + std::string empty; + swap(empty, saved_value_); + } else { + saved_value_.clear(); + } + } + const std::string* const dbname_; Env* const env_; - const Comparator* const user_comparator_; - - // iter_ is positioned just past current entry for DBIter if valid_ Iterator* const iter_; - SequenceNumber const sequence_; + Status status_; - std::string key_; // Always a user key - std::string value_; - Large* large_; // Non-NULL if value is an indirect reference + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse + Large* large_; // Non-NULL if value is an indirect reference + Direction direction_; bool valid_; // No copying allowed @@ -157,204 +154,189 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { } } -void DBIter::FindNextUserEntry() { - if (large_ != NULL) { - if (status_.ok() && !large_->status.ok()) { - status_ = large_->status; - } - delete large_; - large_ = NULL; - } - while (iter_->Valid()) { - ParsedInternalKey ikey; - if (!ParseKey(&ikey)) { - // Skip past corrupted entry +void DBIter::Next() { + assert(valid_); + ForgetLargeValue(); + + if (direction_ == kReverse) { // Switch directions? + direction_ = kForward; + // iter_ is pointing just before the entries for this->key(), + // so advance into the range of entries for this->key() and then + // use the normal skipping code below. + if (!iter_->Valid()) { + iter_->SeekToFirst(); + } else { iter_->Next(); - continue; } - if (ikey.sequence > sequence_) { - // Ignore entries newer than the snapshot - iter_->Next(); - continue; - } - - switch (ikey.type) { - case kTypeDeletion: - SaveKey(ikey.user_key); // Make local copy for use by SkipPast() - iter_->Next(); - SkipPast(key_); - // Do not return deleted entries. Instead keep looping. - break; - - case kTypeValue: - SaveKey(ikey.user_key); - SaveValue(iter_->value()); - iter_->Next(); - SkipPast(key_); - // Yield the value we just found. - valid_ = true; - return; - - case kTypeLargeValueRef: - SaveKey(ikey.user_key); - // Save the large value ref as value_, and read it lazily on a call - // to value() - SaveValue(iter_->value()); - large_ = new Large; - large_->produced = false; - iter_->Next(); - SkipPast(key_); - // Yield the value we just found. - valid_ = true; - return; + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; } } - valid_ = false; - key_.clear(); - value_.clear(); - assert(large_ == NULL); + + // Temporarily use saved_key_ as storage for key to skip. + std::string* skip = &saved_key_; + SaveKey(ExtractUserKey(iter_->key()), skip); + FindNextUserEntry(true, skip); } -void DBIter::SkipPast(const Slice& k) { - while (iter_->Valid()) { +void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { + // Loop until we hit an acceptable entry to yield + assert(iter_->Valid()); + assert(direction_ == kForward); + assert(large_ == NULL); + do { ParsedInternalKey ikey; - // Note that if we cannot parse an internal key, we keep looping - // so that if we have a run like the following: - // => value100 - // - // => value50 - // we will skip over the corrupted entry as well as value50. - if (ParseKey(&ikey) && user_comparator_->Compare(ikey.user_key, k) != 0) { - break; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + switch (ikey.type) { + case kTypeDeletion: + // Arrange to skip all upcoming entries for this key since + // they are hidden by this deletion. + SaveKey(ikey.user_key, skip); + skipping = true; + break; + case kTypeValue: + case kTypeLargeValueRef: + if (skipping && + user_comparator_->Compare(ikey.user_key, *skip) <= 0) { + // Entry hidden + } else { + valid_ = true; + saved_key_.clear(); + if (ikey.type == kTypeLargeValueRef) { + large_ = new Large; + large_->produced = false; + } + return; + } + break; + } } iter_->Next(); + } while (iter_->Valid()); + saved_key_.clear(); + valid_ = false; +} + +void DBIter::Prev() { + assert(valid_); + ForgetLargeValue(); + + if (direction_ == kForward) { // Switch directions? + // iter_ is pointing at the current entry. Scan backwards until + // the key changes so we can use the normal reverse scanning code. + assert(iter_->Valid()); // Otherwise valid_ would have been false + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + while (true) { + iter_->Prev(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + return; + } + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), + saved_key_) < 0) { + break; + } + } + direction_ = kReverse; + } + + FindPrevUserEntry(); +} + +void DBIter::FindPrevUserEntry() { + assert(direction_ == kReverse); + assert(large_ == NULL); + + ValueType value_type = kTypeDeletion; + if (iter_->Valid()) { + SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + do { + ParsedInternalKey ikey; + if (ParseKey(&ikey) && ikey.sequence <= sequence_) { + if ((value_type != kTypeDeletion) && + user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { + // We encountered a non-deleted value in entries for previous keys, + break; + } + value_type = ikey.type; + if (value_type == kTypeDeletion) { + ClearSavedValue(); + } else { + Slice raw_value = iter_->value(); + if (saved_value_.capacity() > raw_value.size() + 1048576) { + std::string empty; + swap(empty, saved_value_); + } + saved_value_.assign(raw_value.data(), raw_value.size()); + } + } + iter_->Prev(); + } while (iter_->Valid()); + } + + if (value_type == kTypeDeletion) { + // End + valid_ = false; + saved_key_.clear(); + ClearSavedValue(); + direction_ = kForward; + } else { + valid_ = true; + if (value_type == kTypeLargeValueRef) { + large_ = new Large; + large_->produced = false; + } + } +} + +void DBIter::Seek(const Slice& target) { + direction_ = kForward; + ForgetLargeValue(); + ClearSavedValue(); + saved_key_.clear(); + AppendInternalKey( + &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + iter_->Seek(saved_key_); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; + } +} + +void DBIter::SeekToFirst() { + direction_ = kForward; + ForgetLargeValue(); + ClearSavedValue(); + iter_->SeekToFirst(); + if (iter_->Valid()) { + FindNextUserEntry(false, &saved_key_ /* temporary storage */); + } else { + valid_ = false; } } void DBIter::SeekToLast() { - // Position iter_ at the last uncorrupted user key and then - // let FindPrevUserEntry() do the heavy lifting to find - // a user key that is live. + direction_ = kReverse; + ForgetLargeValue(); + ClearSavedValue(); iter_->SeekToLast(); - ParsedInternalKey current; - while (iter_->Valid() && !ParseKey(¤t)) { - iter_->Prev(); - } - if (iter_->Valid()) { - SaveKey(current.user_key); - } FindPrevUserEntry(); } -// Let X be the user key at which iter_ is currently positioned. -// Adjust DBIter to point at the last entry with a key <= X that -// has a live value. -void DBIter::FindPrevUserEntry() { - // Consider the following example: - // - // A@540 - // A@400 - // - // B@300 - // B@200 - // B@100 <- iter_ - // - // C@301 - // C@201 - // - // The comments marked "(first iteration)" below relate what happens - // for the preceding example in the first iteration of the while loop - // below. There may be more than one iteration either if there are - // no live values for B, or if there is a corruption. - while (iter_->Valid()) { - std::string saved = key_; - bool found_live; - ScanUntilBeforeCurrentKey(&found_live); - // (first iteration) iter_ at A@400 - if (found_live) { - // Step forward into range of entries with user key >= saved - if (!iter_->Valid()) { - iter_->SeekToFirst(); - } else { - iter_->Next(); - } - // (first iteration) iter_ at B@300 - - FindNextUserEntry(); // Sets key_ to the key of the next value it found - if (valid_ && user_comparator_->Compare(key_, saved) == 0) { - // (first iteration) iter_ at C@301 - return; - } - - // FindNextUserEntry() could not find any entries under the - // user key "saved". This is probably a corruption since - // ScanUntilBefore(saved) found a live value. So we skip - // backwards to an earlier key and ignore the corrupted - // entries for "saved". - // - // (first iteration) iter_ at C@301 and saved == "B" - key_ = saved; - bool ignored; - ScanUntilBeforeCurrentKey(&ignored); - // (first iteration) iter_ at A@400 - } - } - valid_ = false; - key_.clear(); - value_.clear(); -} - -void DBIter::ScanUntilBeforeCurrentKey(bool* found_live) { - *found_live = false; - if (!iter_->Valid()) { - iter_->SeekToLast(); - } - - while (iter_->Valid()) { - ParsedInternalKey current; - if (!ParseKey(¤t)) { - iter_->Prev(); - continue; - } - - if (current.sequence > sequence_) { - // Ignore entries that are serialized after this read - iter_->Prev(); - continue; - } - - const int cmp = user_comparator_->Compare(current.user_key, key_); - if (cmp < 0) { - SaveKey(current.user_key); - return; - } else if (cmp == 0) { - switch (current.type) { - case kTypeDeletion: - *found_live = false; - break; - - case kTypeValue: - case kTypeLargeValueRef: - *found_live = true; - break; - } - } else { // cmp > 0 - *found_live = false; - } - - iter_->Prev(); - } -} - -void DBIter::ReadIndirectValue() const { +void DBIter::ReadIndirectValue(Slice ref) const { assert(!large_->produced); large_->produced = true; LargeValueRef large_ref; - if (value_.size() != LargeValueRef::ByteSize()) { + if (ref.size() != LargeValueRef::ByteSize()) { large_->status = Status::Corruption("malformed large value reference"); return; } - memcpy(large_ref.data, value_.data(), LargeValueRef::ByteSize()); + memcpy(large_ref.data, ref.data(), LargeValueRef::ByteSize()); std::string fname = LargeValueFileName(*dbname_, large_ref); RandomAccessFile* file; Status s = env_->NewRandomAccessFile(fname, &file); diff --git a/db/db_test.cc b/db/db_test.cc index 888c5605c..041417630 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -209,6 +209,16 @@ class DBTest { } } } + + std::string IterStatus(Iterator* iter) { + std::string result; + if (iter->Valid()) { + result = iter->key().ToString() + "->" + iter->value().ToString(); + } else { + result = "(invalid)"; + } + return result; + } }; TEST(DBTest, Empty) { @@ -234,6 +244,180 @@ TEST(DBTest, PutDeleteGet) { ASSERT_EQ("NOT_FOUND", Get("foo")); } +TEST(DBTest, IterEmpty) { + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("foo"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSingle) { + ASSERT_OK(Put("a", "va")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterMulti) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", "vb")); + ASSERT_OK(Put("c", "vc")); + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->Seek(""); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("a"); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Seek("ax"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("b"); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Seek("z"); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + // Switch from reverse to forward + iter->SeekToLast(); + iter->Prev(); + iter->Prev(); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Switch from forward to reverse + iter->SeekToFirst(); + iter->Next(); + iter->Next(); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + + // Make sure iter stays at snapshot + ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a2", "va3")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Delete("b")); + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->vb"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + +TEST(DBTest, IterSmallAndLargeMix) { + ASSERT_OK(Put("a", "va")); + ASSERT_OK(Put("b", std::string(100000, 'b'))); + ASSERT_OK(Put("c", "vc")); + ASSERT_OK(Put("d", std::string(100000, 'd'))); + ASSERT_OK(Put("e", std::string(100000, 'e'))); + + Iterator* iter = db_->NewIterator(ReadOptions()); + + iter->SeekToFirst(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Next(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "c->vc"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b')); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "a->va"); + iter->Prev(); + ASSERT_EQ(IterStatus(iter), "(invalid)"); + + delete iter; +} + TEST(DBTest, Recover) { ASSERT_OK(Put("foo", "v1")); ASSERT_OK(Put("baz", "v5")); diff --git a/doc/index.html b/doc/index.html index 53471d2bc..e0baf2e7e 100644 --- a/doc/index.html +++ b/doc/index.html @@ -132,8 +132,7 @@ range [start,limit): } You can also process entries in reverse order. (Caveat: reverse -iteration is currently a factor of two or three slower than forward -iteration.) +iteration may be somewhat slower than forward iteration.)

   for (it->SeekToLast(); it->Valid(); it->Prev()) {
diff --git a/port/sha1_test.cc b/port/sha1_test.cc
index 46bbebab9..b182e6757 100644
--- a/port/sha1_test.cc
+++ b/port/sha1_test.cc
@@ -31,22 +31,6 @@ TEST(SHA1, Simple) {
             TestSHA1(x.data(), x.size()));
 }
 
-TEST(SHA1, Benchmark) {
-  std::string data(1048576 * 100, 'x');
-  double start = Env::Default()->NowMicros() * 1e-6;
-  static const int kIters = 10;
-  uint32_t sha1 = 0;
-  for (int i = 0; i < kIters; i++) {
-    char hash_val[20];
-    SHA1_Hash(data.data(), data.size(), hash_val);
-    sha1 |= hash_val[0];
-  }
-  double finish = Env::Default()->NowMicros() * 1e-6;
-  double mb = (static_cast(data.size()) * kIters) / 1048576.0;
-  fprintf(stderr, "SHA1 %0.0f MB: %.3f secs; %.1f MB/s, dummy=0x%02x\n",
-          mb, (finish - start), mb / (finish - start), sha1);
-}
-
 }
 }
 
diff --git a/util/crc32c_test.cc b/util/crc32c_test.cc
index a7fc7584d..ba9e80411 100644
--- a/util/crc32c_test.cc
+++ b/util/crc32c_test.cc
@@ -64,20 +64,6 @@ TEST(CRC, Mask) {
   ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc)))));
 }
 
-TEST(CRC, Benchmark) {
-  std::string data(1048576 * 100, 'x');
-  double start = Env::Default()->NowMicros() * 1e-6;
-  static const int kIters = 10;
-  uint32_t crc = 0;
-  for (int i = 0; i < kIters; i++) {
-    crc |= Value(data.data(), data.size());
-  }
-  double finish = Env::Default()->NowMicros() * 1e-6;
-  double mb = (static_cast(data.size()) * kIters) / 1048576.0;
-  fprintf(stderr, "CRC %0.0f MB: %.3f secs; %.1f MB/s, crc=0x%08x\n",
-          mb, (finish - start), mb / (finish - start), crc);
-}
-
 }
 }