From ceff6f12152785a54885a47db349a6d8dfd0ce2c Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 17 Dec 2014 14:18:54 -0500 Subject: [PATCH 001/174] Fix Android/MIPS build. port/atomic_pointer.h was missing an implementation for MemoryBarrier() for this platform. --- port/atomic_pointer.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index 0f6f05ecdf..1c4c7aafc6 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -39,6 +39,8 @@ #define ARCH_CPU_ARM64_FAMILY 1 #elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) #define ARCH_CPU_PPC_FAMILY 1 +#elif defined(__mips__) +#define ARCH_CPU_MIPS_FAMILY 1 #endif namespace leveldb { @@ -110,6 +112,13 @@ inline void MemoryBarrier() { } #define LEVELDB_HAVE_MEMORY_BARRIER +// MIPS +#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + __asm__ __volatile__("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + #endif // AtomicPointer built using platform-specific MemoryBarrier() From c85addcdf36849ea892cef37efde5d8791e3a7f4 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Sat, 10 Jan 2015 17:36:40 -0800 Subject: [PATCH 002/174] readme: improved documentation link This replaces htmlpreview with [rawgit](http://rawgit.com/). rawgit is faster and doesnt use JS to render the page, making it SEO friendly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 480affb5ca..38f78050a7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Forward and backward iteration is supported over the data. * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. - * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code. + * [Detailed documentation](https://rawgit.com/google/leveldb/master/doc/index.html) about how to use the library is included with the source code. # Limitations From dd598676cd655dc2a2aaef47715ce18175d4a550 Mon Sep 17 00:00:00 2001 From: Wankai Zhang Date: Thu, 29 Jan 2015 14:15:31 +0800 Subject: [PATCH 003/174] block_builder header file dependency fixed --- table/block_builder.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/table/block_builder.cc b/table/block_builder.cc index db660cd07c..c744cfa602 100644 --- a/table/block_builder.cc +++ b/table/block_builder.cc @@ -30,8 +30,8 @@ #include #include +#include "leveldb/options.h" #include "leveldb/comparator.h" -#include "leveldb/table_builder.h" #include "util/coding.h" namespace leveldb { From 0e0f07417ca859da1fe3965984b581a1b75e9021 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Tue, 17 Feb 2015 09:55:40 -0800 Subject: [PATCH 004/174] documentation. improved link --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 38f78050a7..36cec633d9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Forward and backward iteration is supported over the data. * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. - * [Detailed documentation](https://rawgit.com/google/leveldb/master/doc/index.html) about how to use the library is included with the source code. + +# Documentation + [LevelDB library documentation](https://rawgit.com/google/leveldb/master/doc/index.html) is online and bundled with the source code. # Limitations From 8fcceb2a6ffbb3dc8c75763951a2536a3571b08e Mon Sep 17 00:00:00 2001 From: ideawu Date: Mon, 20 Apr 2015 12:39:14 +0800 Subject: [PATCH 005/174] log compaction output file's level along with number --- db/db_impl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 49b95953b4..7912174fa3 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -821,8 +821,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, delete iter; if (s.ok()) { Log(options_.info_log, - "Generated table #%llu: %lld keys, %lld bytes", + "Generated table #%llu@%d: %lld keys, %lld bytes", (unsigned long long) output_number, + compact->compaction->level(), (unsigned long long) current_entries, (unsigned long long) current_bytes); } From 76bba139c083f7d54daa170165cad3e23c9d3ec8 Mon Sep 17 00:00:00 2001 From: ideawu Date: Mon, 20 Apr 2015 12:41:01 +0800 Subject: [PATCH 006/174] fix indent --- db/db_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 7912174fa3..a206cc4c58 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -823,7 +823,7 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, Log(options_.info_log, "Generated table #%llu@%d: %lld keys, %lld bytes", (unsigned long long) output_number, - compact->compaction->level(), + compact->compaction->level(), (unsigned long long) current_entries, (unsigned long long) current_bytes); } From ac1d69da31205a979b5a8510f33c31ae977530f0 Mon Sep 17 00:00:00 2001 From: Sanjay Ghemawat Date: Thu, 11 Dec 2014 08:13:18 -0800 Subject: [PATCH 007/174] LevelDB now attempts to reuse the preceding MANIFEST and log file when re-opened. (Based on a suggestion by cmumford.) "open" benchmark on my workstation speeds up significantly since we can now avoid three fdatasync calls and a compaction per open: Before: ~80000 microseconds After: ~130 microseconds Details: (1) Added Options::reuse_logs (currently defaults to false) to control new behavior. The intention is to change the default to true after some baking. (2) Added Env::NewAppendableFile() whose default implementation returns a not-supported error. (3) VersionSet::Recovery attempts to reuse the MANIFEST from which it is recovering. (4) DBImpl recovery attempts to reuse the last log file and memtable. (5) db_test.cc now tests a new configuration that sets reuse_logs to true. (6) fault_injection_test also tests a reuse_logs==true config. (7) Added a new recovery_test. --- Makefile | 4 + db/corruption_test.cc | 2 +- db/db_bench.cc | 7 + db/db_impl.cc | 178 +++++++++++-------- db/db_impl.h | 8 +- db/db_test.cc | 21 ++- db/fault_injection_test.cc | 148 +++++++++------- db/log_test.cc | 24 ++- db/log_writer.cc | 17 +- db/log_writer.h | 6 + db/recovery_test.cc | 324 ++++++++++++++++++++++++++++++++++ db/version_set.cc | 37 +++- db/version_set.h | 4 +- helpers/memenv/memenv.cc | 13 ++ helpers/memenv/memenv_test.cc | 13 +- include/leveldb/env.h | 18 ++ include/leveldb/options.h | 6 + include/leveldb/status.h | 3 + util/env.cc | 4 + util/env_posix.cc | 13 ++ util/options.cc | 2 +- util/testutil.h | 10 ++ 22 files changed, 707 insertions(+), 155 deletions(-) create mode 100644 db/recovery_test.cc diff --git a/Makefile b/Makefile index 24f214a086..b1a1702c5c 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ TESTS = \ issue200_test \ log_test \ memenv_test \ + recovery_test \ skiplist_test \ table_test \ version_edit_test \ @@ -177,6 +178,9 @@ issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +recovery_test: db/recovery_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/recovery_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/db/corruption_test.cc b/db/corruption_test.cc index 96afc68913..37a484d25f 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -36,7 +36,7 @@ class CorruptionTest { tiny_cache_ = NewLRUCache(100); options_.env = &env_; options_.block_cache = tiny_cache_; - dbname_ = test::TmpDir() + "/db_test"; + dbname_ = test::TmpDir() + "/corruption_test"; DestroyDB(dbname_, options_); db_ = NULL; diff --git a/db/db_bench.cc b/db/db_bench.cc index e5975bd6e2..691fd284dc 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -100,6 +100,9 @@ static int FLAGS_bloom_bits = -1; // benchmark will fail. static bool FLAGS_use_existing_db = false; +// If true, reuse existing log/MANIFEST files when re-opening a database. +static bool FLAGS_reuse_logs = false; + // Use the db with the following name. static const char* FLAGS_db = NULL; @@ -700,6 +703,7 @@ class Benchmark { options.write_buffer_size = FLAGS_write_buffer_size; options.max_open_files = FLAGS_open_files; options.filter_policy = filter_policy_; + options.reuse_logs = FLAGS_reuse_logs; Status s = DB::Open(options, FLAGS_db, &db_); if (!s.ok()) { fprintf(stderr, "open error: %s\n", s.ToString().c_str()); @@ -954,6 +958,9 @@ int main(int argc, char** argv) { } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_reuse_logs = n; } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { FLAGS_num = n; } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { diff --git a/db/db_impl.cc b/db/db_impl.cc index 49b95953b4..3fd21b047f 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -125,7 +125,7 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) db_lock_(NULL), shutting_down_(NULL), bg_cv_(&mutex_), - mem_(new MemTable(internal_comparator_)), + mem_(NULL), imm_(NULL), logfile_(NULL), logfile_number_(0), @@ -134,7 +134,6 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), manual_compaction_(NULL) { - mem_->Ref(); has_imm_.Release_Store(NULL); // Reserve ten files or so for other uses and give the rest to TableCache. @@ -271,7 +270,7 @@ void DBImpl::DeleteObsoleteFiles() { } } -Status DBImpl::Recover(VersionEdit* edit) { +Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { mutex_.AssertHeld(); // Ignore error from CreateDir since the creation of the DB is @@ -301,66 +300,69 @@ Status DBImpl::Recover(VersionEdit* edit) { } } - s = versions_->Recover(); - if (s.ok()) { - SequenceNumber max_sequence(0); - - // Recover from all newer log files than the ones named in the - // descriptor (new log files may have been added by the previous - // incarnation without registering them in the descriptor). - // - // Note that PrevLogNumber() is no longer used, but we pay - // attention to it in case we are recovering a database - // produced by an older version of leveldb. - const uint64_t min_log = versions_->LogNumber(); - const uint64_t prev_log = versions_->PrevLogNumber(); - std::vector filenames; - s = env_->GetChildren(dbname_, &filenames); + s = versions_->Recover(save_manifest); + if (!s.ok()) { + return s; + } + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit, + &max_sequence); if (!s.ok()) { return s; } - std::set expected; - versions_->AddLiveFiles(&expected); - uint64_t number; - FileType type; - std::vector logs; - for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type)) { - expected.erase(number); - if (type == kLogFile && ((number >= min_log) || (number == prev_log))) - logs.push_back(number); - } - } - if (!expected.empty()) { - char buf[50]; - snprintf(buf, sizeof(buf), "%d missing files; e.g.", - static_cast(expected.size())); - return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); - } - - // Recover in the order in which the logs were generated - std::sort(logs.begin(), logs.end()); - for (size_t i = 0; i < logs.size(); i++) { - s = RecoverLogFile(logs[i], edit, &max_sequence); - // The previous incarnation may not have written any MANIFEST - // records after allocating this log number. So we manually - // update the file number allocation counter in VersionSet. - versions_->MarkFileNumberUsed(logs[i]); - } + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } - if (s.ok()) { - if (versions_->LastSequence() < max_sequence) { - versions_->SetLastSequence(max_sequence); - } - } + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); } - return s; + return Status::OK(); } -Status DBImpl::RecoverLogFile(uint64_t log_number, - VersionEdit* edit, +Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, + bool* save_manifest, VersionEdit* edit, SequenceNumber* max_sequence) { struct LogReporter : public log::Reader::Reporter { Env* env; @@ -405,6 +407,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, std::string scratch; Slice record; WriteBatch batch; + int compactions = 0; MemTable* mem = NULL; while (reader.ReadRecord(&record, &scratch) && status.ok()) { @@ -432,25 +435,52 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, } if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + compactions++; + *save_manifest = true; status = WriteLevel0Table(mem, edit, NULL); + mem->Unref(); + mem = NULL; if (!status.ok()) { // Reflect errors immediately so that conditions like full // file-systems cause the DB::Open() to fail. break; } - mem->Unref(); - mem = NULL; } } - if (status.ok() && mem != NULL) { - status = WriteLevel0Table(mem, edit, NULL); - // Reflect errors immediately so that conditions like full - // file-systems cause the DB::Open() to fail. + delete file; + + // See if we should keep reusing the last log file. + if (status.ok() && options_.reuse_logs && last_log && compactions == 0) { + assert(logfile_ == NULL); + assert(log_ == NULL); + assert(mem_ == NULL); + uint64_t lfile_size; + if (env_->GetFileSize(fname, &lfile_size).ok() && + env_->NewAppendableFile(fname, &logfile_).ok()) { + Log(options_.info_log, "Reusing old log %s \n", fname.c_str()); + log_ = new log::Writer(logfile_, lfile_size); + logfile_number_ = log_number; + if (mem != NULL) { + mem_ = mem; + mem = NULL; + } else { + // mem can be NULL if lognum exists but was empty. + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + } + } + } + + if (mem != NULL) { + // mem did not get reused; compact it. + if (status.ok()) { + *save_manifest = true; + status = WriteLevel0Table(mem, edit, NULL); + } + mem->Unref(); } - if (mem != NULL) mem->Unref(); - delete file; return status; } @@ -1449,8 +1479,11 @@ Status DB::Open(const Options& options, const std::string& dbname, DBImpl* impl = new DBImpl(options, dbname); impl->mutex_.Lock(); VersionEdit edit; - Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists - if (s.ok()) { + // Recover handles create_if_missing, error_if_exists + bool save_manifest = false; + Status s = impl->Recover(&edit, &save_manifest); + if (s.ok() && impl->mem_ == NULL) { + // Create new log and a corresponding memtable. uint64_t new_log_number = impl->versions_->NewFileNumber(); WritableFile* lfile; s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), @@ -1460,15 +1493,22 @@ Status DB::Open(const Options& options, const std::string& dbname, impl->logfile_ = lfile; impl->logfile_number_ = new_log_number; impl->log_ = new log::Writer(lfile); - s = impl->versions_->LogAndApply(&edit, &impl->mutex_); - } - if (s.ok()) { - impl->DeleteObsoleteFiles(); - impl->MaybeScheduleCompaction(); + impl->mem_ = new MemTable(impl->internal_comparator_); + impl->mem_->Ref(); } } + if (s.ok() && save_manifest) { + edit.SetPrevLogNumber(0); // No older logs needed after recovery. + edit.SetLogNumber(impl->logfile_number_); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } impl->mutex_.Unlock(); if (s.ok()) { + assert(impl->mem_ != NULL); *dbptr = impl; } else { delete impl; diff --git a/db/db_impl.h b/db/db_impl.h index cfc998164a..8ff323e728 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -78,7 +78,8 @@ class DBImpl : public DB { // Recover the descriptor from persistent storage. May do a significant // amount of work to recover recently logged updates. Any changes to // be made to the descriptor are added to *edit. - Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status Recover(VersionEdit* edit, bool* save_manifest) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); void MaybeIgnoreError(Status* s) const; @@ -90,9 +91,8 @@ class DBImpl : public DB { // Errors are recorded in bg_error_. void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); - Status RecoverLogFile(uint64_t log_number, - VersionEdit* edit, - SequenceNumber* max_sequence) + Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest, + VersionEdit* edit, SequenceNumber* max_sequence) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) diff --git a/db/db_test.cc b/db/db_test.cc index 0fed9137d5..74ba8b05e8 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -193,6 +193,7 @@ class DBTest { // Sequence of option configurations to try enum OptionConfig { kDefault, + kReuse, kFilter, kUncompressed, kEnd @@ -237,7 +238,11 @@ class DBTest { // Return the current option configuration. Options CurrentOptions() { Options options; + options.reuse_logs = false; switch (option_config_) { + case kReuse: + options.reuse_logs = true; + break; case kFilter: options.filter_policy = filter_policy_; break; @@ -1080,6 +1085,14 @@ TEST(DBTest, ApproximateSizes) { // 0 because GetApproximateSizes() does not account for memtable space ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + if (options.reuse_logs) { + // Recovery will reuse memtable, and GetApproximateSizes() does not + // account for memtable usage; + Reopen(&options); + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + continue; + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); @@ -1123,6 +1136,11 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + if (options.reuse_logs) { + // Need to force a memtable compaction since recovery does not do so. + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); @@ -2084,7 +2102,8 @@ void BM_LogAndApply(int iters, int num_base_files) { InternalKeyComparator cmp(BytewiseComparator()); Options options; VersionSet vset(dbname, &options, NULL, &cmp); - ASSERT_OK(vset.Recover()); + bool save_manifest; + ASSERT_OK(vset.Recover(&save_manifest)); VersionEdit vbase; uint64_t fnum = 1; for (int i = 0; i < num_base_files; i++) { diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index 8e3e603442..875dfe81ee 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -106,7 +106,7 @@ struct FileState { // is written to or sync'ed. class TestWritableFile : public WritableFile { public: - TestWritableFile(const std::string& fname, + TestWritableFile(const FileState& state, WritableFile* f, FaultInjectionTestEnv* env); virtual ~TestWritableFile(); @@ -130,6 +130,8 @@ class FaultInjectionTestEnv : public EnvWrapper { virtual ~FaultInjectionTestEnv() { } virtual Status NewWritableFile(const std::string& fname, WritableFile** result); + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); virtual Status DeleteFile(const std::string& f); virtual Status RenameFile(const std::string& s, const std::string& t); @@ -154,15 +156,14 @@ class FaultInjectionTestEnv : public EnvWrapper { bool filesystem_active_; // Record flushes, syncs, writes }; -TestWritableFile::TestWritableFile(const std::string& fname, +TestWritableFile::TestWritableFile(const FileState& state, WritableFile* f, FaultInjectionTestEnv* env) - : state_(fname), + : state_(state), target_(f), writable_file_opened_(true), env_(env) { assert(f != NULL); - state_.pos_ = 0; } TestWritableFile::~TestWritableFile() { @@ -228,9 +229,12 @@ Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname, WritableFile* actual_writable_file; Status s = target()->NewWritableFile(fname, &actual_writable_file); if (s.ok()) { - *result = new TestWritableFile(fname, actual_writable_file, this); - // WritableFile doesn't append to files, so if the same file is opened again - // then it will be truncated - so forget our saved state. + FileState state(fname); + state.pos_ = 0; + *result = new TestWritableFile(state, actual_writable_file, this); + // NewWritableFile doesn't append to files, so if the same file is + // opened again then it will be truncated - so forget our saved + // state. UntrackFile(fname); MutexLock l(&mutex_); new_files_since_last_dir_sync_.insert(fname); @@ -238,6 +242,26 @@ Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname, return s; } +Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewAppendableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + { + MutexLock l(&mutex_); + if (db_file_state_.count(fname) == 0) { + new_files_since_last_dir_sync_.insert(fname); + } else { + state = db_file_state_[fname]; + } + } + *result = new TestWritableFile(state, actual_writable_file, this); + } + return s; +} + Status FaultInjectionTestEnv::DropUnsyncedFileData() { Status s; MutexLock l(&mutex_); @@ -301,9 +325,10 @@ Status FaultInjectionTestEnv::RenameFile(const std::string& s, } void FaultInjectionTestEnv::ResetState() { + // Since we are not destroying the database, the existing files + // should keep their recorded synced/flushed state. Therefore + // we do not reset db_file_state_ and new_files_since_last_dir_sync_. MutexLock l(&mutex_); - db_file_state_.clear(); - new_files_since_last_dir_sync_.clear(); SetFilesystemActive(true); } @@ -342,51 +367,28 @@ class FaultInjectionTest { Options options_; DB* db_; - FaultInjectionTest() : env_(NULL), tiny_cache_(NULL), db_(NULL) { NewDB(); } - - ~FaultInjectionTest() { ASSERT_OK(TearDown()); } - - Status NewDB() { - assert(db_ == NULL); - assert(tiny_cache_ == NULL); - assert(env_ == NULL); - - env_ = new FaultInjectionTestEnv(); - - options_ = Options(); + FaultInjectionTest() + : env_(new FaultInjectionTestEnv), + tiny_cache_(NewLRUCache(100)), + db_(NULL) { + dbname_ = test::TmpDir() + "/fault_test"; + DestroyDB(dbname_, Options()); // Destroy any db from earlier run + options_.reuse_logs = true; options_.env = env_; options_.paranoid_checks = true; - - tiny_cache_ = NewLRUCache(100); options_.block_cache = tiny_cache_; - dbname_ = test::TmpDir() + "/fault_test"; - options_.create_if_missing = true; - Status s = OpenDB(); - options_.create_if_missing = false; - return s; } - Status SetUp() { - Status s = TearDown(); - if (s.ok()) { - s = NewDB(); - } - return s; - } - - Status TearDown() { + ~FaultInjectionTest() { CloseDB(); - - Status s = DestroyDB(dbname_, Options()); - + DestroyDB(dbname_, Options()); delete tiny_cache_; - tiny_cache_ = NULL; - delete env_; - env_ = NULL; + } - return s; + void ReuseLogs(bool reuse) { + options_.reuse_logs = reuse; } void Build(int start_idx, int num_vals) { @@ -506,33 +508,43 @@ class FaultInjectionTest { ResetDBState(reset_method); ASSERT_OK(OpenDB()); } -}; - -TEST(FaultInjectionTest, FaultTest) { - Random rnd(0); - ASSERT_OK(SetUp()); - for (size_t idx = 0; idx < kNumIterations; idx++) { - int num_pre_sync = rnd.Uniform(kMaxNumValues); - int num_post_sync = rnd.Uniform(kMaxNumValues); - - PartialCompactTestPreFault(num_pre_sync, num_post_sync); - PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, - num_pre_sync, - num_post_sync); - NoWriteTestPreFault(); - NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA); + void DoTest() { + Random rnd(0); + ASSERT_OK(OpenDB()); + for (size_t idx = 0; idx < kNumIterations; idx++) { + int num_pre_sync = rnd.Uniform(kMaxNumValues); + int num_post_sync = rnd.Uniform(kMaxNumValues); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, + num_pre_sync, + num_post_sync); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + // No new files created so we expect all values since no files will be + // dropped. + PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, + num_pre_sync + num_post_sync, + 0); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); + } + } +}; - PartialCompactTestPreFault(num_pre_sync, num_post_sync); - // No new files created so we expect all values since no files will be - // dropped. - PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, - num_pre_sync + num_post_sync, - 0); +TEST(FaultInjectionTest, FaultTestNoLogReuse) { + ReuseLogs(false); + DoTest(); +} - NoWriteTestPreFault(); - NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); - } +TEST(FaultInjectionTest, FaultTestWithLogReuse) { + ReuseLogs(true); + DoTest(); } } // namespace leveldb diff --git a/db/log_test.cc b/db/log_test.cc index dcf0562652..11e4a47e62 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -104,7 +104,7 @@ class LogTest { StringSource source_; ReportCollector report_; bool reading_; - Writer writer_; + Writer* writer_; Reader reader_; // Record metadata for testing initial offset functionality @@ -113,14 +113,23 @@ class LogTest { public: LogTest() : reading_(false), - writer_(&dest_), + writer_(new Writer(&dest_)), reader_(&source_, &report_, true/*checksum*/, 0/*initial_offset*/) { } + ~LogTest() { + delete writer_; + } + + void ReopenForAppend() { + delete writer_; + writer_ = new Writer(&dest_, dest_.contents_.size()); + } + void Write(const std::string& msg) { ASSERT_TRUE(!reading_) << "Write() after starting to read"; - writer_.AddRecord(Slice(msg)); + writer_->AddRecord(Slice(msg)); } size_t WrittenBytes() const { @@ -318,6 +327,15 @@ TEST(LogTest, AlignedEof) { ASSERT_EQ("EOF", Read()); } +TEST(LogTest, OpenForAppend) { + Write("hello"); + ReopenForAppend(); + Write("world"); + ASSERT_EQ("hello", Read()); + ASSERT_EQ("world", Read()); + ASSERT_EQ("EOF", Read()); +} + TEST(LogTest, RandomRead) { const int N = 500; Random write_rnd(301); diff --git a/db/log_writer.cc b/db/log_writer.cc index 2da99ac088..74a03270da 100644 --- a/db/log_writer.cc +++ b/db/log_writer.cc @@ -12,15 +12,24 @@ namespace leveldb { namespace log { -Writer::Writer(WritableFile* dest) - : dest_(dest), - block_offset_(0) { +static void InitTypeCrc(uint32_t* type_crc) { for (int i = 0; i <= kMaxRecordType; i++) { char t = static_cast(i); - type_crc_[i] = crc32c::Value(&t, 1); + type_crc[i] = crc32c::Value(&t, 1); } } +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + InitTypeCrc(type_crc_); +} + +Writer::Writer(WritableFile* dest, uint64_t dest_length) + : dest_(dest), block_offset_(dest_length % kBlockSize) { + InitTypeCrc(type_crc_); +} + Writer::~Writer() { } diff --git a/db/log_writer.h b/db/log_writer.h index a3a954d967..9e7cc4705b 100644 --- a/db/log_writer.h +++ b/db/log_writer.h @@ -22,6 +22,12 @@ class Writer { // "*dest" must be initially empty. // "*dest" must remain live while this Writer is in use. explicit Writer(WritableFile* dest); + + // Create a writer that will append data to "*dest". + // "*dest" must have initial length "dest_length". + // "*dest" must remain live while this Writer is in use. + Writer(WritableFile* dest, uint64_t dest_length); + ~Writer(); Status AddRecord(const Slice& slice); diff --git a/db/recovery_test.cc b/db/recovery_test.cc new file mode 100644 index 0000000000..e0d0bf83c4 --- /dev/null +++ b/db/recovery_test.cc @@ -0,0 +1,324 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class RecoveryTest { + public: + RecoveryTest() : env_(Env::Default()), db_(NULL) { + dbname_ = test::TmpDir() + "/recovery_test"; + DestroyDB(dbname_, Options()); + Open(); + } + + ~RecoveryTest() { + Close(); + DestroyDB(dbname_, Options()); + } + + DBImpl* dbfull() const { return reinterpret_cast(db_); } + Env* env() const { return env_; } + + bool CanAppend() { + WritableFile* tmp; + Status s = env_->NewAppendableFile(CurrentFileName(dbname_), &tmp); + delete tmp; + if (s.IsNotSupportedError()) { + return false; + } else { + return true; + } + } + + void Close() { + delete db_; + db_ = NULL; + } + + void Open(Options* options = NULL) { + Close(); + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts.reuse_logs = true; // TODO(sanjay): test both ways + opts.create_if_missing = true; + } + if (opts.env == NULL) { + opts.env = env_; + } + ASSERT_OK(DB::Open(opts, dbname_, &db_)); + ASSERT_EQ(1, NumLogs()); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string result; + Status s = db_->Get(ReadOptions(), k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + std::string ManifestFileName() { + std::string current; + ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t)); + size_t len = current.size(); + if (len > 0 && current[len-1] == '\n') { + current.resize(len - 1); + } + return dbname_ + "/" + current; + } + + std::string LogName(uint64_t number) { + return LogFileName(dbname_, number); + } + + size_t DeleteLogFiles() { + std::vector logs = GetFiles(kLogFile); + for (size_t i = 0; i < logs.size(); i++) { + ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]); + } + return logs.size(); + } + + uint64_t FirstLogFile() { + return GetFiles(kLogFile)[0]; + } + + std::vector GetFiles(FileType t) { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + std::vector result; + for (size_t i = 0; i < filenames.size(); i++) { + uint64_t number; + FileType type; + if (ParseFileName(filenames[i], &number, &type) && type == t) { + result.push_back(number); + } + } + return result; + } + + int NumLogs() { + return GetFiles(kLogFile).size(); + } + + int NumTables() { + return GetFiles(kTableFile).size(); + } + + uint64_t FileSize(const std::string& fname) { + uint64_t result; + ASSERT_OK(env_->GetFileSize(fname, &result)) << fname; + return result; + } + + void CompactMemTable() { + dbfull()->TEST_CompactMemTable(); + } + + // Directly construct a log file that sets key to val. + void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) { + std::string fname = LogFileName(dbname_, lognum); + WritableFile* file; + ASSERT_OK(env_->NewWritableFile(fname, &file)); + log::Writer writer(file); + WriteBatch batch; + batch.Put(key, val); + WriteBatchInternal::SetSequence(&batch, seq); + ASSERT_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch))); + ASSERT_OK(file->Flush()); + delete file; + } + + private: + std::string dbname_; + Env* env_; + DB* db_; +}; + +TEST(RecoveryTest, ManifestReused) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, LargeManifestCompacted) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + + // Pad with zeroes to make manifest file very big. + { + uint64_t len = FileSize(old_manifest); + WritableFile* file; + ASSERT_OK(env()->NewAppendableFile(old_manifest, &file)); + std::string zeroes(3*1048576 - static_cast(len), 0); + ASSERT_OK(file->Append(zeroes)); + ASSERT_OK(file->Flush()); + delete file; + } + + Open(); + std::string new_manifest = ManifestFileName(); + ASSERT_NE(old_manifest, new_manifest); + ASSERT_GT(10000, FileSize(new_manifest)); + ASSERT_EQ("bar", Get("foo")); + + Open(); + ASSERT_EQ(new_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, NoLogFiles) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ(1, DeleteLogFiles()); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); +} + +TEST(RecoveryTest, LogFileReuse) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + for (int i = 0; i < 2; i++) { + ASSERT_OK(Put("foo", "bar")); + if (i == 0) { + // Compact to ensure current log is empty + CompactMemTable(); + } + Close(); + ASSERT_EQ(1, NumLogs()); + uint64_t number = FirstLogFile(); + if (i == 0) { + ASSERT_EQ(0, FileSize(LogName(number))); + } else { + ASSERT_LT(0, FileSize(LogName(number))); + } + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(RecoveryTest, MultipleMemTables) { + // Make a large log. + const int kNum = 1000; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_OK(Put(buf, buf)); + } + ASSERT_EQ(0, NumTables()); + Close(); + ASSERT_EQ(0, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t old_log_file = FirstLogFile(); + + // Force creation of multiple memtables by reducing the write buffer size. + Options opt; + opt.reuse_logs = true; + opt.write_buffer_size = (kNum*100) / 2; + Open(&opt); + ASSERT_LE(2, NumTables()); + ASSERT_EQ(1, NumLogs()); + ASSERT_NE(old_log_file, FirstLogFile()) << "must not reuse log"; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_EQ(buf, Get(buf)); + } +} + +TEST(RecoveryTest, MultipleLogFiles) { + ASSERT_OK(Put("foo", "bar")); + Close(); + ASSERT_EQ(1, NumLogs()); + + // Make a bunch of uncompacted log files. + uint64_t old_log = FirstLogFile(); + MakeLogFile(old_log+1, 1000, "hello", "world"); + MakeLogFile(old_log+2, 1001, "hi", "there"); + MakeLogFile(old_log+3, 1002, "foo", "bar2"); + + // Recover and check that all log files were processed. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t new_log = FirstLogFile(); + ASSERT_LE(old_log+3, new_log); + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Test that previous recovery produced recoverable state. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Check that introducing an older log file does not cause it to be re-read. + Close(); + MakeLogFile(old_log+1, 2000, "hello", "stale write"); + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/db/version_set.cc b/db/version_set.cc index aa83df55e4..a07e7ec7bd 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -893,7 +893,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { return s; } -Status VersionSet::Recover() { +Status VersionSet::Recover(bool *save_manifest) { struct LogReporter : public log::Reader::Reporter { Status* status; virtual void Corruption(size_t bytes, const Status& s) { @@ -1003,11 +1003,46 @@ Status VersionSet::Recover() { last_sequence_ = last_sequence; log_number_ = log_number; prev_log_number_ = prev_log_number; + + // See if we can reuse the existing MANIFEST file. + if (ReuseManifest(dscname, current)) { + // No need to save new manifest + } else { + *save_manifest = true; + } } return s; } +bool VersionSet::ReuseManifest(const std::string& dscname, + const std::string& dscbase) { + FileType manifest_type; + uint64_t manifest_number; + uint64_t manifest_size; + if (!ParseFileName(dscbase, &manifest_number, &manifest_type) || + manifest_type != kDescriptorFile || + !env_->GetFileSize(dscname, &manifest_size).ok() || + // Make new compacted MANIFEST if old one is too big + manifest_size >= kTargetFileSize) { + return false; + } + + assert(descriptor_file_ == NULL); + assert(descriptor_log_ == NULL); + Status r = env_->NewAppendableFile(dscname, &descriptor_file_); + if (!r.ok()) { + Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str()); + assert(descriptor_file_ == NULL); + return false; + } + + Log(options_->info_log, "Reusing MANIFEST %s\n", dscname.c_str()); + descriptor_log_ = new log::Writer(descriptor_file_, manifest_size); + manifest_file_number_ = manifest_number; + return true; +} + void VersionSet::MarkFileNumberUsed(uint64_t number) { if (next_file_number_ <= number) { next_file_number_ = number + 1; diff --git a/db/version_set.h b/db/version_set.h index 8dc14b8e01..1dec745673 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -179,7 +179,7 @@ class VersionSet { EXCLUSIVE_LOCKS_REQUIRED(mu); // Recover the last saved descriptor from persistent storage. - Status Recover(); + Status Recover(bool *save_manifest); // Return the current version. Version* current() const { return current_; } @@ -274,6 +274,8 @@ class VersionSet { friend class Compaction; friend class Version; + bool ReuseManifest(const std::string& dscname, const std::string& dscbase); + void Finalize(Version* v); void GetRange(const std::vector& inputs, diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index 43ef2e0729..9a98884daf 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -277,6 +277,19 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + FileState** sptr = &file_map_[fname]; + FileState* file = *sptr; + if (file == NULL) { + file = new FileState(); + file->Ref(); + } + *result = new WritableFileImpl(file); + return Status::OK(); + } + virtual bool FileExists(const std::string& fname) { MutexLock lock(&mutex_); return file_map_.find(fname) != file_map_.end(); diff --git a/helpers/memenv/memenv_test.cc b/helpers/memenv/memenv_test.cc index a44310fed8..5cff77613f 100644 --- a/helpers/memenv/memenv_test.cc +++ b/helpers/memenv/memenv_test.cc @@ -40,6 +40,8 @@ TEST(MemEnvTest, Basics) { // Create a file. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); delete writable_file; // Check that the file exists. @@ -55,9 +57,16 @@ TEST(MemEnvTest, Basics) { ASSERT_OK(writable_file->Append("abc")); delete writable_file; - // Check for expected size. + // Check that append works. + ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file)); ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); ASSERT_EQ(3, file_size); + ASSERT_OK(writable_file->Append("hello")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(8, file_size); // Check that renaming works. ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); @@ -65,7 +74,7 @@ TEST(MemEnvTest, Basics) { ASSERT_TRUE(!env_->FileExists("/dir/f")); ASSERT_TRUE(env_->FileExists("/dir/g")); ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); - ASSERT_EQ(3, file_size); + ASSERT_EQ(8, file_size); // Check that opening non-existent file fails. SequentialFile* seq_file; diff --git a/include/leveldb/env.h b/include/leveldb/env.h index f709514da6..99b6c21414 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -69,6 +69,21 @@ class Env { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) = 0; + // Create an object that either appends to an existing file, or + // writes to a new file (if the file does not exist to begin with). + // On success, stores a pointer to the new file in *result and + // returns OK. On failure stores NULL in *result and returns + // non-OK. + // + // The returned file will only be accessed by one thread at a time. + // + // May return an IsNotSupportedError error if this Env does + // not allow appending to an existing file. Users of Env (including + // the leveldb implementation) must be prepared to deal with + // an Env that does not support appending. + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + // Returns true iff the named file exists. virtual bool FileExists(const std::string& fname) = 0; @@ -289,6 +304,9 @@ class EnvWrapper : public Env { Status NewWritableFile(const std::string& f, WritableFile** r) { return target_->NewWritableFile(f, r); } + Status NewAppendableFile(const std::string& f, WritableFile** r) { + return target_->NewAppendableFile(f, r); + } bool FileExists(const std::string& f) { return target_->FileExists(f); } Status GetChildren(const std::string& dir, std::vector* r) { return target_->GetChildren(dir, r); diff --git a/include/leveldb/options.h b/include/leveldb/options.h index 7c9b973454..83a1ef39a4 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -128,6 +128,12 @@ struct Options { // efficiently detect that and will switch to uncompressed mode. CompressionType compression; + // EXPERIMENTAL: If true, append to existing MANIFEST and log files + // when a database is opened. This can significantly speed up open. + // + // Default: currently false, but may become true later. + bool reuse_logs; + // If non-NULL, use the specified filter policy to reduce disk reads. // Many applications will benefit from passing the result of // NewBloomFilterPolicy() here. diff --git a/include/leveldb/status.h b/include/leveldb/status.h index 11dbd4b47e..d2bdc2286f 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -60,6 +60,9 @@ class Status { // Returns true iff the status indicates an IOError. bool IsIOError() const { return code() == kIOError; } + // Returns true iff the status indicates a NotSupportedError. + bool IsNotSupportedError() const { return code() == kNotSupported; } + // Return a string representation of this status suitable for printing. // Returns the string "OK" for success. std::string ToString() const; diff --git a/util/env.cc b/util/env.cc index c2600e964a..c58a0821ef 100644 --- a/util/env.cc +++ b/util/env.cc @@ -9,6 +9,10 @@ namespace leveldb { Env::~Env() { } +Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { + return Status::NotSupported("NewAppendableFile", fname); +} + SequentialFile::~SequentialFile() { } diff --git a/util/env_posix.cc b/util/env_posix.cc index ad56131ae1..a17dec278f 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -350,6 +350,19 @@ class PosixEnv : public Env { return s; } + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + Status s; + FILE* f = fopen(fname.c_str(), "a"); + if (f == NULL) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixWritableFile(fname, f); + } + return s; + } + virtual bool FileExists(const std::string& fname) { return access(fname.c_str(), F_OK) == 0; } diff --git a/util/options.cc b/util/options.cc index 76af5b9302..8b618fb1ae 100644 --- a/util/options.cc +++ b/util/options.cc @@ -22,8 +22,8 @@ Options::Options() block_size(4096), block_restart_interval(16), compression(kSnappyCompression), + reuse_logs(false), filter_policy(NULL) { } - } // namespace leveldb diff --git a/util/testutil.h b/util/testutil.h index adad3fc1ea..d7e4583702 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -45,6 +45,16 @@ class ErrorEnv : public EnvWrapper { } return target()->NewWritableFile(fname, result); } + + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewAppendableFile(fname, result); + } }; } // namespace test From 65190ac48b614b4bd2f3d5d173cf8fdbb2c0fc1a Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Wed, 17 Jun 2015 10:34:42 -0700 Subject: [PATCH 008/174] Will not reuse manifest if reuse_logs options is false. Prior implementation would always try to reuse the manifest, even if reuse_logs was false (the default). This was missed because the stock Env::NewAppendableFile implementation returns false forcing the creation of a new log. --- db/version_set.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/version_set.cc b/db/version_set.cc index a07e7ec7bd..a5e0f77a6a 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -1017,6 +1017,9 @@ Status VersionSet::Recover(bool *save_manifest) { bool VersionSet::ReuseManifest(const std::string& dscname, const std::string& dscbase) { + if (!options_->reuse_logs) { + return false; + } FileType manifest_type; uint64_t manifest_number; uint64_t manifest_size; From edf2939c0d520ff1a0882af7272fcc691412cfbc Mon Sep 17 00:00:00 2001 From: Venilton FalvoJr Date: Mon, 23 Nov 2015 16:24:16 -0200 Subject: [PATCH 009/174] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 480affb5ca..20241e879d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Multiple changes can be made in one atomic batch. * Users can create a transient snapshot to get a consistent view of data. * Forward and backward iteration is supported over the data. - * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). + * Data is automatically compressed using the [Snappy compression library](http://google.github.io/snappy/). * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code. From b9afa1f2e79e928e08f11e907e8688b6d89cfd2f Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Tue, 11 Aug 2015 15:27:47 -0700 Subject: [PATCH 010/174] include -> Fixes reported public issue #280. --- doc/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.html b/doc/index.html index 3ed0ed9d9e..2155192581 100644 --- a/doc/index.html +++ b/doc/index.html @@ -22,7 +22,7 @@

Opening A Database

creating it if necessary:

-  #include <assert>
+  #include <cassert>
   #include "leveldb/db.h"
 
   leveldb::DB* db;

From ce45404bba80d6f85057b596cd716d6c4759dcad Mon Sep 17 00:00:00 2001
From: Mike Wiacek 
Date: Tue, 11 Aug 2015 15:36:45 -0700
Subject: [PATCH 011/174] Suppress error reporting after seeking but before a
 valid First or Full record is encountered.

Fix a spelling mistake.
---
 db/log_reader.cc | 14 +++++++++++++-
 db/log_reader.h  |  5 +++++
 db/log_test.cc   | 33 +++++++++++++++++++++++++++------
 3 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/db/log_reader.cc b/db/log_reader.cc
index e44b66c85b..6d4a5b2e39 100644
--- a/db/log_reader.cc
+++ b/db/log_reader.cc
@@ -25,7 +25,8 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
       eof_(false),
       last_record_offset_(0),
       end_of_buffer_offset_(0),
-      initial_offset_(initial_offset) {
+      initial_offset_(initial_offset),
+      resyncing_(initial_offset > 0) {
 }
 
 Reader::~Reader() {
@@ -74,6 +75,17 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
   while (true) {
     uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
     const unsigned int record_type = ReadPhysicalRecord(&fragment);
+    if (resyncing_) {
+      if (record_type == kMiddleType) {
+        continue;
+      } else if (record_type == kLastType) {
+        resyncing_ = false;
+        continue;
+      } else {
+        resyncing_ = false;
+      }
+    }
+
     switch (record_type) {
       case kFullType:
         if (in_fragmented_record) {
diff --git a/db/log_reader.h b/db/log_reader.h
index 6aff791716..8389d61f8f 100644
--- a/db/log_reader.h
+++ b/db/log_reader.h
@@ -73,6 +73,11 @@ class Reader {
   // Offset at which to start looking for the first record to return
   uint64_t const initial_offset_;
 
+  // True if we are resynchronizing after a seek (initial_offset_ > 0). In
+  // particular, a run of kMiddleType and kLastType records can be silently
+  // skipped in this mode
+  bool resyncing_;
+
   // Extend record types with the following special values
   enum {
     kEof = kMaxRecordType + 1,
diff --git a/db/log_test.cc b/db/log_test.cc
index 11e4a47e62..7b08c261f5 100644
--- a/db/log_test.cc
+++ b/db/log_test.cc
@@ -79,7 +79,7 @@ class LogTest {
     virtual Status Skip(uint64_t n) {
       if (n > contents_.size()) {
         contents_.clear();
-        return Status::NotFound("in-memory file skipepd past end");
+        return Status::NotFound("in-memory file skipped past end");
       }
 
       contents_.remove_prefix(n);
@@ -105,7 +105,7 @@ class LogTest {
   ReportCollector report_;
   bool reading_;
   Writer* writer_;
-  Reader reader_;
+  Reader* reader_;
 
   // Record metadata for testing initial offset functionality
   static size_t initial_offset_record_sizes_[];
@@ -114,12 +114,13 @@ class LogTest {
  public:
   LogTest() : reading_(false),
               writer_(new Writer(&dest_)),
-              reader_(&source_, &report_, true/*checksum*/,
-                      0/*initial_offset*/) {
+              reader_(new Reader(&source_, &report_, true/*checksum*/,
+                      0/*initial_offset*/)) {
   }
 
   ~LogTest() {
     delete writer_;
+    delete reader_;
   }
 
   void ReopenForAppend() {
@@ -143,7 +144,7 @@ class LogTest {
     }
     std::string scratch;
     Slice record;
-    if (reader_.ReadRecord(&record, &scratch)) {
+    if (reader_->ReadRecord(&record, &scratch)) {
       return record.ToString();
     } else {
       return "EOF";
@@ -198,6 +199,11 @@ class LogTest {
     }
   }
 
+  void StartReadingAt(uint64_t initial_offset) {
+    delete reader_;
+    reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset);
+  }
+
   void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) {
     WriteInitialOffsetLog();
     reading_ = true;
@@ -227,7 +233,6 @@ class LogTest {
     ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
     delete offset_reader;
   }
-
 };
 
 size_t LogTest::initial_offset_record_sizes_[] =
@@ -463,6 +468,22 @@ TEST(LogTest, PartialLastIsIgnored) {
   ASSERT_EQ(0, DroppedBytes());
 }
 
+TEST(LogTest, SkipIntoMultiRecord) {
+  // Consider a fragmented record:
+  //    first(R1), middle(R1), last(R1), first(R2)
+  // If initial_offset points to a record after first(R1) but before first(R2)
+  // incomplete fragment errors are not actual errors, and must be suppressed
+  // until a new first or full record is encountered.
+  Write(BigString("foo", 3*kBlockSize));
+  Write("correct");
+  StartReadingAt(kBlockSize);
+
+  ASSERT_EQ("correct", Read());
+  ASSERT_EQ("", ReportMessage());
+  ASSERT_EQ(0, DroppedBytes());
+  ASSERT_EQ("EOF", Read());
+}
+
 TEST(LogTest, ErrorJoinsRecords) {
   // Consider two fragmented records:
   //    first(R1) last(R1) first(R2) last(R2)

From 5208e7952d691e256a4ffe29888154068e8745b8 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Mon, 29 Jun 2015 14:40:59 -0700
Subject: [PATCH 012/174] Added leveldb::Status::IsInvalidArgument() method.

All other Status::Code enum values have an Is**() method with the one
exception of InvalidArgument.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=97166441
---
 include/leveldb/status.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/leveldb/status.h b/include/leveldb/status.h
index d2bdc2286f..d9575f9753 100644
--- a/include/leveldb/status.h
+++ b/include/leveldb/status.h
@@ -63,6 +63,9 @@ class Status {
   // Returns true iff the status indicates a NotSupportedError.
   bool IsNotSupportedError() const { return code() == kNotSupported; }
 
+  // Returns true iff the status indicates an InvalidArgument.
+  bool IsInvalidArgument() const { return code() == kInvalidArgument; }
+
   // Return a string representation of this status suitable for printing.
   // Returns the string "OK" for success.
   std::string ToString() const;

From 50e77a8263b19033b99ba49cdc45995595f1acf9 Mon Sep 17 00:00:00 2001
From: pkasting 
Date: Wed, 19 Aug 2015 16:40:51 -0700
Subject: [PATCH 013/174] Fix size_t/int comparison/conversion issues in
 leveldb.

The create function took |num_keys| as an int, but callers and implementers wanted it to function as a size_t (e.g. passing std::vector::size() in, passing it to vector constructors as a size arg, indexing containers by it, etc.).  This resulted in implicit conversions between the two types as well as warnings (found with Chromium's external copy of these sources, built with MSVC) about signed vs. unsigned comparisons.

The leveldb sources were already widely using size_t elsewhere, e.g. for key and filter lengths, so using size_t here is not inconsistent with the existing code.  However, it does change the public C API.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=101074871
---
 table/filter_block.cc | 4 ++--
 util/bloom.cc         | 2 +-
 util/bloom_test.cc    | 3 ++-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/table/filter_block.cc b/table/filter_block.cc
index 203e15c8bc..4e78b954f8 100644
--- a/table/filter_block.cc
+++ b/table/filter_block.cc
@@ -68,7 +68,7 @@ void FilterBlockBuilder::GenerateFilter() {
 
   // Generate filter for current set of keys and append to result_.
   filter_offsets_.push_back(result_.size());
-  policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_);
+  policy_->CreateFilter(&tmp_keys_[0], static_cast(num_keys), &result_);
 
   tmp_keys_.clear();
   keys_.clear();
@@ -97,7 +97,7 @@ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
   if (index < num_) {
     uint32_t start = DecodeFixed32(offset_ + index*4);
     uint32_t limit = DecodeFixed32(offset_ + index*4 + 4);
-    if (start <= limit && limit <= (offset_ - data_)) {
+    if (start <= limit && limit <= static_cast(offset_ - data_)) {
       Slice filter = Slice(data_ + start, limit - start);
       return policy_->KeyMayMatch(key, filter);
     } else if (start == limit) {
diff --git a/util/bloom.cc b/util/bloom.cc
index a27a2ace28..bf3e4ca6e9 100644
--- a/util/bloom.cc
+++ b/util/bloom.cc
@@ -47,7 +47,7 @@ class BloomFilterPolicy : public FilterPolicy {
     dst->resize(init_size + bytes, 0);
     dst->push_back(static_cast(k_));  // Remember # of probes in filter
     char* array = &(*dst)[init_size];
-    for (size_t i = 0; i < n; i++) {
+    for (int i = 0; i < n; i++) {
       // Use double-hashing to generate a sequence of hash values.
       // See analysis in [Kirsch,Mitzenmacher 2006].
       uint32_t h = BloomHash(keys[i]);
diff --git a/util/bloom_test.cc b/util/bloom_test.cc
index 77fb1b3159..1b87a2be3f 100644
--- a/util/bloom_test.cc
+++ b/util/bloom_test.cc
@@ -46,7 +46,8 @@ class BloomTest {
       key_slices.push_back(Slice(keys_[i]));
     }
     filter_.clear();
-    policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_);
+    policy_->CreateFilter(&key_slices[0], static_cast(key_slices.size()),
+                          &filter_);
     keys_.clear();
     if (kVerbose >= 2) DumpFilter();
   }

From 359b6bcec28944972d0be503a78205b5cc574a9c Mon Sep 17 00:00:00 2001
From: tzik 
Date: Sun, 23 Aug 2015 20:35:01 -0700
Subject: [PATCH 014/174] Add leveldb::Cache::Prune

Prune() drops on-memory read cache of the database, so that the client can
relief its memory shortage.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=101335710
---
 include/leveldb/cache.h |  7 +++++++
 util/cache.cc           | 19 +++++++++++++++++++
 util/cache_test.cc      | 13 +++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h
index 1a201e5e0a..5f86cd03f3 100644
--- a/include/leveldb/cache.h
+++ b/include/leveldb/cache.h
@@ -81,6 +81,13 @@ class Cache {
   // its cache keys.
   virtual uint64_t NewId() = 0;
 
+  // Remove all cache entries that are not actively in use.  Memory-constrained
+  // applications may wish to call this method to reduce memory usage.
+  // Default implementation of Prune() does nothing.  Subclasses are strongly
+  // encouraged to override the default implementation.  A future release of
+  // leveldb may change Prune() to a pure abstract method.
+  virtual void Prune() {}
+
  private:
   void LRU_Remove(Handle* e);
   void LRU_Append(Handle* e);
diff --git a/util/cache.cc b/util/cache.cc
index 8b197bc02a..7f5fc07e42 100644
--- a/util/cache.cc
+++ b/util/cache.cc
@@ -147,6 +147,7 @@ class LRUCache {
   Cache::Handle* Lookup(const Slice& key, uint32_t hash);
   void Release(Cache::Handle* handle);
   void Erase(const Slice& key, uint32_t hash);
+  void Prune();
 
  private:
   void LRU_Remove(LRUHandle* e);
@@ -264,6 +265,19 @@ void LRUCache::Erase(const Slice& key, uint32_t hash) {
   }
 }
 
+void LRUCache::Prune() {
+  MutexLock l(&mutex_);
+  for (LRUHandle* e = lru_.next; e != &lru_; ) {
+    LRUHandle* next = e->next;
+    if (e->refs == 1) {
+      table_.Remove(e->key(), e->hash);
+      LRU_Remove(e);
+      Unref(e);
+    }
+    e = next;
+  }
+}
+
 static const int kNumShardBits = 4;
 static const int kNumShards = 1 << kNumShardBits;
 
@@ -314,6 +328,11 @@ class ShardedLRUCache : public Cache {
     MutexLock l(&id_mutex_);
     return ++(last_id_);
   }
+  virtual void Prune() {
+    for (int s = 0; s < kNumShards; s++) {
+      shard_[s].Prune();
+    }
+  }
 };
 
 }  // end anonymous namespace
diff --git a/util/cache_test.cc b/util/cache_test.cc
index 43716715a8..90af5fe354 100644
--- a/util/cache_test.cc
+++ b/util/cache_test.cc
@@ -179,6 +179,19 @@ TEST(CacheTest, NewId) {
   ASSERT_NE(a, b);
 }
 
+TEST(CacheTest, Prune) {
+  Insert(1, 100);
+  Insert(2, 200);
+
+  Cache::Handle* handle = cache_->Lookup(EncodeKey(1));
+  ASSERT_TRUE(handle);
+  cache_->Prune();
+  cache_->Release(handle);
+
+  ASSERT_EQ(100, Lookup(1));
+  ASSERT_EQ(-1, Lookup(2));
+}
+
 }  // namespace leveldb
 
 int main(int argc, char** argv) {

From 528c2bc6ad03875865be8712aacdf319f7002d44 Mon Sep 17 00:00:00 2001
From: ssid 
Date: Tue, 29 Sep 2015 11:52:21 -0700
Subject: [PATCH 015/174] Add "approximate-memory-usage" property to
 leveldb::DB::GetProperty

The approximate RAM usage of the database is calculated from the memory
allocated for write buffers and the block cache. This is to give an
estimate of memory usage to leveldb clients.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=104222307
---
 db/db_impl.cc           | 13 +++++++++++++
 db/db_test.cc           | 11 +++++++++++
 include/leveldb/cache.h |  4 ++++
 include/leveldb/db.h    |  2 ++
 util/cache.cc           | 13 ++++++++++++-
 5 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/db/db_impl.cc b/db/db_impl.cc
index 3fd21b047f..dc645d041e 100644
--- a/db/db_impl.cc
+++ b/db/db_impl.cc
@@ -1425,6 +1425,19 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) {
   } else if (in == "sstables") {
     *value = versions_->current()->DebugString();
     return true;
+  } else if (in == "approximate-memory-usage") {
+    size_t total_usage = options_.block_cache->TotalCharge();
+    if (mem_) {
+      total_usage += mem_->ApproximateMemoryUsage();
+    }
+    if (imm_) {
+      total_usage += imm_->ApproximateMemoryUsage();
+    }
+    char buf[50];
+    snprintf(buf, sizeof(buf), "%llu",
+             static_cast(total_usage));
+    value->append(buf);
+    return true;
   }
 
   return false;
diff --git a/db/db_test.cc b/db/db_test.cc
index 74ba8b05e8..a0b08bc19c 100644
--- a/db/db_test.cc
+++ b/db/db_test.cc
@@ -563,6 +563,17 @@ TEST(DBTest, GetFromVersions) {
   } while (ChangeOptions());
 }
 
+TEST(DBTest, GetMemUsage) {
+  do {
+    ASSERT_OK(Put("foo", "v1"));
+    std::string val;
+    ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val));
+    int mem_usage = atoi(val.c_str());
+    ASSERT_GT(mem_usage, 0);
+    ASSERT_LT(mem_usage, 5*1024*1024);
+  } while (ChangeOptions());
+}
+
 TEST(DBTest, GetSnapshot) {
   do {
     // Try with both a short key and a long key
diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h
index 5f86cd03f3..6819d5bc49 100644
--- a/include/leveldb/cache.h
+++ b/include/leveldb/cache.h
@@ -88,6 +88,10 @@ class Cache {
   // leveldb may change Prune() to a pure abstract method.
   virtual void Prune() {}
 
+  // Return an estimate of the combined charges of all elements stored in the
+  // cache.
+  virtual size_t TotalCharge() const = 0;
+
  private:
   void LRU_Remove(Handle* e);
   void LRU_Append(Handle* e);
diff --git a/include/leveldb/db.h b/include/leveldb/db.h
index 4c169bf22e..53c7068765 100644
--- a/include/leveldb/db.h
+++ b/include/leveldb/db.h
@@ -115,6 +115,8 @@ class DB {
   //     about the internal operation of the DB.
   //  "leveldb.sstables" - returns a multi-line string that describes all
   //     of the sstables that make up the db contents.
+  //  "leveldb.approximate-memory-usage" - returns the approximate number of
+  //     bytes of memory in use by the DB.
   virtual bool GetProperty(const Slice& property, std::string* value) = 0;
 
   // For each i in [0,n-1], store in "sizes[i]", the approximate
diff --git a/util/cache.cc b/util/cache.cc
index 7f5fc07e42..0881bce9c5 100644
--- a/util/cache.cc
+++ b/util/cache.cc
@@ -148,6 +148,10 @@ class LRUCache {
   void Release(Cache::Handle* handle);
   void Erase(const Slice& key, uint32_t hash);
   void Prune();
+  size_t TotalCharge() const {
+    MutexLock l(&mutex_);
+    return usage_;
+  }
 
  private:
   void LRU_Remove(LRUHandle* e);
@@ -158,7 +162,7 @@ class LRUCache {
   size_t capacity_;
 
   // mutex_ protects the following state.
-  port::Mutex mutex_;
+  mutable port::Mutex mutex_;
   size_t usage_;
 
   // Dummy head of LRU list.
@@ -333,6 +337,13 @@ class ShardedLRUCache : public Cache {
       shard_[s].Prune();
     }
   }
+  virtual size_t TotalCharge() const {
+    size_t total = 0;
+    for (int s = 0; s < kNumShards; s++) {
+      total += shard_[s].TotalCharge();
+    }
+    return total;
+  }
 };
 
 }  // end anonymous namespace

From 889de31a5a8bc62d1c93cf7c0a2dcbfb0a2c979b Mon Sep 17 00:00:00 2001
From: ndmatthews 
Date: Wed, 7 Oct 2015 07:21:59 -0700
Subject: [PATCH 016/174] Let LevelDB use xcrun to determine Xcode.app path
 instead of using a hardcoded path.

This allows build agents to select from multiple Xcode installations.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=104859097
---
 Makefile | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile
index b1a1702c5c..d991ecc537 100644
--- a/Makefile
+++ b/Makefile
@@ -206,24 +206,22 @@ memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHAR
 ifeq ($(PLATFORM), IOS)
 # For iOS, create universal object files to be used on both the simulator and
 # a device.
-PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
-SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
-DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
-IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
+SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path)
+DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path)
 IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64
 
 .cc.o:
 	mkdir -p ios-x86/$(dir $@)
-	xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@
+	xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 -c $< -o ios-x86/$@
 	mkdir -p ios-arm/$(dir $@)
-	xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@
+	xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot "$(DEVICESDK)" $(IOSARCH) -c $< -o ios-arm/$@
 	xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
 
 .c.o:
 	mkdir -p ios-x86/$(dir $@)
-	xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@
+	xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 -c $< -o ios-x86/$@
 	mkdir -p ios-arm/$(dir $@)
-	xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@
+	xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot "$(DEVICESDK)" $(IOSARCH) -c $< -o ios-arm/$@
 	xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
 
 else

From f8d205cf89819bfa0010e19ec091ad8b1d95f204 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Mon, 12 Oct 2015 13:47:35 -0700
Subject: [PATCH 017/174] Including atomic_pointer.h in port_posix

A recent CL (104348226) created the port_posix library, but omitted: port/atomic_pointer.h.

And when:

    [] test third_party/leveldb:all

was run this error was reported:

    //third_party/leveldb:port_posix does not depend on a
    module exporting 'third_party/leveldb/port/atomic_pointer.h'
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=105243399
---
 port/port_posix.cc | 1 -
 1 file changed, 1 deletion(-)

diff --git a/port/port_posix.cc b/port/port_posix.cc
index 5ba127a5b9..30e8007ae3 100644
--- a/port/port_posix.cc
+++ b/port/port_posix.cc
@@ -7,7 +7,6 @@
 #include 
 #include 
 #include 
-#include "util/logging.h"
 
 namespace leveldb {
 namespace port {

From 3c9ff3c691bc4ccb6098ed80bd71e33a36892f5a Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Wed, 22 Jul 2015 10:20:21 -0700
Subject: [PATCH 018/174] Only compiling TrimSpace on linux.

Incorporated change by zmodem at https://github.com/google/leveldb/pull/310
to fix issue #310.

This change will only build TrimSace on linux to avoid unused function
warning/error.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=105323419
---
 db/db_bench.cc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/db/db_bench.cc b/db/db_bench.cc
index 691fd284dc..7a0f5e08cd 100644
--- a/db/db_bench.cc
+++ b/db/db_bench.cc
@@ -142,6 +142,7 @@ class RandomGenerator {
   }
 };
 
+#if defined(__linux)
 static Slice TrimSpace(Slice s) {
   size_t start = 0;
   while (start < s.size() && isspace(s[start])) {
@@ -153,6 +154,7 @@ static Slice TrimSpace(Slice s) {
   }
   return Slice(s.data() + start, limit - start);
 }
+#endif
 
 static void AppendWithSpace(std::string* str, Slice msg) {
   if (msg.empty()) return;

From 706b7f8d43b0aecdc75c5ee49d3e4ef5f27b9faf Mon Sep 17 00:00:00 2001
From: ssid 
Date: Wed, 11 Nov 2015 03:54:18 -0800
Subject: [PATCH 019/174] Resolve race when getting approximate-memory-usage
 property

The write operations in the table happens without holding the mutex
lock, but concurrent writes are avoided using "writers_" queue.
The Arena::MemoryUsage could access the blocks when write happens.
So, the memory usage is cached in atomic word and can be loaded
from any thread safely.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=107573379
---
 db/memtable.h |  5 +----
 util/arena.cc |  6 +++---
 util/arena.h  | 10 +++++-----
 3 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/db/memtable.h b/db/memtable.h
index 92e90bb099..9f41567cde 100644
--- a/db/memtable.h
+++ b/db/memtable.h
@@ -36,10 +36,7 @@ class MemTable {
   }
 
   // Returns an estimate of the number of bytes of data in use by this
-  // data structure.
-  //
-  // REQUIRES: external synchronization to prevent simultaneous
-  // operations on the same MemTable.
+  // data structure. It is safe to call when MemTable is being modified.
   size_t ApproximateMemoryUsage();
 
   // Return an iterator that yields the contents of the memtable.
diff --git a/util/arena.cc b/util/arena.cc
index 9367f71492..74078213ee 100644
--- a/util/arena.cc
+++ b/util/arena.cc
@@ -9,8 +9,7 @@ namespace leveldb {
 
 static const int kBlockSize = 4096;
 
-Arena::Arena() {
-  blocks_memory_ = 0;
+Arena::Arena() : memory_usage_(0) {
   alloc_ptr_ = NULL;  // First allocation will allocate a block
   alloc_bytes_remaining_ = 0;
 }
@@ -60,8 +59,9 @@ char* Arena::AllocateAligned(size_t bytes) {
 
 char* Arena::AllocateNewBlock(size_t block_bytes) {
   char* result = new char[block_bytes];
-  blocks_memory_ += block_bytes;
   blocks_.push_back(result);
+  memory_usage_.NoBarrier_Store(
+      reinterpret_cast(MemoryUsage() + block_bytes + sizeof(char*)));
   return result;
 }
 
diff --git a/util/arena.h b/util/arena.h
index 73bbf1cb9b..48bab33741 100644
--- a/util/arena.h
+++ b/util/arena.h
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include "port/port.h"
 
 namespace leveldb {
 
@@ -24,10 +25,9 @@ class Arena {
   char* AllocateAligned(size_t bytes);
 
   // Returns an estimate of the total memory usage of data allocated
-  // by the arena (including space allocated but not yet used for user
-  // allocations).
+  // by the arena.
   size_t MemoryUsage() const {
-    return blocks_memory_ + blocks_.capacity() * sizeof(char*);
+    return reinterpret_cast(memory_usage_.NoBarrier_Load());
   }
 
  private:
@@ -41,8 +41,8 @@ class Arena {
   // Array of new[] allocated memory blocks
   std::vector blocks_;
 
-  // Bytes of memory in blocks allocated so far
-  size_t blocks_memory_;
+  // Total memory usage of the arena.
+  port::AtomicPointer memory_usage_;
 
   // No copying allowed
   Arena(const Arena&);

From 4753c9b617039d6d3a68f16de64ae9eca49650c0 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Mon, 4 Jan 2016 13:14:13 -0800
Subject: [PATCH 020/174] Added a contributors section to README.md

In preparation for accepting GitHub pull requests this new README
section outlines the general criteria that the leveldb project owners
will use when accepting external (and internal) project contributions.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=111349899
---
 README.md | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/README.md b/README.md
index 36cec633d9..a2f18de2c3 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,37 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
   * Only a single process (possibly multi-threaded) can access a particular database at a time.
   * There is no client-server support builtin to the library.  An application that needs such support will have to wrap their own server around the library.
 
+# Contributing to the leveldb Project
+The leveldb project welcomes contributions. leveldb's primary goal is to be
+a reliable and fast key/value store. Changes that are in line with the
+features/limitations outlined above, and meet the requirements below,
+will be considered.
+
+Contribution requirements:
+
+1. **POSIX only**. We _generally_ will only accept changes that are both
+   compiled, and tested on a POSIX platform - usually Linux. Very small
+   changes will sometimes be accepted, but consider that more of an
+   exception than the rule.
+
+2. **Stable API**. We strive very hard to maintain a stable API. Changes that
+   require changes for projects using leveldb _might_ be rejected without
+   sufficient benefit to the project.
+
+3. **Tests**: All changes must be accompanied by a new (or changed) test, or
+   a sufficient explanation as to why a new (or changed) test is not required.
+
+## Submitting a Pull Request
+Before any pull request will be accepted the author must first sign a
+Contributor License Agreement (CLA) at https://cla.developers.google.com/.
+
+In order to keep the commit timeline linear
+[squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits)
+your changes down to a single commit and [rebase](https://git-scm.com/docs/git-rebase)
+on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed
+with the internal repository at Google. More information at GitHub's
+[About Git rebase](https://help.github.com/articles/about-git-rebase/) page.
+
 # Performance
 
 Here is a performance report (with explanations) from the run of the

From 9fcae6164169cb99052b14fe687d258fe348b56b Mon Sep 17 00:00:00 2001
From: Chris Mumford 
Date: Thu, 14 Jan 2016 16:09:03 -0800
Subject: [PATCH 021/174] Added a Travis CI build file.

This allows for continuous integration builds by travis-ci.org.
More information at https://docs.travis-ci.com/user/languages/cpp
---
 .travis.yml | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 .travis.yml

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000..f5bd74c454
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: cpp
+compiler:
+- clang
+- gcc
+os:
+- linux
+- osx
+sudo: false
+before_install:
+- echo $LANG
+- echo $LC_ALL
+script:
+- make -j 4 check

From dd1c3c3572eb3e482e7b374d8e94ed483b98b354 Mon Sep 17 00:00:00 2001
From: Lars-Magnus Skog 
Date: Fri, 15 Jan 2016 18:28:08 +0100
Subject: [PATCH 022/174] add travis build badge

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 285654c6d9..c75b185e0e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 **LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.**
 
+[![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb)
+
 Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
 
 # Features

From adbe3eb073565c468005c48f18d16b8f1dd6ef58 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Fri, 29 Jan 2016 15:58:37 -0800
Subject: [PATCH 023/174] Putting build artifacts in subdirectory.

1. Object files, libraries, and compiled executables are put
   into subdirectories.
2. The shared library is linked from individual object files.
   This provides for greater parallelism on large desktops
   while at the same time making for easier builds on small
   (i.e. embedded) systems. Fixes issue #279.
3. One program, db_bench, is compiled using the shared library.
4. The source file for "leveldbutil" was renamed from
   leveldb_main.cc to leveldbutil.cc. This provides for simpler
   makefile rules.
5. Because all targets placed the library (libleveldb.a) at the top
   level, the last platform built (desktop/device) always overwrote
   any prior artifact.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=113407013
---
 Makefile                               | 469 +++++++++++++++++--------
 build_detect_platform                  |   2 +-
 db/{leveldb_main.cc => leveldbutil.cc} |   0
 3 files changed, 326 insertions(+), 145 deletions(-)
 rename db/{leveldb_main.cc => leveldbutil.cc} (100%)

diff --git a/Makefile b/Makefile
index d991ecc537..299da38d60 100644
--- a/Makefile
+++ b/Makefile
@@ -20,214 +20,395 @@ $(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \
 # this file is generated by the previous line to set build flags and sources
 include build_config.mk
 
+TESTS = \
+	db/autocompact_test \
+	db/c_test \
+	db/corruption_test \
+	db/db_test \
+	db/dbformat_test \
+	db/fault_injection_test \
+	db/filename_test \
+	db/log_test \
+	db/recovery_test \
+	db/skiplist_test \
+	db/version_edit_test \
+	db/version_set_test \
+	db/write_batch_test \
+	helpers/memenv/memenv_test \
+	issues/issue178_test \
+	issues/issue200_test \
+	table/filter_block_test \
+	table/table_test \
+	util/arena_test \
+	util/bloom_test \
+	util/cache_test \
+	util/coding_test \
+	util/crc32c_test \
+	util/env_test \
+	util/hash_test
+
+UTILS = \
+	db/db_bench \
+	db/leveldbutil
+
+# Put the object files in a subdirectory, but the application at the top of the object dir.
+PROGNAMES := $(notdir $(TESTS) $(UTILS))
+
+# On Linux may need libkyotocabinet-dev for dependency.
+BENCHMARKS = \
+	doc/bench/db_bench_sqlite3 \
+	doc/bench/db_bench_tree_db
+
 CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
 CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)
 
 LDFLAGS += $(PLATFORM_LDFLAGS)
 LIBS += $(PLATFORM_LIBS)
 
-LIBOBJECTS = $(SOURCES:.cc=.o)
-MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
-
-TESTUTIL = ./util/testutil.o
-TESTHARNESS = ./util/testharness.o $(TESTUTIL)
+SIMULATOR_OUTDIR=out-ios-x86
+DEVICE_OUTDIR=out-ios-arm
 
-# Note: iOS should probably be using libtool, not ar.
 ifeq ($(PLATFORM), IOS)
+# Note: iOS should probably be using libtool, not ar.
 AR=xcrun ar
+SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path)
+DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path)
+DEVICE_CFLAGS = -isysroot "$(DEVICESDK)" -arch armv6 -arch armv7 -arch armv7s -arch arm64
+SIMULATOR_CFLAGS = -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64
+STATIC_OUTDIR=out-ios-universal
+else
+STATIC_OUTDIR=out-static
+SHARED_OUTDIR=out-shared
+STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES))
+SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench)
 endif
 
-TESTS = \
-	arena_test \
-	autocompact_test \
-	bloom_test \
-	c_test \
-	cache_test \
-	coding_test \
-	corruption_test \
-	crc32c_test \
-	db_test \
-	dbformat_test \
-	env_test \
-	fault_injection_test \
-	filename_test \
-	filter_block_test \
-	hash_test \
-	issue178_test \
-	issue200_test \
-	log_test \
-	memenv_test \
-	recovery_test \
-	skiplist_test \
-	table_test \
-	version_edit_test \
-	version_set_test \
-	write_batch_test
-
-PROGRAMS = db_bench leveldbutil $(TESTS)
-BENCHMARKS = db_bench_sqlite3 db_bench_tree_db
-
-LIBRARY = libleveldb.a
-MEMENVLIBRARY = libmemenv.a
+STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o))
+STATIC_MEMENVOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+DEVICE_LIBOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(SOURCES:.cc=.o))
+DEVICE_MEMENVOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+SIMULATOR_LIBOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(SOURCES:.cc=.o))
+SIMULATOR_MEMENVOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+SHARED_LIBOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(SOURCES:.cc=.o))
+SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
+
+TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o
+TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL)
+
+STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS)))
+STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS)))
+STATIC_ALLOBJS := $(STATIC_LIBOBJECTS) $(STATIC_MEMENVOBJECTS) $(STATIC_TESTOBJS) $(STATIC_UTILOBJS) $(TESTHARNESS)
+DEVICE_ALLOBJS := $(DEVICE_LIBOBJECTS) $(DEVICE_MEMENVOBJECTS)
+SIMULATOR_ALLOBJS := $(SIMULATOR_LIBOBJECTS) $(SIMULATOR_MEMENVOBJECTS)
 
 default: all
 
 # Should we build shared libraries?
 ifneq ($(PLATFORM_SHARED_EXT),)
 
+# Many leveldb test apps use non-exported API's. Only build a subset for testing.
+SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS)
+
 ifneq ($(PLATFORM_SHARED_VERSIONED),true)
-SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
-SHARED2 = $(SHARED1)
-SHARED3 = $(SHARED1)
-SHARED = $(SHARED1)
+SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED_LIB2 = $(SHARED_LIB1)
+SHARED_LIB3 = $(SHARED_LIB1)
+SHARED_LIBS = $(SHARED_LIB1)
+SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
 else
 # Update db.h if you change these.
-SHARED_MAJOR = 1
-SHARED_MINOR = 18
-SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
-SHARED2 = $(SHARED1).$(SHARED_MAJOR)
-SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
-SHARED = $(SHARED1) $(SHARED2) $(SHARED3)
-$(SHARED1): $(SHARED3)
-	ln -fs $(SHARED3) $(SHARED1)
-$(SHARED2): $(SHARED3)
-	ln -fs $(SHARED3) $(SHARED2)
+SHARED_VERSION_MAJOR = 1
+SHARED_VERSION_MINOR = 18
+SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
+SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR)
+SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR)
+SHARED_LIBS = $(SHARED_OUTDIR)/$(SHARED_LIB1) $(SHARED_OUTDIR)/$(SHARED_LIB2) $(SHARED_OUTDIR)/$(SHARED_LIB3)
+$(SHARED_OUTDIR)/$(SHARED_LIB1): $(SHARED_OUTDIR)/$(SHARED_LIB3)
+	ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB1)
+$(SHARED_OUTDIR)/$(SHARED_LIB2): $(SHARED_OUTDIR)/$(SHARED_LIB3)
+	ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB2)
+SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
 endif
 
-$(SHARED3):
-	$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS)
+$(SHARED_OUTDIR)/$(SHARED_LIB3): $(SHARED_LIBOBJECTS)
+	$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED_LIB2) $(SHARED_LIBOBJECTS) -o $(SHARED_OUTDIR)/$(SHARED_LIB3) $(LIBS)
 
 endif  # PLATFORM_SHARED_EXT
 
-all: $(SHARED) $(LIBRARY)
+all: $(SHARED_LIBS) $(SHARED_PROGRAMS) $(STATIC_OUTDIR)/libleveldb.a $(STATIC_OUTDIR)/libmemenv.a $(STATIC_PROGRAMS)
 
-check: all $(PROGRAMS) $(TESTS)
-	for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
+check: $(STATIC_PROGRAMS)
+	for t in $(notdir $(TESTS)); do echo "***** Running $$t"; $(STATIC_OUTDIR)/$$t || exit 1; done
 
 clean:
-	-rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk
-	-rm -rf ios-x86/* ios-arm/*
+	-rm -rf out-static out-shared out-ios-x86 out-ios-arm out-ios-universal
+	-rm -f build_config.mk
+	-rm -rf ios-x86 ios-arm
 
-$(LIBRARY): $(LIBOBJECTS)
-	rm -f $@
-	$(AR) -rs $@ $(LIBOBJECTS)
+$(STATIC_OUTDIR):
+	mkdir $@
 
-db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL)
-	$(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/db: | $(STATIC_OUTDIR)
+	mkdir $@
 
-db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL)
-	$(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
+$(STATIC_OUTDIR)/helpers/memenv: | $(STATIC_OUTDIR)
+	mkdir -p $@
 
-db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL)
-	$(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
+$(STATIC_OUTDIR)/port: | $(STATIC_OUTDIR)
+	mkdir $@
 
-leveldbutil: db/leveldb_main.o $(LIBOBJECTS)
-	$(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/table: | $(STATIC_OUTDIR)
+	mkdir $@
 
-arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(STATIC_OUTDIR)/util: | $(STATIC_OUTDIR)
+	mkdir $@
 
-autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: STATIC_OBJDIRS
+STATIC_OBJDIRS: \
+	$(STATIC_OUTDIR)/db \
+	$(STATIC_OUTDIR)/port \
+	$(STATIC_OUTDIR)/table \
+	$(STATIC_OUTDIR)/util \
+	$(STATIC_OUTDIR)/helpers/memenv
 
-bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR):
+	mkdir $@
 
-c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/db: | $(SHARED_OUTDIR)
+	mkdir $@
 
-cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/helpers/memenv: | $(SHARED_OUTDIR)
+	mkdir -p $@
 
-coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/port: | $(SHARED_OUTDIR)
+	mkdir $@
 
-corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/table: | $(SHARED_OUTDIR)
+	mkdir $@
 
-crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SHARED_OUTDIR)/util: | $(SHARED_OUTDIR)
+	mkdir $@
 
-db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: SHARED_OBJDIRS
+SHARED_OBJDIRS: \
+	$(SHARED_OUTDIR)/db \
+	$(SHARED_OUTDIR)/port \
+	$(SHARED_OUTDIR)/table \
+	$(SHARED_OUTDIR)/util \
+	$(SHARED_OUTDIR)/helpers/memenv
 
-dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR):
+	mkdir $@
 
-env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/db: | $(DEVICE_OUTDIR)
+	mkdir $@
 
-fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/helpers/memenv: | $(DEVICE_OUTDIR)
+	mkdir -p $@
 
-filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/port: | $(DEVICE_OUTDIR)
+	mkdir $@
 
-filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/table: | $(DEVICE_OUTDIR)
+	mkdir $@
 
-hash_test: util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(DEVICE_OUTDIR)/util: | $(DEVICE_OUTDIR)
+	mkdir $@
 
-issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: DEVICE_OBJDIRS
+DEVICE_OBJDIRS: \
+	$(DEVICE_OUTDIR)/db \
+	$(DEVICE_OUTDIR)/port \
+	$(DEVICE_OUTDIR)/table \
+	$(DEVICE_OUTDIR)/util \
+	$(DEVICE_OUTDIR)/helpers/memenv
 
-issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR):
+	mkdir $@
 
-log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/db: | $(SIMULATOR_OUTDIR)
+	mkdir $@
 
-recovery_test: db/recovery_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/recovery_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/helpers/memenv: | $(SIMULATOR_OUTDIR)
+	mkdir -p $@
 
-table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/port: | $(SIMULATOR_OUTDIR)
+	mkdir $@
 
-skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/table: | $(SIMULATOR_OUTDIR)
+	mkdir $@
 
-version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/util: | $(SIMULATOR_OUTDIR)
+	mkdir $@
 
-version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+.PHONY: SIMULATOR_OBJDIRS
+SIMULATOR_OBJDIRS: \
+	$(SIMULATOR_OUTDIR)/db \
+	$(SIMULATOR_OUTDIR)/port \
+	$(SIMULATOR_OUTDIR)/table \
+	$(SIMULATOR_OUTDIR)/util \
+	$(SIMULATOR_OUTDIR)/helpers/memenv
 
-write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+$(STATIC_ALLOBJS): | STATIC_OBJDIRS
+$(DEVICE_ALLOBJS): | DEVICE_OBJDIRS
+$(SIMULATOR_ALLOBJS): | SIMULATOR_OBJDIRS
+$(SHARED_ALLOBJS): | SHARED_OBJDIRS
 
-$(MEMENVLIBRARY) : $(MEMENVOBJECTS)
+ifeq ($(PLATFORM), IOS)
+$(DEVICE_OUTDIR)/libleveldb.a: $(DEVICE_LIBOBJECTS)
 	rm -f $@
-	$(AR) -rs $@ $(MEMENVOBJECTS)
+	$(AR) -rs $@ $(DEVICE_LIBOBJECTS)
 
-memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS)
-	$(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS)
+$(SIMULATOR_OUTDIR)/libleveldb.a: $(SIMULATOR_LIBOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(SIMULATOR_LIBOBJECTS)
 
-ifeq ($(PLATFORM), IOS)
-# For iOS, create universal object files to be used on both the simulator and
+$(DEVICE_OUTDIR)/libmemenv.a: $(DEVICE_MEMENVOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(DEVICE_MEMENVOBJECTS)
+
+$(SIMULATOR_OUTDIR)/libmemenv.a: $(SIMULATOR_MEMENVOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(SIMULATOR_MEMENVOBJECTS)
+
+# For iOS, create universal object libraries to be used on both the simulator and
 # a device.
-SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path)
-DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path)
-IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64
-
-.cc.o:
-	mkdir -p ios-x86/$(dir $@)
-	xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 -c $< -o ios-x86/$@
-	mkdir -p ios-arm/$(dir $@)
-	xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot "$(DEVICESDK)" $(IOSARCH) -c $< -o ios-arm/$@
-	xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
-
-.c.o:
-	mkdir -p ios-x86/$(dir $@)
-	xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 -c $< -o ios-x86/$@
-	mkdir -p ios-arm/$(dir $@)
-	xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot "$(DEVICESDK)" $(IOSARCH) -c $< -o ios-arm/$@
-	xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@
+$(STATIC_OUTDIR)/libleveldb.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a
+	lipo -create $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a -output $@
 
+$(STATIC_OUTDIR)/libmemenv.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a
+	lipo -create $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a -output $@
 else
-.cc.o:
+$(STATIC_OUTDIR)/libleveldb.a:$(STATIC_LIBOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(STATIC_LIBOBJECTS)
+
+$(STATIC_OUTDIR)/libmemenv.a:$(STATIC_MEMENVOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(STATIC_MEMENVOBJECTS)
+endif
+
+$(SHARED_MEMENVLIB):$(SHARED_MEMENVOBJECTS)
+	rm -f $@
+	$(AR) -rs $@ $(SHARED_MEMENVOBJECTS)
+
+$(STATIC_OUTDIR)/db_bench:db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/db_bench_sqlite3:doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
+
+$(STATIC_OUTDIR)/db_bench_tree_db:doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
+
+$(STATIC_OUTDIR)/leveldbutil:db/leveldbutil.cc $(STATIC_LIBOBJECTS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/leveldbutil.cc $(STATIC_LIBOBJECTS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/arena_test:util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/autocompact_test:db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/bloom_test:util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/c_test:$(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/cache_test:util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/coding_test:util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/corruption_test:db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/crc32c_test:util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/fault_injection_test:db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/filename_test:db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/filter_block_test:table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/hash_test:util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/issue178_test:issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/issue200_test:issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/log_test:db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/recovery_test:db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/table_test:table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/skiplist_test:db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/version_edit_test:db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/version_set_test:db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
+	$(CXX) $(LDFLAGS) $(CXXFLAGS) db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
+$(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS)
+	$(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS)
+
+$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL)
+	$(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS)
+
+.PHONY: run-shared
+run-shared: $(SHARED_OUTDIR)/db_bench
+	LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench
+
+$(SIMULATOR_OUTDIR)/%.o: %.cc
+	xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
+
+$(DEVICE_OUTDIR)/%.o: %.cc
+	xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
+
+$(SIMULATOR_OUTDIR)/%.o: %.c
+	xcrun -sdk iphonesimulator $(CC) $(CFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
+
+$(DEVICE_OUTDIR)/%.o: %.c
+	xcrun -sdk iphoneos $(CC) $(CFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
+
+$(STATIC_OUTDIR)/%.o: %.cc
 	$(CXX) $(CXXFLAGS) -c $< -o $@
 
-.c.o:
+$(STATIC_OUTDIR)/%.o: %.c
 	$(CC) $(CFLAGS) -c $< -o $@
-endif
+
+$(SHARED_OUTDIR)/%.o: %.cc
+	$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
+
+$(SHARED_OUTDIR)/%.o: %.c
+	$(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
diff --git a/build_detect_platform b/build_detect_platform
index bb76c4f22e..f0629939ca 100755
--- a/build_detect_platform
+++ b/build_detect_platform
@@ -161,7 +161,7 @@ DIRS="$PREFIX/db $PREFIX/util $PREFIX/table"
 set -f # temporarily disable globbing so that our patterns aren't expanded
 PRUNE_TEST="-name *test*.cc -prune"
 PRUNE_BENCH="-name *_bench.cc -prune"
-PRUNE_TOOL="-name leveldb_main.cc -prune"
+PRUNE_TOOL="-name leveldbutil.cc -prune"
 PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "`
 
 set +f # re-enable globbing
diff --git a/db/leveldb_main.cc b/db/leveldbutil.cc
similarity index 100%
rename from db/leveldb_main.cc
rename to db/leveldbutil.cc

From 6b18316d011e495b42ca84813936c994cd90e88d Mon Sep 17 00:00:00 2001
From: Bruce Dawson 
Date: Fri, 19 Feb 2016 13:59:19 -0800
Subject: [PATCH 024/174] Fix signed/unsigned mismatch on VC++ builds

---
 db/skiplist_test.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc
index c78f4b4fb1..aee1461e1b 100644
--- a/db/skiplist_test.cc
+++ b/db/skiplist_test.cc
@@ -250,7 +250,7 @@ class ConcurrentTest {
         // Note that generation 0 is never inserted, so it is ok if
         // <*,0,*> is missing.
         ASSERT_TRUE((gen(pos) == 0) ||
-                    (gen(pos) > initial_state.Get(key(pos)))
+                    (gen(pos) > static_cast(initial_state.Get(key(pos))))
                     ) << "key: " << key(pos)
                       << "; gen: " << gen(pos)
                       << "; initgen: "

From 32113439095d148fa93c7581a15f52ff26a389d3 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Sat, 30 Jan 2016 07:44:54 -0800
Subject: [PATCH 025/174] Deleted redundant null ptr check prior to delete.

Fixes issue #338.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=113439460
---
 table/table.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/table/table.cc b/table/table.cc
index dff8a82590..decf8082cc 100644
--- a/table/table.cc
+++ b/table/table.cc
@@ -82,7 +82,7 @@ Status Table::Open(const Options& options,
     *table = new Table(rep);
     (*table)->ReadMeta(footer);
   } else {
-    if (index_block) delete index_block;
+    delete index_block;
   }
 
   return s;

From e84b5bdb5af6a4df8c3dc3f6f644b4ca3b6722cc Mon Sep 17 00:00:00 2001
From: mjwiacek 
Date: Tue, 23 Feb 2016 07:36:39 -0800
Subject: [PATCH 026/174] This CL fixes a bug encountered when reading records
 from leveldb files that have been split, as in a [] input task split.

Detailed description:

Suppose an input split is generated between two leveldb record blocks and the preceding block ends with null padding.

A reader that previously read at least 1 record within the first block (before encountering the padding) upon trying to read the next record, will successfully and correctly read the next logical record from the subsequent block, but will return a last record offset pointing to the padding in the first block.

When this happened in a [], it resulted in duplicate records being handled at what appeared to be different offsets that were separated by only a few bytes.

This behavior is only observed when at least 1 record was read from the first block before encountering the padding. If the initial offset for a reader was within the padding, the correct record offset would be reported, namely the offset within the second block.

The tests failed to catch this scenario/bug, because each read test only read a single record with an initial offset. This CL adds an explicit test case for this scenario, and modifies the test structure to read all remaining records in the test case after an initial offset is specified.  Thus an initial offset that jumps to record #3, with 5 total records in the test file, will result in reading 2 records, and validating the offset of each of them in order to pass successfully.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=115338487
---
 db/log_reader.cc |  8 +++++++-
 db/log_test.cc   | 44 +++++++++++++++++++++++++++++++++-----------
 2 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/db/log_reader.cc b/db/log_reader.cc
index 6d4a5b2e39..a6d304545d 100644
--- a/db/log_reader.cc
+++ b/db/log_reader.cc
@@ -73,8 +73,14 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
 
   Slice fragment;
   while (true) {
-    uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
     const unsigned int record_type = ReadPhysicalRecord(&fragment);
+
+    // ReadPhysicalRecord may have only had an empty trailer remaining in its
+    // internal buffer. Calculate the offset of the next physical record now
+    // that it has returned, properly accounting for its header size.
+    uint64_t physical_record_offset =
+        end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size();
+
     if (resyncing_) {
       if (record_type == kMiddleType) {
         continue;
diff --git a/db/log_test.cc b/db/log_test.cc
index 7b08c261f5..48a5928657 100644
--- a/db/log_test.cc
+++ b/db/log_test.cc
@@ -110,6 +110,7 @@ class LogTest {
   // Record metadata for testing initial offset functionality
   static size_t initial_offset_record_sizes_[];
   static uint64_t initial_offset_last_record_offsets_[];
+  static int num_initial_offset_records_;
 
  public:
   LogTest() : reading_(false),
@@ -192,7 +193,7 @@ class LogTest {
   }
 
   void WriteInitialOffsetLog() {
-    for (int i = 0; i < 4; i++) {
+    for (int i = 0; i < num_initial_offset_records_; i++) {
       std::string record(initial_offset_record_sizes_[i],
                          static_cast('a' + i));
       Write(record);
@@ -223,14 +224,20 @@ class LogTest {
     source_.contents_ = Slice(dest_.contents_);
     Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
                                        initial_offset);
-    Slice record;
-    std::string scratch;
-    ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));
-    ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],
-              record.size());
-    ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],
-              offset_reader->LastRecordOffset());
-    ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
+
+    // Read all records from expected_record_offset through the last one.
+    ASSERT_LT(expected_record_offset, num_initial_offset_records_);
+    for (; expected_record_offset < num_initial_offset_records_;
+         ++expected_record_offset) {
+      Slice record;
+      std::string scratch;
+      ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));
+      ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],
+                record.size());
+      ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],
+                offset_reader->LastRecordOffset());
+      ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
+    }
     delete offset_reader;
   }
 };
@@ -239,15 +246,26 @@ size_t LogTest::initial_offset_record_sizes_[] =
     {10000,  // Two sizable records in first block
      10000,
      2 * log::kBlockSize - 1000,  // Span three blocks
-     1};
+     1,
+     13716,  // Consume all but two bytes of block 3.
+     log::kBlockSize - kHeaderSize, // Consume the entirety of block 4.
+    };
 
 uint64_t LogTest::initial_offset_last_record_offsets_[] =
     {0,
      kHeaderSize + 10000,
      2 * (kHeaderSize + 10000),
      2 * (kHeaderSize + 10000) +
-         (2 * log::kBlockSize - 1000) + 3 * kHeaderSize};
+         (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
+     2 * (kHeaderSize + 10000) +
+         (2 * log::kBlockSize - 1000) + 3 * kHeaderSize
+         + kHeaderSize + 1,
+     3 * log::kBlockSize,
+    };
 
+// LogTest::initial_offset_last_record_offsets_ must be defined before this.
+int LogTest::num_initial_offset_records_ =
+    sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t);
 
 TEST(LogTest, Empty) {
   ASSERT_EQ("EOF", Read());
@@ -553,6 +571,10 @@ TEST(LogTest, ReadFourthStart) {
       3);
 }
 
+TEST(LogTest, ReadInitialOffsetIntoBlockPadding) {
+  CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5);
+}
+
 TEST(LogTest, ReadEnd) {
   CheckOffsetPastEndReturnsNoRecords(0);
 }

From ea992b467b9f3c892ae87267ba908e97d532b1b2 Mon Sep 17 00:00:00 2001
From: Nicholas Westlake 
Date: Tue, 12 Apr 2016 23:38:09 +0100
Subject: [PATCH 027/174] Change std::uint64_t to uint64_t (#354)

-This fixes compile errors with default setup on RHEL 6 systems.
---
 db/recovery_test.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/db/recovery_test.cc b/db/recovery_test.cc
index e0d0bf83c4..9596f4288a 100644
--- a/db/recovery_test.cc
+++ b/db/recovery_test.cc
@@ -104,10 +104,10 @@ class RecoveryTest {
     return GetFiles(kLogFile)[0];
   }
 
-  std::vector GetFiles(FileType t) {
+  std::vector GetFiles(FileType t) {
     std::vector filenames;
     ASSERT_OK(env_->GetChildren(dbname_, &filenames));
-    std::vector result;
+    std::vector result;
     for (size_t i = 0; i < filenames.size(); i++) {
       uint64_t number;
       FileType type;

From a7bff697baa062c8f6b8fb760eacf658712b611a Mon Sep 17 00:00:00 2001
From: John Abd-El-Malek 
Date: Fri, 15 Apr 2016 10:58:27 -0700
Subject: [PATCH 028/174] Fix LevelDB build when asserts are enabled in release
 builds. (#367)

* Fix LevelDB build when asserts are enabled in release builds.

BUG=https://bugs.chromium.org/p/chromium/issues/detail?id=603166

* fix

* Add comment
---
 table/format.cc | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/table/format.cc b/table/format.cc
index aa63144c9e..24e4e02445 100644
--- a/table/format.cc
+++ b/table/format.cc
@@ -30,15 +30,14 @@ Status BlockHandle::DecodeFrom(Slice* input) {
 }
 
 void Footer::EncodeTo(std::string* dst) const {
-#ifndef NDEBUG
   const size_t original_size = dst->size();
-#endif
   metaindex_handle_.EncodeTo(dst);
   index_handle_.EncodeTo(dst);
   dst->resize(2 * BlockHandle::kMaxEncodedLength);  // Padding
   PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu));
   PutFixed32(dst, static_cast(kTableMagicNumber >> 32));
   assert(dst->size() == original_size + kEncodedLength);
+  (void)original_size;  // Disable unused variable warning.
 }
 
 Status Footer::DecodeFrom(Slice* input) {

From 06a191b8dec693c74c4d3e8347a89dc8596b42fd Mon Sep 17 00:00:00 2001
From: m3b 
Date: Wed, 25 May 2016 13:32:03 -0700
Subject: [PATCH 029/174] fix problems in LevelDB's caching code

Background:

LevelDB uses a cache (util/cache.h, util/cache.cc) of (key,value)
pairs for two purposes:
- a cache of (table, file handle) pairs
- a cache of blocks

The cache places the (key,value) pairs in a reference-counted
wrapper.  When it returns a value, it returns a reference to this
wrapper.  When the client has finished using the reference and
its enclosed (key,value), it calls Release() to decrement the
reference count.

Each (key,value) pair has an associated resource usage.  The
cache maintains the sum of the usages of the elements it holds,
and removes values as needed to keep the sum below a capacity
threshold.  It maintains an LRU list so that it will remove the
least-recently used elements first.

The max_open_files option to LevelDB sets the size of the cache
of (table, file handle) pairs.  The option is not used in any
other way.

The observed behaviour:

If LevelDB at any time used more file handles concurrently than
the cache size set via max_open_files, it attempted to reduce the
number by evicting entries from the table cache.  This could
happen most easily during compaction, and if max_open_files was
low.  Because the handles were in use, their reference count did
not drop to zero, and so the usage sum in the cache was not
modified by the evictions.  Subsequent Insert() calls returned
valid handles, but their entries were immediately evicted from
the cache, which though empty still acted as though full.  As a
result, there was effectively no caching, and the number of open
file handles rose []ly until it hit system-imposed limits and
the process died.

If one set max_open_files lower, the cache was more likely to
exhibit this beahviour, and cause the process to run out of file
descriptors.  That is, max_open_files acted in almost exactly the
opposite manner from what was intended.

The problems:

1. The cache kept all elements on its LRU list eligible for capacity
   eviction---even those with outstanding references from clients.  This was
   ineffective in reducing resource consumption because there was an
   outstanding reference, guaranteeing that the items remained.  A secondary
   issue was that there is no guarantee that these in-use items will be the
   last things reached in the LRU chain, which actually recorded
   "least-recently requested" rather than "least-recently used".

2. The sum of usages was decremented not when a (key,value) was evicted from
   the cache, but when its reference count went to zero.  Thus, when things
   were removed from the cache, either by garbage collection or via Erase(),
   the usage sum was not necessarily decreased.  This allowed the cache to act
   as though full when it was in fact not, reducing caching effectiveness, and
   leading to more resources being consumed---the opposite of what the
   evictions were intended to achieve.

3. (minor) The cache's clients insert items into it by first looking up the
   key, and inserting only if no value is found.  Although the cache has an
   internal lock, the clients use no locking to ensure atomicity of the
   Lookup/Insert pair.  (see table/table.cc:  block_cache->Insert() and
   db/table_cache.cc:  cache_->Insert()).  Thus, if two threads Insert() at
   about the same time, they can both Lookup(), find nothing, and both
   Insert().  The second Insert() would evict the first value, leaving each
   thread with a handle on its own version of the data, and with the second
   version in the cache.  It would be better if both threads ended up with a
   handle on the same (key,value) pair, which implies it must be the first item
   inserted.  This suggests that Insert() should not replace an existing value.

   This can be made safe with current usage inside LeveDB itself, but this is
   not easy to change first because Cache is a public interface, so to change
   the semantics of an existing call might break things, second because Cache
   is an abstract virtual class, so adding a new abstract virtual method may
   break other implementations, and third, the new method "insert without
   replacing" cannot be implemented in terms of the existing methods, so cannot
   be implemented with a non-abstract default.   But fortunately, the effects
   of this issue are minor, so this issue is not fixed by this change.

The changes:

The assumption in the fixes is that it is always better to cache
entries unless removal from the cache would lead to deallocation.

Cache entries now have an "in_cache" boolean indicating whether
the cache has a reference on the entry.  The only ways that this can
become false without the entry being passed to its "deleter" are via
Erase(), via Insert() when an element with a duplicate key is inserted,
or on destruction of the cache.

The cache now keeps two linked lists instead of one.  All items
in the cache are in one list or the other, and never both.  Items
still referenced by clients but erased from the cache are in
neither list.  The lists are:
- in-use:  contains the items currently referenced by clients, in no particular
  order.  (This list is used for invariant checking.  If we removed the check,
  elements that would otherwise be on this list could be left as disconnected
  singleton lists.)
- LRU:  contains the items not currently referenced by clients, in LRU order

A new internal Ref() method increments the reference count.  If
incrementing from 1 to 2 for an item in the cache, it is moved
from the LRU list to the in-use list.

The Unref() call now moves things from the in-use list to the LRU
list if the reference count falls to 1, and the item is in the
cache.  It no longer adjusts the usage sum.  The usage sum now
reflects only what is in the cache, rather than including
still-referenced items that have been evicted.

The LRU_Append() now takes a "list" parameter so that it can be
used to append either to the LRU list or the in-use list.

Lookup() is modified to use the new Ref() call, rather than
adjusting the reference count and LRU chain directly.

Insert() eviction code is also modified to adjust the usage sum and the
in_cache boolean of the evicted elements.  Some LevelDB tests assume that there
will be no caching whatsoever if the cache size is set to zero, so this is
handled as a special case.

A new private method FinishErase() is factored out
with the common code from where items are removed from the cache.

Erase() is modified to adjust the usage sum and the in_cache
boolean of the erased elements, and to use FinishErase().

Prune() is modified to use FinishErase() also, and to make use of the fact that
the lru_ list now contains only items with reference count 1.

- EvictionPolicy is modified to test that an entry with an
outstanding handle is not evicted.  This test fails with the old cache.cc.

- A new test case UseExceedsCacheSize verifies that even when the
cache is overfull of entries with outstanding handles, none are
evicted.  This test fails with the old cache.cc, and is the key
issue that causes file descriptors to run out when the cache
size is set too small.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123247237
---
 util/cache.cc      | 118 ++++++++++++++++++++++++++++++++-------------
 util/cache_test.cc |  29 ++++++++++-
 2 files changed, 112 insertions(+), 35 deletions(-)

diff --git a/util/cache.cc b/util/cache.cc
index 0881bce9c5..ce46886171 100644
--- a/util/cache.cc
+++ b/util/cache.cc
@@ -19,6 +19,23 @@ Cache::~Cache() {
 namespace {
 
 // LRU cache implementation
+//
+// Cache entries have an "in_cache" boolean indicating whether the cache has a
+// reference on the entry.  The only ways that this can become false without the
+// entry being passed to its "deleter" are via Erase(), via Insert() when
+// an element with a duplicate key is inserted, or on destruction of the cache.
+//
+// The cache keeps two linked lists of items in the cache.  All items in the
+// cache are in one list or the other, and never both.  Items still referenced
+// by clients but erased from the cache are in neither list.  The lists are:
+// - in-use:  contains the items currently referenced by clients, in no
+//   particular order.  (This list is used for invariant checking.  If we
+//   removed the check, elements that would otherwise be on this list could be
+//   left as disconnected singleton lists.)
+// - LRU:  contains the items not currently referenced by clients, in LRU order
+// Elements are moved between these lists by the Ref() and Unref() methods,
+// when they detect an element in the cache acquiring or losing its only
+// external reference.
 
 // An entry is a variable length heap-allocated structure.  Entries
 // are kept in a circular doubly linked list ordered by access time.
@@ -30,7 +47,8 @@ struct LRUHandle {
   LRUHandle* prev;
   size_t charge;      // TODO(opt): Only allow uint32_t?
   size_t key_length;
-  uint32_t refs;
+  bool in_cache;      // Whether entry is in the cache.
+  uint32_t refs;      // References, including cache reference, if present.
   uint32_t hash;      // Hash of key(); used for fast sharding and comparisons
   char key_data[1];   // Beginning of key
 
@@ -155,8 +173,10 @@ class LRUCache {
 
  private:
   void LRU_Remove(LRUHandle* e);
-  void LRU_Append(LRUHandle* e);
+  void LRU_Append(LRUHandle*list, LRUHandle* e);
+  void Ref(LRUHandle* e);
   void Unref(LRUHandle* e);
+  bool FinishErase(LRUHandle* e);
 
   // Initialized before use.
   size_t capacity_;
@@ -167,34 +187,55 @@ class LRUCache {
 
   // Dummy head of LRU list.
   // lru.prev is newest entry, lru.next is oldest entry.
+  // Entries have refs==1 and in_cache==true.
   LRUHandle lru_;
 
+  // Dummy head of in-use list.
+  // Entries are in use by clients, and have refs >= 2 and in_cache==true.
+  LRUHandle in_use_;
+
   HandleTable table_;
 };
 
 LRUCache::LRUCache()
     : usage_(0) {
-  // Make empty circular linked list
+  // Make empty circular linked lists.
   lru_.next = &lru_;
   lru_.prev = &lru_;
+  in_use_.next = &in_use_;
+  in_use_.prev = &in_use_;
 }
 
 LRUCache::~LRUCache() {
+  assert(in_use_.next == &in_use_);  // Error if caller has an unreleased handle
   for (LRUHandle* e = lru_.next; e != &lru_; ) {
     LRUHandle* next = e->next;
-    assert(e->refs == 1);  // Error if caller has an unreleased handle
+    assert(e->in_cache);
+    e->in_cache = false;
+    assert(e->refs == 1);  // Invariant of lru_ list.
     Unref(e);
     e = next;
   }
 }
 
+void LRUCache::Ref(LRUHandle* e) {
+  if (e->refs == 1 && e->in_cache) {  // If on lru_ list, move to in_use_ list.
+    LRU_Remove(e);
+    LRU_Append(&in_use_, e);
+  }
+  e->refs++;
+}
+
 void LRUCache::Unref(LRUHandle* e) {
   assert(e->refs > 0);
   e->refs--;
-  if (e->refs <= 0) {
-    usage_ -= e->charge;
+  if (e->refs == 0) { // Deallocate.
+    assert(!e->in_cache);
     (*e->deleter)(e->key(), e->value);
     free(e);
+  } else if (e->in_cache && e->refs == 1) {  // No longer in use; move to lru_ list.
+    LRU_Remove(e);
+    LRU_Append(&lru_, e);
   }
 }
 
@@ -203,10 +244,10 @@ void LRUCache::LRU_Remove(LRUHandle* e) {
   e->prev->next = e->next;
 }
 
-void LRUCache::LRU_Append(LRUHandle* e) {
-  // Make "e" newest entry by inserting just before lru_
-  e->next = &lru_;
-  e->prev = lru_.prev;
+void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {
+  // Make "e" newest entry by inserting just before *list
+  e->next = list;
+  e->prev = list->prev;
   e->prev->next = e;
   e->next->prev = e;
 }
@@ -215,9 +256,7 @@ Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
   MutexLock l(&mutex_);
   LRUHandle* e = table_.Lookup(key, hash);
   if (e != NULL) {
-    e->refs++;
-    LRU_Remove(e);
-    LRU_Append(e);
+    Ref(e);
   }
   return reinterpret_cast(e);
 }
@@ -239,46 +278,57 @@ Cache::Handle* LRUCache::Insert(
   e->charge = charge;
   e->key_length = key.size();
   e->hash = hash;
-  e->refs = 2;  // One from LRUCache, one for the returned handle
+  e->in_cache = false;
+  e->refs = 1;  // for the returned handle.
   memcpy(e->key_data, key.data(), key.size());
-  LRU_Append(e);
-  usage_ += charge;
 
-  LRUHandle* old = table_.Insert(e);
-  if (old != NULL) {
-    LRU_Remove(old);
-    Unref(old);
-  }
+  if (capacity_ > 0) {
+    e->refs++;  // for the cache's reference.
+    e->in_cache = true;
+    LRU_Append(&in_use_, e);
+    usage_ += charge;
+    FinishErase(table_.Insert(e));
+  } // else don't cache.  (Tests use capacity_==0 to turn off caching.)
 
   while (usage_ > capacity_ && lru_.next != &lru_) {
     LRUHandle* old = lru_.next;
-    LRU_Remove(old);
-    table_.Remove(old->key(), old->hash);
-    Unref(old);
+    assert(old->refs == 1);
+    bool erased = FinishErase(table_.Remove(old->key(), old->hash));
+    if (!erased) {  // to avoid unused variable when compiled NDEBUG
+      assert(erased);
+    }
   }
 
   return reinterpret_cast(e);
 }
 
-void LRUCache::Erase(const Slice& key, uint32_t hash) {
-  MutexLock l(&mutex_);
-  LRUHandle* e = table_.Remove(key, hash);
+// If e != NULL, finish removing *e from the cache; it has already been removed
+// from the hash table.  Return whether e != NULL.  Requires mutex_ held.
+bool LRUCache::FinishErase(LRUHandle* e) {
   if (e != NULL) {
+    assert(e->in_cache);
     LRU_Remove(e);
+    e->in_cache = false;
+    usage_ -= e->charge;
     Unref(e);
   }
+  return e != NULL;
+}
+
+void LRUCache::Erase(const Slice& key, uint32_t hash) {
+  MutexLock l(&mutex_);
+  FinishErase(table_.Remove(key, hash));
 }
 
 void LRUCache::Prune() {
   MutexLock l(&mutex_);
-  for (LRUHandle* e = lru_.next; e != &lru_; ) {
-    LRUHandle* next = e->next;
-    if (e->refs == 1) {
-      table_.Remove(e->key(), e->hash);
-      LRU_Remove(e);
-      Unref(e);
+  while (lru_.next != &lru_) {
+    LRUHandle* e = lru_.next;
+    assert(e->refs == 1);
+    bool erased = FinishErase(table_.Remove(e->key(), e->hash));
+    if (!erased) {  // to avoid unused variable when compiled NDEBUG
+      assert(erased);
     }
-    e = next;
   }
 }
 
diff --git a/util/cache_test.cc b/util/cache_test.cc
index 90af5fe354..468f7a6425 100644
--- a/util/cache_test.cc
+++ b/util/cache_test.cc
@@ -59,6 +59,11 @@ class CacheTest {
                                    &CacheTest::Deleter));
   }
 
+  Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) {
+    return cache_->Insert(EncodeKey(key), EncodeValue(value), charge,
+                          &CacheTest::Deleter);
+  }
+
   void Erase(int key) {
     cache_->Erase(EncodeKey(key));
   }
@@ -135,8 +140,11 @@ TEST(CacheTest, EntriesArePinned) {
 TEST(CacheTest, EvictionPolicy) {
   Insert(100, 101);
   Insert(200, 201);
+  Insert(300, 301);
+  Cache::Handle* h = cache_->Lookup(EncodeKey(300));
 
-  // Frequently used entry must be kept around
+  // Frequently used entry must be kept around,
+  // as must things that are still in use.
   for (int i = 0; i < kCacheSize + 100; i++) {
     Insert(1000+i, 2000+i);
     ASSERT_EQ(2000+i, Lookup(1000+i));
@@ -144,6 +152,25 @@ TEST(CacheTest, EvictionPolicy) {
   }
   ASSERT_EQ(101, Lookup(100));
   ASSERT_EQ(-1, Lookup(200));
+  ASSERT_EQ(301, Lookup(300));
+  cache_->Release(h);
+}
+
+TEST(CacheTest, UseExceedsCacheSize) {
+  // Overfill the cache, keeping handles on all inserted entries.
+  std::vector h;
+  for (int i = 0; i < kCacheSize + 100; i++) {
+    h.push_back(InsertAndReturnHandle(1000+i, 2000+i));
+  }
+
+  // Check that all the entries can be found in the cache.
+  for (int i = 0; i < h.size(); i++) {
+    ASSERT_EQ(2000+i, Lookup(1000+i));
+  }
+
+  for (int i = 0; i < h.size(); i++) {
+    cache_->Release(h[i]);
+  }
 }
 
 TEST(CacheTest, HeavyEntries) {

From fa6dc010a20b6eb175d92d05651972651278e06f Mon Sep 17 00:00:00 2001
From: sanjay 
Date: Fri, 27 May 2016 10:37:28 -0700
Subject: [PATCH 030/174] A zippy change broke test assumptions about the size
 of compressed output. Fix the tests by allowing more slop in zippy's
 behavior. ------------- Created by MOE: https://github.com/google/moe
 MOE_MIGRATED_REVID=123432472

---
 table/table_test.cc | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/table/table_test.cc b/table/table_test.cc
index c723bf84cf..abf6e246ff 100644
--- a/table/table_test.cc
+++ b/table/table_test.cc
@@ -853,12 +853,20 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
   options.compression = kSnappyCompression;
   c.Finish(options, &keys, &kvmap);
 
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"),       0,      0));
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"),       0,      0));
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"),       0,      0));
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"),    2000,   3000));
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"),    2000,   3000));
-  ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"),    4000,   6000));
+  // Expected upper and lower bounds of space used by compressible strings.
+  static const int kSlop = 1000;  // Compressor effectiveness varies.
+  const int expected = 2500;  // 10000 * compression ratio (0.25)
+  const int min_z = expected - kSlop;
+  const int max_z = expected + kSlop;
+
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop));
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop));
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop));
+  // Have now emitted a large compressible string, so adjust expected offset.
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z));
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z));
+  // Have now emitted two large compressible strings, so adjust expected offset.
+  ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z));
 }
 
 }  // namespace leveldb

From 3080a45b626f8ddb474bc5e860796a48b51b3cf0 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Wed, 10 Aug 2016 17:05:16 -0700
Subject: [PATCH 031/174] Increase leveldb version to 1.19.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=129930720
---
 Makefile             | 2 +-
 include/leveldb/db.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 299da38d60..07a5a1ead6 100644
--- a/Makefile
+++ b/Makefile
@@ -121,7 +121,7 @@ SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
 else
 # Update db.h if you change these.
 SHARED_VERSION_MAJOR = 1
-SHARED_VERSION_MINOR = 18
+SHARED_VERSION_MINOR = 19
 SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
 SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR)
 SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR)
diff --git a/include/leveldb/db.h b/include/leveldb/db.h
index 53c7068765..9752cbad51 100644
--- a/include/leveldb/db.h
+++ b/include/leveldb/db.h
@@ -14,7 +14,7 @@ namespace leveldb {
 
 // Update Makefile if you change these
 static const int kMajorVersion = 1;
-static const int kMinorVersion = 18;
+static const int kMinorVersion = 19;
 
 struct Options;
 struct ReadOptions;

From a2fb086d07b7dbd9c4a59fe57646bd465841edd5 Mon Sep 17 00:00:00 2001
From: corrado 
Date: Tue, 27 Sep 2016 04:50:38 -0700
Subject: [PATCH 032/174] Add option for max file size. The currend hard-coded
 value of 2M is inefficient in colossus.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=134391640
---
 db/db_bench.cc            | 38 +++++++++++++++++++-------
 db/db_impl.cc             |  1 +
 db/version_set.cc         | 57 ++++++++++++++++++++++++---------------
 db/version_set.h          |  2 +-
 include/leveldb/options.h | 12 +++++++++
 util/options.cc           |  1 +
 6 files changed, 79 insertions(+), 32 deletions(-)

diff --git a/db/db_bench.cc b/db/db_bench.cc
index 7a0f5e08cd..3ad19a512b 100644
--- a/db/db_bench.cc
+++ b/db/db_bench.cc
@@ -84,6 +84,14 @@ static bool FLAGS_histogram = false;
 // (initialized to default value by "main")
 static int FLAGS_write_buffer_size = 0;
 
+// Number of bytes written to each file.
+// (initialized to default value by "main")
+static int FLAGS_max_file_size = 0;
+
+// Approximate size of user data packed per block (before compression.
+// (initialized to default value by "main")
+static int FLAGS_block_size = 0;
+
 // Number of bytes to use as a cache of uncompressed data.
 // Negative means use default settings.
 static int FLAGS_cache_size = -1;
@@ -109,6 +117,7 @@ static const char* FLAGS_db = NULL;
 namespace leveldb {
 
 namespace {
+leveldb::Env* g_env = NULL;
 
 // Helper for quickly generating random data.
 class RandomGenerator {
@@ -186,7 +195,7 @@ class Stats {
     done_ = 0;
     bytes_ = 0;
     seconds_ = 0;
-    start_ = Env::Default()->NowMicros();
+    start_ = g_env->NowMicros();
     finish_ = start_;
     message_.clear();
   }
@@ -204,7 +213,7 @@ class Stats {
   }
 
   void Stop() {
-    finish_ = Env::Default()->NowMicros();
+    finish_ = g_env->NowMicros();
     seconds_ = (finish_ - start_) * 1e-6;
   }
 
@@ -214,7 +223,7 @@ class Stats {
 
   void FinishedSingleOp() {
     if (FLAGS_histogram) {
-      double now = Env::Default()->NowMicros();
+      double now = g_env->NowMicros();
       double micros = now - last_op_finish_;
       hist_.Add(micros);
       if (micros > 20000) {
@@ -404,10 +413,10 @@ class Benchmark {
     reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
     heap_counter_(0) {
     std::vector files;
-    Env::Default()->GetChildren(FLAGS_db, &files);
+    g_env->GetChildren(FLAGS_db, &files);
     for (size_t i = 0; i < files.size(); i++) {
       if (Slice(files[i]).starts_with("heap-")) {
-        Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
+        g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
       }
     }
     if (!FLAGS_use_existing_db) {
@@ -589,7 +598,7 @@ class Benchmark {
       arg[i].shared = &shared;
       arg[i].thread = new ThreadState(i);
       arg[i].thread->shared = &shared;
-      Env::Default()->StartThread(ThreadBody, &arg[i]);
+      g_env->StartThread(ThreadBody, &arg[i]);
     }
 
     shared.mu.Lock();
@@ -700,9 +709,12 @@ class Benchmark {
   void Open() {
     assert(db_ == NULL);
     Options options;
+    options.env = g_env;
     options.create_if_missing = !FLAGS_use_existing_db;
     options.block_cache = cache_;
     options.write_buffer_size = FLAGS_write_buffer_size;
+    options.max_file_size = FLAGS_max_file_size;
+    options.block_size = FLAGS_block_size;
     options.max_open_files = FLAGS_open_files;
     options.filter_policy = filter_policy_;
     options.reuse_logs = FLAGS_reuse_logs;
@@ -925,7 +937,7 @@ class Benchmark {
     char fname[100];
     snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);
     WritableFile* file;
-    Status s = Env::Default()->NewWritableFile(fname, &file);
+    Status s = g_env->NewWritableFile(fname, &file);
     if (!s.ok()) {
       fprintf(stderr, "%s\n", s.ToString().c_str());
       return;
@@ -934,7 +946,7 @@ class Benchmark {
     delete file;
     if (!ok) {
       fprintf(stderr, "heap profiling not supported\n");
-      Env::Default()->DeleteFile(fname);
+      g_env->DeleteFile(fname);
     }
   }
 };
@@ -943,6 +955,8 @@ class Benchmark {
 
 int main(int argc, char** argv) {
   FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;
+  FLAGS_max_file_size = leveldb::Options().max_file_size;
+  FLAGS_block_size = leveldb::Options().block_size;
   FLAGS_open_files = leveldb::Options().max_open_files;
   std::string default_db_path;
 
@@ -973,6 +987,10 @@ int main(int argc, char** argv) {
       FLAGS_value_size = n;
     } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {
       FLAGS_write_buffer_size = n;
+    } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) {
+      FLAGS_max_file_size = n;
+    } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) {
+      FLAGS_block_size = n;
     } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
       FLAGS_cache_size = n;
     } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) {
@@ -987,9 +1005,11 @@ int main(int argc, char** argv) {
     }
   }
 
+  leveldb::g_env = leveldb::Env::Default();
+
   // Choose a location for the test database if none given with --db=
   if (FLAGS_db == NULL) {
-      leveldb::Env::Default()->GetTestDirectory(&default_db_path);
+      leveldb::g_env->GetTestDirectory(&default_db_path);
       default_db_path += "/dbbench";
       FLAGS_db = default_db_path.c_str();
   }
diff --git a/db/db_impl.cc b/db/db_impl.cc
index 60f4e66e55..f43ad76794 100644
--- a/db/db_impl.cc
+++ b/db/db_impl.cc
@@ -96,6 +96,7 @@ Options SanitizeOptions(const std::string& dbname,
   result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL;
   ClipToRange(&result.max_open_files,    64 + kNumNonTableCacheFiles, 50000);
   ClipToRange(&result.write_buffer_size, 64<<10,                      1<<30);
+  ClipToRange(&result.max_file_size,     1<<20,                       1<<30);
   ClipToRange(&result.block_size,        1<<10,                       4<<20);
   if (result.info_log == NULL) {
     // Open a log file in the same directory as the db
diff --git a/db/version_set.cc b/db/version_set.cc
index a5e0f77a6a..b1256f90e1 100644
--- a/db/version_set.cc
+++ b/db/version_set.cc
@@ -20,21 +20,29 @@
 
 namespace leveldb {
 
-static const int kTargetFileSize = 2 * 1048576;
+static int TargetFileSize(const Options* options) {
+  return options->max_file_size;
+}
 
 // Maximum bytes of overlaps in grandparent (i.e., level+2) before we
 // stop building a single file in a level->level+1 compaction.
-static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize;
+static int64_t MaxGrandParentOverlapBytes(const Options* options) {
+  return 10 * TargetFileSize(options);
+}
 
 // Maximum number of bytes in all compacted files.  We avoid expanding
 // the lower level file set of a compaction if it would make the
 // total compaction cover more than this many bytes.
-static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize;
+static int64_t ExpandedCompactionByteSizeLimit(const Options* options) {
+  return 25 * TargetFileSize(options);
+}
 
-static double MaxBytesForLevel(int level) {
+static double MaxBytesForLevel(const Options* options, int level) {
   // Note: the result for level zero is not really used since we set
   // the level-0 compaction threshold based on number of files.
-  double result = 10 * 1048576.0;  // Result for both level-0 and level-1
+
+  // Result for both level-0 and level-1
+  double result = 10. * 1048576.0;
   while (level > 1) {
     result *= 10;
     level--;
@@ -42,8 +50,9 @@ static double MaxBytesForLevel(int level) {
   return result;
 }
 
-static uint64_t MaxFileSizeForLevel(int level) {
-  return kTargetFileSize;  // We could vary per level to reduce number of files?
+static uint64_t MaxFileSizeForLevel(const Options* options, int level) {
+  // We could vary per level to reduce number of files?
+  return TargetFileSize(options);
 }
 
 static int64_t TotalFileSize(const std::vector& files) {
@@ -508,7 +517,7 @@ int Version::PickLevelForMemTableOutput(
         // Check that file does not overlap too many grandparent bytes.
         GetOverlappingInputs(level + 2, &start, &limit, &overlaps);
         const int64_t sum = TotalFileSize(overlaps);
-        if (sum > kMaxGrandParentOverlapBytes) {
+        if (sum > MaxGrandParentOverlapBytes(vset_->options_)) {
           break;
         }
       }
@@ -1027,7 +1036,7 @@ bool VersionSet::ReuseManifest(const std::string& dscname,
       manifest_type != kDescriptorFile ||
       !env_->GetFileSize(dscname, &manifest_size).ok() ||
       // Make new compacted MANIFEST if old one is too big
-      manifest_size >= kTargetFileSize) {
+      manifest_size >= TargetFileSize(options_)) {
     return false;
   }
 
@@ -1076,7 +1085,8 @@ void VersionSet::Finalize(Version* v) {
     } else {
       // Compute the ratio of current size to size limit.
       const uint64_t level_bytes = TotalFileSize(v->files_[level]);
-      score = static_cast(level_bytes) / MaxBytesForLevel(level);
+      score =
+          static_cast(level_bytes) / MaxBytesForLevel(options_, level);
     }
 
     if (score > best_score) {
@@ -1290,7 +1300,7 @@ Compaction* VersionSet::PickCompaction() {
     level = current_->compaction_level_;
     assert(level >= 0);
     assert(level+1 < config::kNumLevels);
-    c = new Compaction(level);
+    c = new Compaction(options_, level);
 
     // Pick the first file that comes after compact_pointer_[level]
     for (size_t i = 0; i < current_->files_[level].size(); i++) {
@@ -1307,7 +1317,7 @@ Compaction* VersionSet::PickCompaction() {
     }
   } else if (seek_compaction) {
     level = current_->file_to_compact_level_;
-    c = new Compaction(level);
+    c = new Compaction(options_, level);
     c->inputs_[0].push_back(current_->file_to_compact_);
   } else {
     return NULL;
@@ -1352,7 +1362,8 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
     const int64_t inputs1_size = TotalFileSize(c->inputs_[1]);
     const int64_t expanded0_size = TotalFileSize(expanded0);
     if (expanded0.size() > c->inputs_[0].size() &&
-        inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) {
+        inputs1_size + expanded0_size <
+            ExpandedCompactionByteSizeLimit(options_)) {
       InternalKey new_start, new_limit;
       GetRange(expanded0, &new_start, &new_limit);
       std::vector expanded1;
@@ -1414,7 +1425,7 @@ Compaction* VersionSet::CompactRange(
   // and we must not pick one file and drop another older file if the
   // two files overlap.
   if (level > 0) {
-    const uint64_t limit = MaxFileSizeForLevel(level);
+    const uint64_t limit = MaxFileSizeForLevel(options_, level);
     uint64_t total = 0;
     for (size_t i = 0; i < inputs.size(); i++) {
       uint64_t s = inputs[i]->file_size;
@@ -1426,7 +1437,7 @@ Compaction* VersionSet::CompactRange(
     }
   }
 
-  Compaction* c = new Compaction(level);
+  Compaction* c = new Compaction(options_, level);
   c->input_version_ = current_;
   c->input_version_->Ref();
   c->inputs_[0] = inputs;
@@ -1434,9 +1445,9 @@ Compaction* VersionSet::CompactRange(
   return c;
 }
 
-Compaction::Compaction(int level)
+Compaction::Compaction(const Options* options, int level)
     : level_(level),
-      max_output_file_size_(MaxFileSizeForLevel(level)),
+      max_output_file_size_(MaxFileSizeForLevel(options, level)),
       input_version_(NULL),
       grandparent_index_(0),
       seen_key_(false),
@@ -1453,12 +1464,13 @@ Compaction::~Compaction() {
 }
 
 bool Compaction::IsTrivialMove() const {
+  const VersionSet* vset = input_version_->vset_;
   // Avoid a move if there is lots of overlapping grandparent data.
   // Otherwise, the move could create a parent file that will require
   // a very expensive merge later on.
-  return (num_input_files(0) == 1 &&
-          num_input_files(1) == 0 &&
-          TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes);
+  return (num_input_files(0) == 1 && num_input_files(1) == 0 &&
+          TotalFileSize(grandparents_) <=
+              MaxGrandParentOverlapBytes(vset->options_));
 }
 
 void Compaction::AddInputDeletions(VersionEdit* edit) {
@@ -1491,8 +1503,9 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) {
 }
 
 bool Compaction::ShouldStopBefore(const Slice& internal_key) {
+  const VersionSet* vset = input_version_->vset_;
   // Scan to find earliest grandparent file that contains key.
-  const InternalKeyComparator* icmp = &input_version_->vset_->icmp_;
+  const InternalKeyComparator* icmp = &vset->icmp_;
   while (grandparent_index_ < grandparents_.size() &&
       icmp->Compare(internal_key,
                     grandparents_[grandparent_index_]->largest.Encode()) > 0) {
@@ -1503,7 +1516,7 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) {
   }
   seen_key_ = true;
 
-  if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) {
+  if (overlapped_bytes_ > MaxGrandParentOverlapBytes(vset->options_)) {
     // Too much overlap for current output; start new output
     overlapped_bytes_ = 0;
     return true;
diff --git a/db/version_set.h b/db/version_set.h
index 1dec745673..c4e7ac360b 100644
--- a/db/version_set.h
+++ b/db/version_set.h
@@ -366,7 +366,7 @@ class Compaction {
   friend class Version;
   friend class VersionSet;
 
-  explicit Compaction(int level);
+  Compaction(const Options* options, int level);
 
   int level_;
   uint64_t max_output_file_size_;
diff --git a/include/leveldb/options.h b/include/leveldb/options.h
index 83a1ef39a4..976e38122a 100644
--- a/include/leveldb/options.h
+++ b/include/leveldb/options.h
@@ -112,6 +112,18 @@ struct Options {
   // Default: 16
   int block_restart_interval;
 
+  // Leveldb will write up to this amount of bytes to a file before
+  // switching to a new one.
+  // Most clients should leave this parameter alone.  However if your
+  // filesystem is more efficient with larger files, you could
+  // consider increasing the value.  The downside will be longer
+  // compactions and hence longer latency/performance hiccups.
+  // Another reason to increase this parameter might be when you are
+  // initially populating a large database.
+  //
+  // Default: 2MB
+  size_t max_file_size;
+
   // Compress blocks using the specified compression algorithm.  This
   // parameter can be changed dynamically.
   //
diff --git a/util/options.cc b/util/options.cc
index 8b618fb1ae..b5e6227613 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -21,6 +21,7 @@ Options::Options()
       block_cache(NULL),
       block_size(4096),
       block_restart_interval(16),
+      max_file_size(2<<20),
       compression(kSnappyCompression),
       reuse_logs(false),
       filter_policy(NULL) {

From 7d060117fa0d5cab7cb15b0cf127533bea9ffbc7 Mon Sep 17 00:00:00 2001
From: proller 
Date: Mon, 3 Oct 2016 19:40:07 +0300
Subject: [PATCH 033/174] broken db: fix assertion in
 leveldb::InternalKey::Encode, mark base as corrupt

---
 db/dbformat.h      | 6 +++++-
 db/version_edit.cc | 3 +--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/db/dbformat.h b/db/dbformat.h
index ea897b13c0..415dc96c37 100644
--- a/db/dbformat.h
+++ b/db/dbformat.h
@@ -150,7 +150,11 @@ class InternalKey {
     AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
   }
 
-  void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
+  bool DecodeFrom(const Slice& s) {
+    rep_.assign(s.data(), s.size());
+    return !rep_.empty();
+  }
+
   Slice Encode() const {
     assert(!rep_.empty());
     return rep_;
diff --git a/db/version_edit.cc b/db/version_edit.cc
index f10a2d58b2..1eea2d1356 100644
--- a/db/version_edit.cc
+++ b/db/version_edit.cc
@@ -88,8 +88,7 @@ void VersionEdit::EncodeTo(std::string* dst) const {
 static bool GetInternalKey(Slice* input, InternalKey* dst) {
   Slice str;
   if (GetLengthPrefixedSlice(input, &str)) {
-    dst->DecodeFrom(str);
-    return true;
+    return dst->DecodeFrom(str);
   } else {
     return false;
   }

From 646c3588de84ac532a0e3525eae03edae1ea759f Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Tue, 3 Jan 2017 17:39:49 -0800
Subject: [PATCH 034/174] Limit the number of read-only files the POSIX Env
 will have open.

Background compaction can create an unbounded number of
leveldb::RandomAccessFile instances. On 64-bit systems mmap is used and
file descriptors are only used beyond a certain number of mmap's.
32-bit systems to not use mmap at all. leveldb::RandomAccessFile does not
observe Options.max_open_files so compaction could exhaust the file
descriptor limit.

This change uses getrlimit to determine the maximum number of open
files and limits RandomAccessFile to approximately 20% of that value.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=143505556
---
 util/env_posix.cc            | 192 ++++++++++++++++++++++++-----------
 util/env_posix_test_helper.h |  28 +++++
 util/env_test.cc             |  42 ++++++++
 3 files changed, 204 insertions(+), 58 deletions(-)
 create mode 100644 util/env_posix_test_helper.h

diff --git a/util/env_posix.cc b/util/env_posix.cc
index a17dec278f..342a2ff9aa 100644
--- a/util/env_posix.cc
+++ b/util/env_posix.cc
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -23,15 +24,70 @@
 #include "util/logging.h"
 #include "util/mutexlock.h"
 #include "util/posix_logger.h"
+#include "util/env_posix_test_helper.h"
 
 namespace leveldb {
 
 namespace {
 
+static int open_read_only_file_limit = -1;
+static int mmap_limit = -1;
+
 static Status IOError(const std::string& context, int err_number) {
   return Status::IOError(context, strerror(err_number));
 }
 
+// Helper class to limit resource usage to avoid exhaustion.
+// Currently used to limit read-only file descriptors and mmap file usage
+// so that we do not end up running out of file descriptors, virtual memory,
+// or running into kernel performance problems for very large databases.
+class Limiter {
+ public:
+  // Limit maximum number of resources to |n|.
+  Limiter(intptr_t n) {
+    SetAllowed(n);
+  }
+
+  // If another resource is available, acquire it and return true.
+  // Else return false.
+  bool Acquire() {
+    if (GetAllowed() <= 0) {
+      return false;
+    }
+    MutexLock l(&mu_);
+    intptr_t x = GetAllowed();
+    if (x <= 0) {
+      return false;
+    } else {
+      SetAllowed(x - 1);
+      return true;
+    }
+  }
+
+  // Release a resource acquired by a previous call to Acquire() that returned
+  // true.
+  void Release() {
+    MutexLock l(&mu_);
+    SetAllowed(GetAllowed() + 1);
+  }
+
+ private:
+  port::Mutex mu_;
+  port::AtomicPointer allowed_;
+
+  intptr_t GetAllowed() const {
+    return reinterpret_cast(allowed_.Acquire_Load());
+  }
+
+  // REQUIRES: mu_ must be held
+  void SetAllowed(intptr_t v) {
+    allowed_.Release_Store(reinterpret_cast(v));
+  }
+
+  Limiter(const Limiter&);
+  void operator=(const Limiter&);
+};
+
 class PosixSequentialFile: public SequentialFile {
  private:
   std::string filename_;
@@ -69,73 +125,51 @@ class PosixSequentialFile: public SequentialFile {
 class PosixRandomAccessFile: public RandomAccessFile {
  private:
   std::string filename_;
+  bool temporary_fd_;  // If true, fd_ is -1 and we open on every read.
   int fd_;
+  Limiter* limiter_;
 
  public:
-  PosixRandomAccessFile(const std::string& fname, int fd)
-      : filename_(fname), fd_(fd) { }
-  virtual ~PosixRandomAccessFile() { close(fd_); }
+  PosixRandomAccessFile(const std::string& fname, int fd, Limiter* limiter)
+      : filename_(fname), fd_(fd), limiter_(limiter) {
+    temporary_fd_ = !limiter->Acquire();
+    if (temporary_fd_) {
+      // Open file on every access.
+      close(fd_);
+      fd_ = -1;
+    }
+  }
+
+  virtual ~PosixRandomAccessFile() {
+    if (!temporary_fd_) {
+      close(fd_);
+      limiter_->Release();
+    }
+  }
 
   virtual Status Read(uint64_t offset, size_t n, Slice* result,
                       char* scratch) const {
+    int fd = fd_;
+    if (temporary_fd_) {
+      fd = open(filename_.c_str(), O_RDONLY);
+      if (fd < 0) {
+        return IOError(filename_, errno);
+      }
+    }
+
     Status s;
-    ssize_t r = pread(fd_, scratch, n, static_cast(offset));
+    ssize_t r = pread(fd, scratch, n, static_cast(offset));
     *result = Slice(scratch, (r < 0) ? 0 : r);
     if (r < 0) {
       // An error: return a non-ok status
       s = IOError(filename_, errno);
     }
-    return s;
-  }
-};
-
-// Helper class to limit mmap file usage so that we do not end up
-// running out virtual memory or running into kernel performance
-// problems for very large databases.
-class MmapLimiter {
- public:
-  // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes.
-  MmapLimiter() {
-    SetAllowed(sizeof(void*) >= 8 ? 1000 : 0);
-  }
-
-  // If another mmap slot is available, acquire it and return true.
-  // Else return false.
-  bool Acquire() {
-    if (GetAllowed() <= 0) {
-      return false;
-    }
-    MutexLock l(&mu_);
-    intptr_t x = GetAllowed();
-    if (x <= 0) {
-      return false;
-    } else {
-      SetAllowed(x - 1);
-      return true;
+    if (temporary_fd_) {
+      // Close the temporary file descriptor opened earlier.
+      close(fd);
     }
+    return s;
   }
-
-  // Release a slot acquired by a previous call to Acquire() that returned true.
-  void Release() {
-    MutexLock l(&mu_);
-    SetAllowed(GetAllowed() + 1);
-  }
-
- private:
-  port::Mutex mu_;
-  port::AtomicPointer allowed_;
-
-  intptr_t GetAllowed() const {
-    return reinterpret_cast(allowed_.Acquire_Load());
-  }
-
-  // REQUIRES: mu_ must be held
-  void SetAllowed(intptr_t v) {
-    allowed_.Release_Store(reinterpret_cast(v));
-  }
-
-  MmapLimiter(const MmapLimiter&);
-  void operator=(const MmapLimiter&);
 };
 
 // mmap() based random-access
@@ -144,12 +178,12 @@ class PosixMmapReadableFile: public RandomAccessFile {
   std::string filename_;
   void* mmapped_region_;
   size_t length_;
-  MmapLimiter* limiter_;
+  Limiter* limiter_;
 
  public:
   // base[0,length-1] contains the mmapped contents of the file.
   PosixMmapReadableFile(const std::string& fname, void* base, size_t length,
-                        MmapLimiter* limiter)
+                        Limiter* limiter)
       : filename_(fname), mmapped_region_(base), length_(length),
         limiter_(limiter) {
   }
@@ -332,7 +366,7 @@ class PosixEnv : public Env {
         mmap_limit_.Release();
       }
     } else {
-      *result = new PosixRandomAccessFile(fname, fd);
+      *result = new PosixRandomAccessFile(fname, fd, &fd_limit_);
     }
     return s;
   }
@@ -532,10 +566,42 @@ class PosixEnv : public Env {
   BGQueue queue_;
 
   PosixLockTable locks_;
-  MmapLimiter mmap_limit_;
+  Limiter mmap_limit_;
+  Limiter fd_limit_;
 };
 
-PosixEnv::PosixEnv() : started_bgthread_(false) {
+// Return the maximum number of concurrent mmaps.
+static int MaxMmaps() {
+  if (mmap_limit >= 0) {
+    return mmap_limit;
+  }
+  // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes.
+  mmap_limit = sizeof(void*) >= 8 ? 1000 : 0;
+  return mmap_limit;
+}
+
+// Return the maximum number of read-only files to keep open.
+static intptr_t MaxOpenFiles() {
+  if (open_read_only_file_limit >= 0) {
+    return open_read_only_file_limit;
+  }
+  struct rlimit rlim;
+  if (getrlimit(RLIMIT_NOFILE, &rlim)) {
+    // getrlimit failed, fallback to hard-coded default.
+    open_read_only_file_limit = 50;
+  } else if (rlim.rlim_cur == RLIM_INFINITY) {
+    open_read_only_file_limit = std::numeric_limits::max();
+  } else {
+    // Allow use of 20% of available file descriptors for read-only files.
+    open_read_only_file_limit = rlim.rlim_cur / 5;
+  }
+  return open_read_only_file_limit;
+}
+
+PosixEnv::PosixEnv()
+    : started_bgthread_(false),
+      mmap_limit_(MaxMmaps()),
+      fd_limit_(MaxOpenFiles()) {
   PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL));
   PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL));
 }
@@ -610,6 +676,16 @@ static pthread_once_t once = PTHREAD_ONCE_INIT;
 static Env* default_env;
 static void InitDefaultEnv() { default_env = new PosixEnv; }
 
+void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
+  assert(default_env == NULL);
+  open_read_only_file_limit = limit;
+}
+
+void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) {
+  assert(default_env == NULL);
+  mmap_limit = limit;
+}
+
 Env* Env::Default() {
   pthread_once(&once, InitDefaultEnv);
   return default_env;
diff --git a/util/env_posix_test_helper.h b/util/env_posix_test_helper.h
new file mode 100644
index 0000000000..0386960598
--- /dev/null
+++ b/util/env_posix_test_helper.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+#ifndef STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_
+#define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_
+
+namespace leveldb {
+
+class EnvPosixTest;
+
+// A helper for the POSIX Env to facilitate testing.
+class EnvPosixTestHelper {
+ private:
+  friend class EnvPosixTest;
+
+  // Set the maximum number of read-only files that will be opened.
+  // Must be called before creating an Env.
+  static void SetReadOnlyFDLimit(int limit);
+
+  // Set the maximum number of read-only files that will be mapped via mmap.
+  // Must be called before creating an Env.
+  static void SetReadOnlyMMapLimit(int limit);
+};
+
+}  // namespace leveldb
+
+#endif  // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_
diff --git a/util/env_test.cc b/util/env_test.cc
index b72cb44384..0cb1577bc3 100644
--- a/util/env_test.cc
+++ b/util/env_test.cc
@@ -6,10 +6,13 @@
 
 #include "port/port.h"
 #include "util/testharness.h"
+#include "util/env_posix_test_helper.h"
 
 namespace leveldb {
 
 static const int kDelayMicros = 100000;
+static const int kReadOnlyFileLimit = 4;
+static const int kMMapLimit = 4;
 
 class EnvPosixTest {
  private:
@@ -19,6 +22,11 @@ class EnvPosixTest {
  public:
   Env* env_;
   EnvPosixTest() : env_(Env::Default()) { }
+
+  static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
+    EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
+    EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);
+  }
 };
 
 static void SetBool(void* ptr) {
@@ -97,8 +105,42 @@ TEST(EnvPosixTest, StartThread) {
   ASSERT_EQ(state.val, 3);
 }
 
+TEST(EnvPosixTest, TestOpenOnRead) {
+  // Write some test data to a single file that will be opened |n| times.
+  std::string test_dir;
+  ASSERT_OK(Env::Default()->GetTestDirectory(&test_dir));
+  std::string test_file = test_dir + "/open_on_read.txt";
+
+  FILE* f = fopen(test_file.c_str(), "w");
+  ASSERT_TRUE(f != NULL);
+  const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
+  fputs(kFileData, f);
+  fclose(f);
+
+  // Open test file some number above the sum of the two limits to force
+  // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
+  const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
+  leveldb::RandomAccessFile* files[kNumFiles] = {0};
+  for (int i = 0; i < kNumFiles; i++) {
+    ASSERT_OK(Env::Default()->NewRandomAccessFile(test_file, &files[i]));
+  }
+  char scratch;
+  Slice read_result;
+  for (int i = 0; i < kNumFiles; i++) {
+    ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch));
+    ASSERT_EQ(kFileData[i], read_result[0]);
+  }
+  for (int i = 0; i < kNumFiles; i++) {
+    delete files[i];
+  }
+  ASSERT_OK(Env::Default()->DeleteFile(test_file));
+}
+
 }  // namespace leveldb
 
 int main(int argc, char** argv) {
+  // All tests currently run with the same read-only file limits.
+  leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
+                                       leveldb::kMMapLimit);
   return leveldb::test::RunAllTests();
 }

From 95cd743e5e71c7b06e7149a837e33b91309dfa48 Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Tue, 7 Feb 2017 15:17:04 -0800
Subject: [PATCH 035/174] Including  for std::numeric_limits.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=146841327
---
 util/env_posix.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/util/env_posix.cc b/util/env_posix.cc
index 342a2ff9aa..84aabb20a7 100644
--- a/util/env_posix.cc
+++ b/util/env_posix.cc
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include "leveldb/env.h"
 #include "leveldb/slice.h"

From ea175e28f8ef7f6a8f5931ebad1835d95ec466ed Mon Sep 17 00:00:00 2001
From: costan 
Date: Mon, 27 Feb 2017 14:29:18 -0800
Subject: [PATCH 036/174] Implement support for Intel crc32 instruction (SSE
 4.2)

This change authored by vadimskipin and submitted via:

    https://github.com/google/leveldb/pull/309

Changes made to support iOS builds and other architectures
without support for SSE 4.2.

db_bench reports original crc32 speed at:

    crc32c : 3.610 micros/op; 1082.0 MB/s (4K per op)

with this change performance has increased to:

    crc32c : 0.843 micros/op; 4633.6 MB/s (4K per op)

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148694935
---
 Makefile               |   6 ++
 build_detect_platform  |  30 +++++++++-
 port/port_example.h    |   6 ++
 port/port_posix.h      |   2 +
 port/port_posix_sse.cc | 125 +++++++++++++++++++++++++++++++++++++++++
 util/crc32c.cc         |  18 ++++++
 6 files changed, 186 insertions(+), 1 deletion(-)
 create mode 100644 port/port_posix_sse.cc

diff --git a/Makefile b/Makefile
index 07a5a1ead6..66e32261f6 100644
--- a/Makefile
+++ b/Makefile
@@ -412,3 +412,9 @@ $(SHARED_OUTDIR)/%.o: %.cc
 
 $(SHARED_OUTDIR)/%.o: %.c
 	$(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
+
+$(STATIC_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc
+	$(CXX) $(CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@
+
+$(SHARED_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc
+	$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@
diff --git a/build_detect_platform b/build_detect_platform
index f0629939ca..d2a20ce5b6 100755
--- a/build_detect_platform
+++ b/build_detect_platform
@@ -63,6 +63,7 @@ PLATFORM_SHARED_EXT="so"
 PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl,"
 PLATFORM_SHARED_CFLAGS="-fPIC"
 PLATFORM_SHARED_VERSIONED=true
+PLATFORM_SSEFLAGS=
 
 MEMCMP_FLAG=
 if [ "$CXX" = "g++" ]; then
@@ -77,6 +78,7 @@ case "$TARGET_OS" in
         COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN"
         PLATFORM_LDFLAGS="-lpthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     Darwin)
         PLATFORM=OS_MACOSX
@@ -85,48 +87,56 @@ case "$TARGET_OS" in
         [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd`
         PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     Linux)
         PLATFORM=OS_LINUX
         COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX"
         PLATFORM_LDFLAGS="-pthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     SunOS)
         PLATFORM=OS_SOLARIS
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS"
         PLATFORM_LIBS="-lpthread -lrt"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     FreeBSD)
         PLATFORM=OS_FREEBSD
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD"
         PLATFORM_LIBS="-lpthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     NetBSD)
         PLATFORM=OS_NETBSD
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD"
         PLATFORM_LIBS="-lpthread -lgcc_s"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     OpenBSD)
         PLATFORM=OS_OPENBSD
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD"
         PLATFORM_LDFLAGS="-pthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     DragonFly)
         PLATFORM=OS_DRAGONFLYBSD
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD"
         PLATFORM_LIBS="-lpthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         ;;
     OS_ANDROID_CROSSCOMPILE)
         PLATFORM=OS_ANDROID
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
         PLATFORM_LDFLAGS=""  # All pthread features are in the Android C library
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         CROSS_COMPILE=true
         ;;
     HP-UX)
@@ -134,6 +144,7 @@ case "$TARGET_OS" in
         COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX"
         PLATFORM_LDFLAGS="-pthread"
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         # man ld: +h internal_name
         PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl,"
         ;;
@@ -142,6 +153,7 @@ case "$TARGET_OS" in
         COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX"
         [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd`
         PORT_FILE=port/port_posix.cc
+        PORT_SSE_FILE=port/port_posix_sse.cc
         PLATFORM_SHARED_EXT=
         PLATFORM_SHARED_LDFLAGS=
         PLATFORM_SHARED_CFLAGS=
@@ -168,7 +180,7 @@ set +f # re-enable globbing
 
 # The sources consist of the portable files, plus the platform-specific port
 # file.
-echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT
+echo "SOURCES=$PORTABLE_FILES $PORT_FILE $PORT_SSE_FILE" >> $OUTPUT
 echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
 
 if [ "$CROSS_COMPILE" = "true" ]; then
@@ -210,6 +222,21 @@ EOF
     fi
 
     rm -f $CXXOUTPUT 2>/dev/null
+
+    # Test if gcc SSE 4.2 is supported
+    $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -msse4.2 2>/dev/null  </dev/null
+fi
+
+# Use the SSE 4.2 CRC32C intrinsics iff runtime checks indicate compiler supports them.
+if [ -n "$PLATFORM_SSEFLAGS" ]; then
+    PLATFORM_SSEFLAGS="$PLATFORM_SSEFLAGS -DLEVELDB_PLATFORM_POSIX_SSE"
 fi
 
 PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
@@ -222,6 +249,7 @@ echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT
 echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT
 echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT
 echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT
+echo "PLATFORM_SSEFLAGS=$PLATFORM_SSEFLAGS" >> $OUTPUT
 echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT
 echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT
 echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT
diff --git a/port/port_example.h b/port/port_example.h
index ab9e489b32..97bd669a5e 100644
--- a/port/port_example.h
+++ b/port/port_example.h
@@ -129,6 +129,12 @@ extern bool Snappy_Uncompress(const char* input_data, size_t input_length,
 // The concatenation of all "data[0,n-1]" fragments is the heap profile.
 extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg);
 
+// Extend the CRC to include the first n bytes of buf.
+//
+// Returns zero if the CRC cannot be extended using acceleration, else returns
+// the newly extended CRC value (which may also be zero).
+uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size);
+
 }  // namespace port
 }  // namespace leveldb
 
diff --git a/port/port_posix.h b/port/port_posix.h
index 89fc222c04..d67ab68c27 100644
--- a/port/port_posix.h
+++ b/port/port_posix.h
@@ -148,6 +148,8 @@ inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {
   return false;
 }
 
+uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size);
+
 } // namespace port
 } // namespace leveldb
 
diff --git a/port/port_posix_sse.cc b/port/port_posix_sse.cc
new file mode 100644
index 0000000000..57ec8fee7e
--- /dev/null
+++ b/port/port_posix_sse.cc
@@ -0,0 +1,125 @@
+// Copyright 2016 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+//
+// A portable implementation of crc32c, optimized to handle
+// four bytes at a time.
+//
+// In a separate source file to allow this accelerated CRC32C function to be
+// compiled with the appropriate compiler flags to enable x86 SSE 4.2
+// instructions.
+
+#include 
+#include 
+#include "port/port.h"
+
+#if defined(LEVELDB_PLATFORM_POSIX_SSE)
+
+#if defined(_MSC_VER)
+#include 
+#elif defined(__GNUC__) && defined(__SSE4_2__)
+#include 
+#include 
+#endif
+
+#endif  // defined(LEVELDB_PLATFORM_POSIX_SSE)
+
+namespace leveldb {
+namespace port {
+
+#if defined(LEVELDB_PLATFORM_POSIX_SSE)
+
+// Used to fetch a naturally-aligned 32-bit word in little endian byte-order
+static inline uint32_t LE_LOAD32(const uint8_t *p) {
+  // SSE is x86 only, so ensured that |p| is always little-endian.
+  uint32_t word;
+  memcpy(&word, p, sizeof(word));
+  return word;
+}
+
+// Used to fetch a naturally-aligned 64-bit word in little endian byte-order
+static inline uint64_t LE_LOAD64(const uint8_t *p) {
+  uint64_t dword;
+  memcpy(&dword, p, sizeof(dword));
+  return dword;
+}
+
+static inline bool HaveSSE42() {
+#if defined(_MSC_VER)
+  int cpu_info[4];
+  __cpuid(cpu_info, 1);
+  return (cpu_info[2] & (1 << 20)) != 0;
+#elif defined(__GNUC__)
+  unsigned int eax, ebx, ecx, edx;
+  __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+  return (ecx & (1 << 20)) != 0;
+#else
+  return false;
+#endif
+}
+
+#endif  // defined(LEVELDB_PLATFORM_POSIX_SSE)
+
+// For further improvements see Intel publication at:
+// http://download.intel.com/design/intarch/papers/323405.pdf
+uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) {
+#if !defined(LEVELDB_PLATFORM_POSIX_SSE)
+  return 0;
+#else
+  static bool have = HaveSSE42();
+  if (!have) {
+    return 0;
+  }
+
+  const uint8_t *p = reinterpret_cast(buf);
+  const uint8_t *e = p + size;
+  uint32_t l = crc ^ 0xffffffffu;
+
+#define STEP1 do {                              \
+    l = _mm_crc32_u8(l, *p++);                  \
+} while (0)
+#define STEP4 do {                              \
+    l = _mm_crc32_u32(l, LE_LOAD32(p));         \
+    p += 4;                                     \
+} while (0)
+#define STEP8 do {                              \
+    l = _mm_crc32_u64(l, LE_LOAD64(p));         \
+    p += 8;                                     \
+} while (0)
+
+  if (size > 16) {
+    // Process unaligned bytes
+    for (unsigned int i = reinterpret_cast(p) % 8; i; --i) {
+      STEP1;
+    }
+
+    // _mm_crc32_u64 is only available on x64.
+#if defined(_M_X64) || defined(__x86_64__)
+    // Process 8 bytes at a time
+    while ((e-p) >= 8) {
+      STEP8;
+    }
+    // Process 4 bytes at a time
+    if ((e-p) >= 4) {
+      STEP4;
+    }
+#else  // !(defined(_M_X64) || defined(__x86_64__))
+    // Process 4 bytes at a time
+    while ((e-p) >= 4) {
+      STEP4;
+    }
+#endif  // defined(_M_X64) || defined(__x86_64__)
+  }
+  // Process the last few bytes
+  while (p != e) {
+    STEP1;
+  }
+#undef STEP8
+#undef STEP4
+#undef STEP1
+  return l ^ 0xffffffffu;
+#endif  // defined(LEVELDB_PLATFORM_POSIX_SSE)
+}
+
+}  // namespace port
+}  // namespace leveldb
diff --git a/util/crc32c.cc b/util/crc32c.cc
index 6db9e77077..edd61cfd6f 100644
--- a/util/crc32c.cc
+++ b/util/crc32c.cc
@@ -8,6 +8,8 @@
 #include "util/crc32c.h"
 
 #include 
+
+#include "port/port.h"
 #include "util/coding.h"
 
 namespace leveldb {
@@ -283,7 +285,23 @@ static inline uint32_t LE_LOAD32(const uint8_t *p) {
   return DecodeFixed32(reinterpret_cast(p));
 }
 
+// Determine if the CPU running this program can accelerate the CRC32C
+// calculation.
+static bool CanAccelerateCRC32C() {
+  // port::AcceleretedCRC32C returns zero when unable to accelerate.
+  static const char kTestCRCBuffer[] = "TestCRCBuffer";
+  static const char kBufSize = sizeof(kTestCRCBuffer) - 1;
+  static const uint32_t kTestCRCValue = 0xdcbc59fa;
+
+  return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue;
+}
+
 uint32_t Extend(uint32_t crc, const char* buf, size_t size) {
+  static bool accelerate = CanAccelerateCRC32C();
+  if (accelerate) {
+    return port::AcceleratedCRC32C(crc, buf, size);
+  }
+
   const uint8_t *p = reinterpret_cast(buf);
   const uint8_t *e = p + size;
   uint32_t l = crc ^ 0xffffffffu;

From 7fa20948d5a92d0e4b62486ed49e82814fa7bd6d Mon Sep 17 00:00:00 2001
From: cmumford 
Date: Tue, 28 Feb 2017 16:22:17 -0800
Subject: [PATCH 037/174] Convert documentation to markdown.

Markdown is more readable in a text editor and when hosted
on GitHub is more readable than HTML.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148830423
---
 README.md             |  25 +-
 db/log_format.h       |   2 +-
 doc/doc.css           |  89 -------
 doc/impl.html         | 213 ----------------
 doc/impl.md           | 170 +++++++++++++
 doc/index.html        | 549 ------------------------------------------
 doc/index.md          | 523 ++++++++++++++++++++++++++++++++++++++++
 doc/log_format.md     |  75 ++++++
 doc/log_format.txt    |  75 ------
 doc/table_format.md   | 107 ++++++++
 doc/table_format.txt  | 104 --------
 table/filter_block.cc |   2 +-
 12 files changed, 890 insertions(+), 1044 deletions(-)
 delete mode 100644 doc/doc.css
 delete mode 100644 doc/impl.html
 create mode 100644 doc/impl.md
 delete mode 100644 doc/index.html
 create mode 100644 doc/index.md
 create mode 100644 doc/log_format.md
 delete mode 100644 doc/log_format.txt
 create mode 100644 doc/table_format.md
 delete mode 100644 doc/table_format.txt

diff --git a/README.md b/README.md
index c75b185e0e..d600c5f45d 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
   * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.
 
 # Documentation
-  [LevelDB library documentation](https://rawgit.com/google/leveldb/master/doc/index.html) is online and bundled with the source code.
+  [LevelDB library documentation](https://rawgit.com/google/leveldb/master/doc/index.md) is online and bundled with the source code.
 
 
 # Limitations
@@ -113,29 +113,30 @@ by the one or two disk seeks needed to fetch the data from disk.
 Write performance will be mostly unaffected by whether or not the
 working set fits in memory.
 
-    readrandom   :      16.677 micros/op;  (approximately 60,000 reads per second)
-    readseq      :       0.476 micros/op;  232.3 MB/s
-    readreverse  :       0.724 micros/op;  152.9 MB/s
+    readrandom  : 16.677 micros/op;  (approximately 60,000 reads per second)
+    readseq     :  0.476 micros/op;  232.3 MB/s
+    readreverse :  0.724 micros/op;  152.9 MB/s
 
 LevelDB compacts its underlying storage data in the background to
 improve read performance.  The results listed above were done
 immediately after a lot of random writes.  The results after
 compactions (which are usually triggered automatically) are better.
 
-    readrandom   :      11.602 micros/op;  (approximately 85,000 reads per second)
-    readseq      :       0.423 micros/op;  261.8 MB/s
-    readreverse  :       0.663 micros/op;  166.9 MB/s
+    readrandom  : 11.602 micros/op;  (approximately 85,000 reads per second)
+    readseq     :  0.423 micros/op;  261.8 MB/s
+    readreverse :  0.663 micros/op;  166.9 MB/s
 
 Some of the high cost of reads comes from repeated decompression of blocks
 read from disk.  If we supply enough cache to the leveldb so it can hold the
 uncompressed blocks in memory, the read performance improves again:
 
-    readrandom   :       9.775 micros/op;  (approximately 100,000 reads per second before compaction)
-    readrandom   :       5.215 micros/op;  (approximately 190,000 reads per second after compaction)
+    readrandom  : 9.775 micros/op;  (approximately 100,000 reads per second before compaction)
+    readrandom  : 5.215 micros/op;  (approximately 190,000 reads per second after compaction)
 
 ## Repository contents
 
-See doc/index.html for more explanation. See doc/impl.html for a brief overview of the implementation.
+See [doc/index.md](doc/index.md) for more explanation. See
+[doc/impl.md](doc/impl.md) for a brief overview of the implementation.
 
 The public interface is in include/*.h.  Callers should not include or
 rely on the details of any other header files in this package.  Those
@@ -148,7 +149,7 @@ Guide to header files:
 * **include/options.h**: Control over the behavior of an entire database,
 and also control over the behavior of individual reads and writes.
 
-* **include/comparator.h**: Abstraction for user-specified comparison function. 
+* **include/comparator.h**: Abstraction for user-specified comparison function.
 If you want just bytewise comparison of keys, you can use the default
 comparator, but clients can write their own comparator implementations if they
 want custom ordering (e.g. to handle different character encodings, etc.)
@@ -165,7 +166,7 @@ length into some other byte array.
 * **include/status.h**: Status is returned from many of the public interfaces
 and is used to report success and various kinds of errors.
 
-* **include/env.h**: 
+* **include/env.h**:
 Abstraction of the OS environment.  A posix implementation of this interface is
 in util/env_posix.cc
 
diff --git a/db/log_format.h b/db/log_format.h
index a8c06efe18..356e69fca2 100644
--- a/db/log_format.h
+++ b/db/log_format.h
@@ -3,7 +3,7 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 //
 // Log format information shared by reader and writer.
-// See ../doc/log_format.txt for more detail.
+// See ../doc/log_format.md for more detail.
 
 #ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_
 #define STORAGE_LEVELDB_DB_LOG_FORMAT_H_
diff --git a/doc/doc.css b/doc/doc.css
deleted file mode 100644
index 700c564e43..0000000000
--- a/doc/doc.css
+++ /dev/null
@@ -1,89 +0,0 @@
-body {
-  margin-left: 0.5in;
-  margin-right: 0.5in;
-  background: white;
-  color: black;
-}
-
-h1 {
-  margin-left: -0.2in;
-  font-size: 14pt;
-}
-h2 {
-  margin-left: -0in;
-  font-size: 12pt;
-}
-h3 {
-  margin-left: -0in;
-}
-h4 {
-  margin-left: -0in;
-}
-hr {
-  margin-left: -0in;
-}
-
-/* Definition lists: definition term bold */
-dt {
-  font-weight: bold;
-}
-
-address {
-  text-align: center;
-}
-code,samp,var {
-  color: blue;
-}
-kbd {
-  color: #600000;
-}
-div.note p {
-  float: right;
-  width: 3in;
-  margin-right: 0%;
-  padding: 1px;
-  border: 2px solid #6060a0;
-  background-color: #fffff0;
-}
-
-ul {
-  margin-top: -0em;
-  margin-bottom: -0em;
-}
-
-ol {
-  margin-top: -0em;
-  margin-bottom: -0em;
-}
-
-UL.nobullets {
-  list-style-type: none;
-  list-style-image: none;
-  margin-left: -1em;
-}
-
-p {
-  margin: 1em 0 1em 0;
-  padding: 0 0 0 0;
-}
-
-pre {
-  line-height: 1.3em;
-  padding: 0.4em 0 0.8em 0;
-  margin:  0 0 0 0;
-  border:  0 0 0 0;
-  color: blue;
-}
-
-.datatable {
-  margin-left: auto;
-  margin-right: auto;
-  margin-top: 2em;
-  margin-bottom: 2em;
-  border: 1px solid;
-}
-
-.datatable td,th {
-  padding: 0 0.5em 0 0.5em;
-  text-align: right;
-}
diff --git a/doc/impl.html b/doc/impl.html
deleted file mode 100644
index 6a468be095..0000000000
--- a/doc/impl.html
+++ /dev/null
@@ -1,213 +0,0 @@
-
-
-
-
-Leveldb file layout and compactions
-
-
-
-
-

Files

- -The implementation of leveldb is similar in spirit to the -representation of a single - -Bigtable tablet (section 5.3). -However the organization of the files that make up the representation -is somewhat different and is explained below. - -

-Each database is represented by a set of files stored in a directory. -There are several different types of files as documented below: -

-

Log files

-

-A log file (*.log) stores a sequence of recent updates. Each update -is appended to the current log file. When the log file reaches a -pre-determined size (approximately 4MB by default), it is converted -to a sorted table (see below) and a new log file is created for future -updates. -

-A copy of the current log file is kept in an in-memory structure (the -memtable). This copy is consulted on every read so that read -operations reflect all logged updates. -

-

Sorted tables

-

-A sorted table (*.sst) stores a sequence of entries sorted by key. -Each entry is either a value for the key, or a deletion marker for the -key. (Deletion markers are kept around to hide obsolete values -present in older sorted tables). -

-The set of sorted tables are organized into a sequence of levels. The -sorted table generated from a log file is placed in a special young -level (also called level-0). When the number of young files exceeds a -certain threshold (currently four), all of the young files are merged -together with all of the overlapping level-1 files to produce a -sequence of new level-1 files (we create a new level-1 file for every -2MB of data.) -

-Files in the young level may contain overlapping keys. However files -in other levels have distinct non-overlapping key ranges. Consider -level number L where L >= 1. When the combined size of files in -level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, -...), one file in level-L, and all of the overlapping files in -level-(L+1) are merged to form a set of new files for level-(L+1). -These merges have the effect of gradually migrating new updates from -the young level to the largest level using only bulk reads and writes -(i.e., minimizing expensive seeks). - -

Manifest

-

-A MANIFEST file lists the set of sorted tables that make up each -level, the corresponding key ranges, and other important metadata. -A new MANIFEST file (with a new number embedded in the file name) -is created whenever the database is reopened. The MANIFEST file is -formatted as a log, and changes made to the serving state (as files -are added or removed) are appended to this log. -

-

Current

-

-CURRENT is a simple text file that contains the name of the latest -MANIFEST file. -

-

Info logs

-

-Informational messages are printed to files named LOG and LOG.old. -

-

Others

-

-Other files used for miscellaneous purposes may also be present -(LOCK, *.dbtmp). - -

Level 0

-When the log file grows above a certain size (1MB by default): -
    -
  • Create a brand new memtable and log file and direct future updates here -
  • In the background: -
      -
    • Write the contents of the previous memtable to an sstable -
    • Discard the memtable -
    • Delete the old log file and the old memtable -
    • Add the new sstable to the young (level-0) level. -
    -
- -

Compactions

- -

-When the size of level L exceeds its limit, we compact it in a -background thread. The compaction picks a file from level L and all -overlapping files from the next level L+1. Note that if a level-L -file overlaps only part of a level-(L+1) file, the entire file at -level-(L+1) is used as an input to the compaction and will be -discarded after the compaction. Aside: because level-0 is special -(files in it may overlap each other), we treat compactions from -level-0 to level-1 specially: a level-0 compaction may pick more than -one level-0 file in case some of these files overlap each other. - -

-A compaction merges the contents of the picked files to produce a -sequence of level-(L+1) files. We switch to producing a new -level-(L+1) file after the current output file has reached the target -file size (2MB). We also switch to a new output file when the key -range of the current output file has grown enough to overlap more than -ten level-(L+2) files. This last rule ensures that a later compaction -of a level-(L+1) file will not pick up too much data from level-(L+2). - -

-The old files are discarded and the new files are added to the serving -state. - -

-Compactions for a particular level rotate through the key space. In -more detail, for each level L, we remember the ending key of the last -compaction at level L. The next compaction for level L will pick the -first file that starts after this key (wrapping around to the -beginning of the key space if there is no such file). - -

-Compactions drop overwritten values. They also drop deletion markers -if there are no higher numbered levels that contain a file whose range -overlaps the current key. - -

Timing

- -Level-0 compactions will read up to four 1MB files from level-0, and -at worst all the level-1 files (10MB). I.e., we will read 14MB and -write 14MB. - -

-Other than the special level-0 compactions, we will pick one 2MB file -from level L. In the worst case, this will overlap ~ 12 files from -level L+1 (10 because level-(L+1) is ten times the size of level-L, -and another two at the boundaries since the file ranges at level-L -will usually not be aligned with the file ranges at level-L+1). The -compaction will therefore read 26MB and write 26MB. Assuming a disk -IO rate of 100MB/s (ballpark range for modern drives), the worst -compaction cost will be approximately 0.5 second. - -

-If we throttle the background writing to something small, say 10% of -the full 100MB/s speed, a compaction may take up to 5 seconds. If the -user is writing at 10MB/s, we might build up lots of level-0 files -(~50 to hold the 5*10MB). This may significantly increase the cost of -reads due to the overhead of merging more files together on every -read. - -

-Solution 1: To reduce this problem, we might want to increase the log -switching threshold when the number of level-0 files is large. Though -the downside is that the larger this threshold, the more memory we will -need to hold the corresponding memtable. - -

-Solution 2: We might want to decrease write rate artificially when the -number of level-0 files goes up. - -

-Solution 3: We work on reducing the cost of very wide merges. -Perhaps most of the level-0 files will have their blocks sitting -uncompressed in the cache and we will only need to worry about the -O(N) complexity in the merging iterator. - -

Number of files

- -Instead of always making 2MB files, we could make larger files for -larger levels to reduce the total file count, though at the expense of -more bursty compactions. Alternatively, we could shard the set of -files into multiple directories. - -

-An experiment on an ext3 filesystem on Feb 04, 2011 shows -the following timings to do 100K file opens in directories with -varying number of files: - - - - - -
Files in directoryMicroseconds to open a file
10009
1000010
10000016
-So maybe even the sharding is not necessary on modern filesystems? - -

Recovery

- -
    -
  • Read CURRENT to find name of the latest committed MANIFEST -
  • Read the named MANIFEST file -
  • Clean up stale files -
  • We could open all sstables here, but it is probably better to be lazy... -
  • Convert log chunk to a new level-0 sstable -
  • Start directing new writes to a new log file with recovered sequence# -
- -

Garbage collection of files

- -DeleteObsoleteFiles() is called at the end of every -compaction and at the end of recovery. It finds the names of all -files in the database. It deletes all log files that are not the -current log file. It deletes all table files that are not referenced -from some level and are not the output of an active compaction. - - - diff --git a/doc/impl.md b/doc/impl.md new file mode 100644 index 0000000000..4b13f2a6ba --- /dev/null +++ b/doc/impl.md @@ -0,0 +1,170 @@ +## Files + +The implementation of leveldb is similar in spirit to the representation of a +single [Bigtable tablet (section 5.3)](http://research.google.com/archive/bigtable.html). +However the organization of the files that make up the representation is +somewhat different and is explained below. + +Each database is represented by a set of files stored in a directory. There are +several different types of files as documented below: + +### Log files + +A log file (*.log) stores a sequence of recent updates. Each update is appended +to the current log file. When the log file reaches a pre-determined size +(approximately 4MB by default), it is converted to a sorted table (see below) +and a new log file is created for future updates. + +A copy of the current log file is kept in an in-memory structure (the +`memtable`). This copy is consulted on every read so that read operations +reflect all logged updates. + +## Sorted tables + +A sorted table (*.ldb) stores a sequence of entries sorted by key. Each entry is +either a value for the key, or a deletion marker for the key. (Deletion markers +are kept around to hide obsolete values present in older sorted tables). + +The set of sorted tables are organized into a sequence of levels. The sorted +table generated from a log file is placed in a special **young** level (also +called level-0). When the number of young files exceeds a certain threshold +(currently four), all of the young files are merged together with all of the +overlapping level-1 files to produce a sequence of new level-1 files (we create +a new level-1 file for every 2MB of data.) + +Files in the young level may contain overlapping keys. However files in other +levels have distinct non-overlapping key ranges. Consider level number L where +L >= 1. When the combined size of files in level-L exceeds (10^L) MB (i.e., 10MB +for level-1, 100MB for level-2, ...), one file in level-L, and all of the +overlapping files in level-(L+1) are merged to form a set of new files for +level-(L+1). These merges have the effect of gradually migrating new updates +from the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +### Manifest + +A MANIFEST file lists the set of sorted tables that make up each level, the +corresponding key ranges, and other important metadata. A new MANIFEST file +(with a new number embedded in the file name) is created whenever the database +is reopened. The MANIFEST file is formatted as a log, and changes made to the +serving state (as files are added or removed) are appended to this log. + +### Current + +CURRENT is a simple text file that contains the name of the latest MANIFEST +file. + +### Info logs + +Informational messages are printed to files named LOG and LOG.old. + +### Others + +Other files used for miscellaneous purposes may also be present (LOCK, *.dbtmp). + +## Level 0 + +When the log file grows above a certain size (1MB by default): +Create a brand new memtable and log file and direct future updates here +In the background: +Write the contents of the previous memtable to an sstable +Discard the memtable +Delete the old log file and the old memtable +Add the new sstable to the young (level-0) level. + +## Compactions + +When the size of level L exceeds its limit, we compact it in a background +thread. The compaction picks a file from level L and all overlapping files from +the next level L+1. Note that if a level-L file overlaps only part of a +level-(L+1) file, the entire file at level-(L+1) is used as an input to the +compaction and will be discarded after the compaction. Aside: because level-0 +is special (files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than one +level-0 file in case some of these files overlap each other. + +A compaction merges the contents of the picked files to produce a sequence of +level-(L+1) files. We switch to producing a new level-(L+1) file after the +current output file has reached the target file size (2MB). We also switch to a +new output file when the key range of the current output file has grown enough +to overlap more than ten level-(L+2) files. This last rule ensures that a later +compaction of a level-(L+1) file will not pick up too much data from +level-(L+2). + +The old files are discarded and the new files are added to the serving state. + +Compactions for a particular level rotate through the key space. In more detail, +for each level L, we remember the ending key of the last compaction at level L. +The next compaction for level L will pick the first file that starts after this +key (wrapping around to the beginning of the key space if there is no such +file). + +Compactions drop overwritten values. They also drop deletion markers if there +are no higher numbered levels that contain a file whose range overlaps the +current key. + +### Timing + +Level-0 compactions will read up to four 1MB files from level-0, and at worst +all the level-1 files (10MB). I.e., we will read 14MB and write 14MB. + +Other than the special level-0 compactions, we will pick one 2MB file from level +L. In the worst case, this will overlap ~ 12 files from level L+1 (10 because +level-(L+1) is ten times the size of level-L, and another two at the boundaries +since the file ranges at level-L will usually not be aligned with the file +ranges at level-L+1). The compaction will therefore read 26MB and write 26MB. +Assuming a disk IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +If we throttle the background writing to something small, say 10% of the full +100MB/s speed, a compaction may take up to 5 seconds. If the user is writing at +10MB/s, we might build up lots of level-0 files (~50 to hold the 5*10MB). This +may significantly increase the cost of reads due to the overhead of merging more +files together on every read. + +Solution 1: To reduce this problem, we might want to increase the log switching +threshold when the number of level-0 files is large. Though the downside is that +the larger this threshold, the more memory we will need to hold the +corresponding memtable. + +Solution 2: We might want to decrease write rate artificially when the number of +level-0 files goes up. + +Solution 3: We work on reducing the cost of very wide merges. Perhaps most of +the level-0 files will have their blocks sitting uncompressed in the cache and +we will only need to worry about the O(N) complexity in the merging iterator. + +### Number of files + +Instead of always making 2MB files, we could make larger files for larger levels +to reduce the total file count, though at the expense of more bursty +compactions. Alternatively, we could shard the set of files into multiple +directories. + +An experiment on an ext3 filesystem on Feb 04, 2011 shows the following timings +to do 100K file opens in directories with varying number of files: + + +| Files in directory | Microseconds to open a file | +|-------------------:|----------------------------:| +| 1000 | 9 | +| 10000 | 10 | +| 100000 | 16 | + +So maybe even the sharding is not necessary on modern filesystems? + +## Recovery + +* Read CURRENT to find name of the latest committed MANIFEST +* Read the named MANIFEST file +* Clean up stale files +* We could open all sstables here, but it is probably better to be lazy... +* Convert log chunk to a new level-0 sstable +* Start directing new writes to a new log file with recovered sequence# + +## Garbage collection of files + +`DeleteObsoleteFiles()` is called at the end of every compaction and at the end +of recovery. It finds the names of all files in the database. It deletes all log +files that are not the current log file. It deletes all table files that are not +referenced from some level and are not the output of an active compaction. diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index 2155192581..0000000000 --- a/doc/index.html +++ /dev/null @@ -1,549 +0,0 @@ - - - - -Leveldb - - - -

Leveldb

-
Jeff Dean, Sanjay Ghemawat
-

-The leveldb library provides a persistent key value store. Keys and -values are arbitrary byte arrays. The keys are ordered within the key -value store according to a user-specified comparator function. - -

-

Opening A Database

-

-A leveldb database has a name which corresponds to a file system -directory. All of the contents of database are stored in this -directory. The following example shows how to open a database, -creating it if necessary: -

-

-  #include <cassert>
-  #include "leveldb/db.h"
-
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  assert(status.ok());
-  ...
-
-If you want to raise an error if the database already exists, add -the following line before the leveldb::DB::Open call: -
-  options.error_if_exists = true;
-
-

Status

-

-You may have noticed the leveldb::Status type above. Values of this -type are returned by most functions in leveldb that may encounter an -error. You can check if such a result is ok, and also print an -associated error message: -

-

-   leveldb::Status s = ...;
-   if (!s.ok()) cerr << s.ToString() << endl;
-
-

Closing A Database

-

-When you are done with a database, just delete the database object. -Example: -

-

-  ... open the db as described above ...
-  ... do something with db ...
-  delete db;
-
-

Reads And Writes

-

-The database provides Put, Delete, and Get methods to -modify/query the database. For example, the following code -moves the value stored under key1 to key2. -

-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
-  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
-
- -

Atomic Updates

-

-Note that if the process dies after the Put of key2 but before the -delete of key1, the same value may be left stored under multiple keys. -Such problems can be avoided by using the WriteBatch class to -atomically apply a set of updates: -

-

-  #include "leveldb/write_batch.h"
-  ...
-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) {
-    leveldb::WriteBatch batch;
-    batch.Delete(key1);
-    batch.Put(key2, value);
-    s = db->Write(leveldb::WriteOptions(), &batch);
-  }
-
-The WriteBatch holds a sequence of edits to be made to the database, -and these edits within the batch are applied in order. Note that we -called Delete before Put so that if key1 is identical to key2, -we do not end up erroneously dropping the value entirely. -

-Apart from its atomicity benefits, WriteBatch may also be used to -speed up bulk updates by placing lots of individual mutations into the -same batch. - -

Synchronous Writes

-By default, each write to leveldb is asynchronous: it -returns after pushing the write from the process into the operating -system. The transfer from operating system memory to the underlying -persistent storage happens asynchronously. The sync flag -can be turned on for a particular write to make the write operation -not return until the data being written has been pushed all the way to -persistent storage. (On Posix systems, this is implemented by calling -either fsync(...) or fdatasync(...) or -msync(..., MS_SYNC) before the write operation returns.) -
-  leveldb::WriteOptions write_options;
-  write_options.sync = true;
-  db->Put(write_options, ...);
-
-Asynchronous writes are often more than a thousand times as fast as -synchronous writes. The downside of asynchronous writes is that a -crash of the machine may cause the last few updates to be lost. Note -that a crash of just the writing process (i.e., not a reboot) will not -cause any loss since even when sync is false, an update -is pushed from the process memory into the operating system before it -is considered done. - -

-Asynchronous writes can often be used safely. For example, when -loading a large amount of data into the database you can handle lost -updates by restarting the bulk load after a crash. A hybrid scheme is -also possible where every Nth write is synchronous, and in the event -of a crash, the bulk load is restarted just after the last synchronous -write finished by the previous run. (The synchronous write can update -a marker that describes where to restart on a crash.) - -

-WriteBatch provides an alternative to asynchronous writes. -Multiple updates may be placed in the same WriteBatch and -applied together using a synchronous write (i.e., -write_options.sync is set to true). The extra cost of -the synchronous write will be amortized across all of the writes in -the batch. - -

-

Concurrency

-

-A database may only be opened by one process at a time. -The leveldb implementation acquires a lock from the -operating system to prevent misuse. Within a single process, the -same leveldb::DB object may be safely shared by multiple -concurrent threads. I.e., different threads may write into or fetch -iterators or call Get on the same database without any -external synchronization (the leveldb implementation will -automatically do the required synchronization). However other objects -(like Iterator and WriteBatch) may require external synchronization. -If two threads share such an object, they must protect access to it -using their own locking protocol. More details are available in -the public header files. -

-

Iteration

-

-The following example demonstrates how to print all key,value pairs -in a database. -

-

-  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
-  }
-  assert(it->status().ok());  // Check for any errors found during the scan
-  delete it;
-
-The following variation shows how to process just the keys in the -range [start,limit): -

-

-  for (it->Seek(start);
-       it->Valid() && it->key().ToString() < limit;
-       it->Next()) {
-    ...
-  }
-
-You can also process entries in reverse order. (Caveat: reverse -iteration may be somewhat slower than forward iteration.) -

-

-  for (it->SeekToLast(); it->Valid(); it->Prev()) {
-    ...
-  }
-
-

Snapshots

-

-Snapshots provide consistent read-only views over the entire state of -the key-value store. ReadOptions::snapshot may be non-NULL to indicate -that a read should operate on a particular version of the DB state. -If ReadOptions::snapshot is NULL, the read will operate on an -implicit snapshot of the current state. -

-Snapshots are created by the DB::GetSnapshot() method: -

-

-  leveldb::ReadOptions options;
-  options.snapshot = db->GetSnapshot();
-  ... apply some updates to db ...
-  leveldb::Iterator* iter = db->NewIterator(options);
-  ... read using iter to view the state when the snapshot was created ...
-  delete iter;
-  db->ReleaseSnapshot(options.snapshot);
-
-Note that when a snapshot is no longer needed, it should be released -using the DB::ReleaseSnapshot interface. This allows the -implementation to get rid of state that was being maintained just to -support reading as of that snapshot. -

Slice

-

-The return value of the it->key() and it->value() calls above -are instances of the leveldb::Slice type. Slice is a simple -structure that contains a length and a pointer to an external byte -array. Returning a Slice is a cheaper alternative to returning a -std::string since we do not need to copy potentially large keys and -values. In addition, leveldb methods do not return null-terminated -C-style strings since leveldb keys and values are allowed to -contain '\0' bytes. -

-C++ strings and null-terminated C-style strings can be easily converted -to a Slice: -

-

-   leveldb::Slice s1 = "hello";
-
-   std::string str("world");
-   leveldb::Slice s2 = str;
-
-A Slice can be easily converted back to a C++ string: -
-   std::string str = s1.ToString();
-   assert(str == std::string("hello"));
-
-Be careful when using Slices since it is up to the caller to ensure that -the external byte array into which the Slice points remains live while -the Slice is in use. For example, the following is buggy: -

-

-   leveldb::Slice slice;
-   if (...) {
-     std::string str = ...;
-     slice = str;
-   }
-   Use(slice);
-
-When the if statement goes out of scope, str will be destroyed and the -backing storage for slice will disappear. -

-

Comparators

-

-The preceding examples used the default ordering function for key, -which orders bytes lexicographically. You can however supply a custom -comparator when opening a database. For example, suppose each -database key consists of two numbers and we should sort by the first -number, breaking ties by the second number. First, define a proper -subclass of leveldb::Comparator that expresses these rules: -

-

-  class TwoPartComparator : public leveldb::Comparator {
-   public:
-    // Three-way comparison function:
-    //   if a < b: negative result
-    //   if a > b: positive result
-    //   else: zero result
-    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
-      int a1, a2, b1, b2;
-      ParseKey(a, &a1, &a2);
-      ParseKey(b, &b1, &b2);
-      if (a1 < b1) return -1;
-      if (a1 > b1) return +1;
-      if (a2 < b2) return -1;
-      if (a2 > b2) return +1;
-      return 0;
-    }
-
-    // Ignore the following methods for now:
-    const char* Name() const { return "TwoPartComparator"; }
-    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
-    void FindShortSuccessor(std::string*) const { }
-  };
-
-Now create a database using this custom comparator: -

-

-  TwoPartComparator cmp;
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  options.comparator = &cmp;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  ...
-
-

Backwards compatibility

-

-The result of the comparator's Name method is attached to the -database when it is created, and is checked on every subsequent -database open. If the name changes, the leveldb::DB::Open call will -fail. Therefore, change the name if and only if the new key format -and comparison function are incompatible with existing databases, and -it is ok to discard the contents of all existing databases. -

-You can however still gradually evolve your key format over time with -a little bit of pre-planning. For example, you could store a version -number at the end of each key (one byte should suffice for most uses). -When you wish to switch to a new key format (e.g., adding an optional -third part to the keys processed by TwoPartComparator), -(a) keep the same comparator name (b) increment the version number -for new keys (c) change the comparator function so it uses the -version numbers found in the keys to decide how to interpret them. -

-

Performance

-

-Performance can be tuned by changing the default values of the -types defined in include/leveldb/options.h. - -

-

Block size

-

-leveldb groups adjacent keys together into the same block and such a -block is the unit of transfer to and from persistent storage. The -default block size is approximately 4096 uncompressed bytes. -Applications that mostly do bulk scans over the contents of the -database may wish to increase this size. Applications that do a lot -of point reads of small values may wish to switch to a smaller block -size if performance measurements indicate an improvement. There isn't -much benefit in using blocks smaller than one kilobyte, or larger than -a few megabytes. Also note that compression will be more effective -with larger block sizes. -

-

Compression

-

-Each block is individually compressed before being written to -persistent storage. Compression is on by default since the default -compression method is very fast, and is automatically disabled for -uncompressible data. In rare cases, applications may want to disable -compression entirely, but should only do so if benchmarks show a -performance improvement: -

-

-  leveldb::Options options;
-  options.compression = leveldb::kNoCompression;
-  ... leveldb::DB::Open(options, name, ...) ....
-
-

Cache

-

-The contents of the database are stored in a set of files in the -filesystem and each file stores a sequence of compressed blocks. If -options.cache is non-NULL, it is used to cache frequently used -uncompressed block contents. -

-

-  #include "leveldb/cache.h"
-
-  leveldb::Options options;
-  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
-  leveldb::DB* db;
-  leveldb::DB::Open(options, name, &db);
-  ... use the db ...
-  delete db
-  delete options.cache;
-
-Note that the cache holds uncompressed data, and therefore it should -be sized according to application level data sizes, without any -reduction from compression. (Caching of compressed blocks is left to -the operating system buffer cache, or any custom Env -implementation provided by the client.) -

-When performing a bulk read, the application may wish to disable -caching so that the data processed by the bulk read does not end up -displacing most of the cached contents. A per-iterator option can be -used to achieve this: -

-

-  leveldb::ReadOptions options;
-  options.fill_cache = false;
-  leveldb::Iterator* it = db->NewIterator(options);
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    ...
-  }
-
-

Key Layout

-

-Note that the unit of disk transfer and caching is a block. Adjacent -keys (according to the database sort order) will usually be placed in -the same block. Therefore the application can improve its performance -by placing keys that are accessed together near each other and placing -infrequently used keys in a separate region of the key space. -

-For example, suppose we are implementing a simple file system on top -of leveldb. The types of entries we might wish to store are: -

-

-   filename -> permission-bits, length, list of file_block_ids
-   file_block_id -> data
-
-We might want to prefix filename keys with one letter (say '/') and the -file_block_id keys with a different letter (say '0') so that scans -over just the metadata do not force us to fetch and cache bulky file -contents. -

-

Filters

-

-Because of the way leveldb data is organized on disk, -a single Get() call may involve multiple reads from disk. -The optional FilterPolicy mechanism can be used to reduce -the number of disk reads substantially. -

-   leveldb::Options options;
-   options.filter_policy = NewBloomFilterPolicy(10);
-   leveldb::DB* db;
-   leveldb::DB::Open(options, "/tmp/testdb", &db);
-   ... use the database ...
-   delete db;
-   delete options.filter_policy;
-
-The preceding code associates a -Bloom filter -based filtering policy with the database. Bloom filter based -filtering relies on keeping some number of bits of data in memory per -key (in this case 10 bits per key since that is the argument we passed -to NewBloomFilterPolicy). This filter will reduce the number of unnecessary -disk reads needed for Get() calls by a factor of -approximately a 100. Increasing the bits per key will lead to a -larger reduction at the cost of more memory usage. We recommend that -applications whose working set does not fit in memory and that do a -lot of random reads set a filter policy. -

-If you are using a custom comparator, you should ensure that the filter -policy you are using is compatible with your comparator. For example, -consider a comparator that ignores trailing spaces when comparing keys. -NewBloomFilterPolicy must not be used with such a comparator. -Instead, the application should provide a custom filter policy that -also ignores trailing spaces. For example: -

-  class CustomFilterPolicy : public leveldb::FilterPolicy {
-   private:
-    FilterPolicy* builtin_policy_;
-   public:
-    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
-    ~CustomFilterPolicy() { delete builtin_policy_; }
-
-    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
-
-    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      std::vector<Slice> trimmed(n);
-      for (int i = 0; i < n; i++) {
-        trimmed[i] = RemoveTrailingSpaces(keys[i]);
-      }
-      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
-    }
-
-    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
-    }
-  };
-
-

-Advanced applications may provide a filter policy that does not use -a bloom filter but uses some other mechanism for summarizing a set -of keys. See leveldb/filter_policy.h for detail. -

-

Checksums

-

-leveldb associates checksums with all data it stores in the file system. -There are two separate controls provided over how aggressively these -checksums are verified: -

-

    -
  • ReadOptions::verify_checksums may be set to true to force - checksum verification of all data that is read from the file system on - behalf of a particular read. By default, no such verification is - done. -

    -

  • Options::paranoid_checks may be set to true before opening a - database to make the database implementation raise an error as soon as - it detects an internal corruption. Depending on which portion of the - database has been corrupted, the error may be raised when the database - is opened, or later by another database operation. By default, - paranoid checking is off so that the database can be used even if - parts of its persistent storage have been corrupted. -

    - If a database is corrupted (perhaps it cannot be opened when - paranoid checking is turned on), the leveldb::RepairDB function - may be used to recover as much of the data as possible -

    -

-

Approximate Sizes

-

-The GetApproximateSizes method can used to get the approximate -number of bytes of file system space used by one or more key ranges. -

-

-   leveldb::Range ranges[2];
-   ranges[0] = leveldb::Range("a", "c");
-   ranges[1] = leveldb::Range("x", "z");
-   uint64_t sizes[2];
-   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
-
-The preceding call will set sizes[0] to the approximate number of -bytes of file system space used by the key range [a..c) and -sizes[1] to the approximate number of bytes used by the key range -[x..z). -

-

Environment

-

-All file operations (and other operating system calls) issued by the -leveldb implementation are routed through a leveldb::Env object. -Sophisticated clients may wish to provide their own Env -implementation to get better control. For example, an application may -introduce artificial delays in the file IO paths to limit the impact -of leveldb on other activities in the system. -

-

-  class SlowEnv : public leveldb::Env {
-    .. implementation of the Env interface ...
-  };
-
-  SlowEnv env;
-  leveldb::Options options;
-  options.env = &env;
-  Status s = leveldb::DB::Open(options, ...);
-
-

Porting

-

-leveldb may be ported to a new platform by providing platform -specific implementations of the types/methods/functions exported by -leveldb/port/port.h. See leveldb/port/port_example.h for more -details. -

-In addition, the new platform may need a new default leveldb::Env -implementation. See leveldb/util/env_posix.h for an example. - -

Other Information

- -

-Details about the leveldb implementation may be found in -the following documents: -

- - - diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000000..be8569692b --- /dev/null +++ b/doc/index.md @@ -0,0 +1,523 @@ +leveldb +======= + +_Jeff Dean, Sanjay Ghemawat_ + +The leveldb library provides a persistent key value store. Keys and values are +arbitrary byte arrays. The keys are ordered within the key value store +according to a user-specified comparator function. + +## Opening A Database + +A leveldb database has a name which corresponds to a file system directory. All +of the contents of database are stored in this directory. The following example +shows how to open a database, creating it if necessary: + +```c++ +#include +#include "leveldb/db.h" + +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +assert(status.ok()); +... +``` + +If you want to raise an error if the database already exists, add the following +line before the `leveldb::DB::Open` call: + +```c++ +options.error_if_exists = true; +``` + +## Status + +You may have noticed the `leveldb::Status` type above. Values of this type are +returned by most functions in leveldb that may encounter an error. You can check +if such a result is ok, and also print an associated error message: + +```c++ +leveldb::Status s = ...; +if (!s.ok()) cerr << s.ToString() << endl; +``` + +## Closing A Database + +When you are done with a database, just delete the database object. Example: + +```c++ +... open the db as described above ... +... do something with db ... +delete db; +``` + +## Reads And Writes + +The database provides Put, Delete, and Get methods to modify/query the database. +For example, the following code moves the value stored under key1 to key2. + +```c++ +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value); +if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1); +``` + +## Atomic Updates + +Note that if the process dies after the Put of key2 but before the delete of +key1, the same value may be left stored under multiple keys. Such problems can +be avoided by using the `WriteBatch` class to atomically apply a set of updates: + +```c++ +#include "leveldb/write_batch.h" +... +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) { + leveldb::WriteBatch batch; + batch.Delete(key1); + batch.Put(key2, value); + s = db->Write(leveldb::WriteOptions(), &batch); +} +``` + +The `WriteBatch` holds a sequence of edits to be made to the database, and these +edits within the batch are applied in order. Note that we called Delete before +Put so that if key1 is identical to key2, we do not end up erroneously dropping +the value entirely. + +Apart from its atomicity benefits, `WriteBatch` may also be used to speed up +bulk updates by placing lots of individual mutations into the same batch. + +## Synchronous Writes + +By default, each write to leveldb is asynchronous: it returns after pushing the +write from the process into the operating system. The transfer from operating +system memory to the underlying persistent storage happens asynchronously. The +sync flag can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling either +`fsync(...)` or `fdatasync(...)` or `msync(..., MS_SYNC)` before the write +operation returns.) + +```c++ +leveldb::WriteOptions write_options; +write_options.sync = true; +db->Put(write_options, ...); +``` + +Asynchronous writes are often more than a thousand times as fast as synchronous +writes. The downside of asynchronous writes is that a crash of the machine may +cause the last few updates to be lost. Note that a crash of just the writing +process (i.e., not a reboot) will not cause any loss since even when sync is +false, an update is pushed from the process memory into the operating system +before it is considered done. + +Asynchronous writes can often be used safely. For example, when loading a large +amount of data into the database you can handle lost updates by restarting the +bulk load after a crash. A hybrid scheme is also possible where every Nth write +is synchronous, and in the event of a crash, the bulk load is restarted just +after the last synchronous write finished by the previous run. (The synchronous +write can update a marker that describes where to restart on a crash.) + +`WriteBatch` provides an alternative to asynchronous writes. Multiple updates +may be placed in the same WriteBatch and applied together using a synchronous +write (i.e., `write_options.sync` is set to true). The extra cost of the +synchronous write will be amortized across all of the writes in the batch. + +## Concurrency + +A database may only be opened by one process at a time. The leveldb +implementation acquires a lock from the operating system to prevent misuse. +Within a single process, the same `leveldb::DB` object may be safely shared by +multiple concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any external synchronization +(the leveldb implementation will automatically do the required synchronization). +However other objects (like Iterator and `WriteBatch`) may require external +synchronization. If two threads share such an object, they must protect access +to it using their own locking protocol. More details are available in the public +header files. + +## Iteration + +The following example demonstrates how to print all key,value pairs in a +database. + +```c++ +leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + cout << it->key().ToString() << ": " << it->value().ToString() << endl; +} +assert(it->status().ok()); // Check for any errors found during the scan +delete it; +``` + +The following variation shows how to process just the keys in the range +[start,limit): + +```c++ +for (it->Seek(start); + it->Valid() && it->key().ToString() < limit; + it->Next()) { + ... +} +``` + +You can also process entries in reverse order. (Caveat: reverse iteration may be +somewhat slower than forward iteration.) + +```c++ +for (it->SeekToLast(); it->Valid(); it->Prev()) { + ... +} +``` + +## Snapshots + +Snapshots provide consistent read-only views over the entire state of the +key-value store. `ReadOptions::snapshot` may be non-NULL to indicate that a +read should operate on a particular version of the DB state. If +`ReadOptions::snapshot` is NULL, the read will operate on an implicit snapshot +of the current state. + +Snapshots are created by the `DB::GetSnapshot()` method: + +```c++ +leveldb::ReadOptions options; +options.snapshot = db->GetSnapshot(); +... apply some updates to db ... +leveldb::Iterator* iter = db->NewIterator(options); +... read using iter to view the state when the snapshot was created ... +delete iter; +db->ReleaseSnapshot(options.snapshot); +``` + +Note that when a snapshot is no longer needed, it should be released using the +`DB::ReleaseSnapshot` interface. This allows the implementation to get rid of +state that was being maintained just to support reading as of that snapshot. + +## Slice + +The return value of the `it->key()` and `it->value()` calls above are instances +of the `leveldb::Slice` type. Slice is a simple structure that contains a length +and a pointer to an external byte array. Returning a Slice is a cheaper +alternative to returning a `std::string` since we do not need to copy +potentially large keys and values. In addition, leveldb methods do not return +null-terminated C-style strings since leveldb keys and values are allowed to +contain `'\0'` bytes. + +C++ strings and null-terminated C-style strings can be easily converted to a +Slice: + +```c++ +leveldb::Slice s1 = "hello"; + +std::string str("world"); +leveldb::Slice s2 = str; +``` + +A Slice can be easily converted back to a C++ string: + +```c++ +std::string str = s1.ToString(); +assert(str == std::string("hello")); +``` + +Be careful when using Slices since it is up to the caller to ensure that the +external byte array into which the Slice points remains live while the Slice is +in use. For example, the following is buggy: + +```c++ +leveldb::Slice slice; +if (...) { + std::string str = ...; + slice = str; +} +Use(slice); +``` + +When the if statement goes out of scope, str will be destroyed and the backing +storage for slice will disappear. + +## Comparators + +The preceding examples used the default ordering function for key, which orders +bytes lexicographically. You can however supply a custom comparator when opening +a database. For example, suppose each database key consists of two numbers and +we should sort by the first number, breaking ties by the second number. First, +define a proper subclass of `leveldb::Comparator` that expresses these rules: + +```c++ +class TwoPartComparator : public leveldb::Comparator { + public: + // Three-way comparison function: + // if a < b: negative result + // if a > b: positive result + // else: zero result + int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const { + int a1, a2, b1, b2; + ParseKey(a, &a1, &a2); + ParseKey(b, &b1, &b2); + if (a1 < b1) return -1; + if (a1 > b1) return +1; + if (a2 < b2) return -1; + if (a2 > b2) return +1; + return 0; + } + + // Ignore the following methods for now: + const char* Name() const { return "TwoPartComparator"; } + void FindShortestSeparator(std::string*, const leveldb::Slice&) const {} + void FindShortSuccessor(std::string*) const {} +}; +``` + +Now create a database using this custom comparator: + +```c++ +TwoPartComparator cmp; +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +options.comparator = &cmp; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +... +``` + +### Backwards compatibility + +The result of the comparator's Name method is attached to the database when it +is created, and is checked on every subsequent database open. If the name +changes, the `leveldb::DB::Open` call will fail. Therefore, change the name if +and only if the new key format and comparison function are incompatible with +existing databases, and it is ok to discard the contents of all existing +databases. + +You can however still gradually evolve your key format over time with a little +bit of pre-planning. For example, you could store a version number at the end of +each key (one byte should suffice for most uses). When you wish to switch to a +new key format (e.g., adding an optional third part to the keys processed by +`TwoPartComparator`), (a) keep the same comparator name (b) increment the +version number for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. + +## Performance + +Performance can be tuned by changing the default values of the types defined in +`include/leveldb/options.h`. + +### Block size + +leveldb groups adjacent keys together into the same block and such a block is +the unit of transfer to and from persistent storage. The default block size is +approximately 4096 uncompressed bytes. Applications that mostly do bulk scans +over the contents of the database may wish to increase this size. Applications +that do a lot of point reads of small values may wish to switch to a smaller +block size if performance measurements indicate an improvement. There isn't much +benefit in using blocks smaller than one kilobyte, or larger than a few +megabytes. Also note that compression will be more effective with larger block +sizes. + +### Compression + +Each block is individually compressed before being written to persistent +storage. Compression is on by default since the default compression method is +very fast, and is automatically disabled for uncompressible data. In rare cases, +applications may want to disable compression entirely, but should only do so if +benchmarks show a performance improvement: + +```c++ +leveldb::Options options; +options.compression = leveldb::kNoCompression; +... leveldb::DB::Open(options, name, ...) .... +``` + +### Cache + +The contents of the database are stored in a set of files in the filesystem and +each file stores a sequence of compressed blocks. If options.cache is non-NULL, +it is used to cache frequently used uncompressed block contents. + +```c++ +#include "leveldb/cache.h" + +leveldb::Options options; +options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache +leveldb::DB* db; +leveldb::DB::Open(options, name, &db); +... use the db ... +delete db +delete options.cache; +``` + +Note that the cache holds uncompressed data, and therefore it should be sized +according to application level data sizes, without any reduction from +compression. (Caching of compressed blocks is left to the operating system +buffer cache, or any custom Env implementation provided by the client.) + +When performing a bulk read, the application may wish to disable caching so that +the data processed by the bulk read does not end up displacing most of the +cached contents. A per-iterator option can be used to achieve this: + +```c++ +leveldb::ReadOptions options; +options.fill_cache = false; +leveldb::Iterator* it = db->NewIterator(options); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + ... +} +``` + +### Key Layout + +Note that the unit of disk transfer and caching is a block. Adjacent keys +(according to the database sort order) will usually be placed in the same block. +Therefore the application can improve its performance by placing keys that are +accessed together near each other and placing infrequently used keys in a +separate region of the key space. + +For example, suppose we are implementing a simple file system on top of leveldb. +The types of entries we might wish to store are: + + filename -> permission-bits, length, list of file_block_ids + file_block_id -> data + +We might want to prefix filename keys with one letter (say '/') and the +`file_block_id` keys with a different letter (say '0') so that scans over just +the metadata do not force us to fetch and cache bulky file contents. + +### Filters + +Because of the way leveldb data is organized on disk, a single `Get()` call may +involve multiple reads from disk. The optional FilterPolicy mechanism can be +used to reduce the number of disk reads substantially. + +```c++ +leveldb::Options options; +options.filter_policy = NewBloomFilterPolicy(10); +leveldb::DB* db; +leveldb::DB::Open(options, "/tmp/testdb", &db); +... use the database ... +delete db; +delete options.filter_policy; +``` + +The preceding code associates a Bloom filter based filtering policy with the +database. Bloom filter based filtering relies on keeping some number of bits of +data in memory per key (in this case 10 bits per key since that is the argument +we passed to `NewBloomFilterPolicy`). This filter will reduce the number of +unnecessary disk reads needed for Get() calls by a factor of approximately +a 100. Increasing the bits per key will lead to a larger reduction at the cost +of more memory usage. We recommend that applications whose working set does not +fit in memory and that do a lot of random reads set a filter policy. + +If you are using a custom comparator, you should ensure that the filter policy +you are using is compatible with your comparator. For example, consider a +comparator that ignores trailing spaces when comparing keys. +`NewBloomFilterPolicy` must not be used with such a comparator. Instead, the +application should provide a custom filter policy that also ignores trailing +spaces. For example: + +```c++ +class CustomFilterPolicy : public leveldb::FilterPolicy { + private: + FilterPolicy* builtin_policy_; + + public: + CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) {} + ~CustomFilterPolicy() { delete builtin_policy_; } + + const char* Name() const { return "IgnoreTrailingSpacesFilter"; } + + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Use builtin bloom filter code after removing trailing spaces + std::vector trimmed(n); + for (int i = 0; i < n; i++) { + trimmed[i] = RemoveTrailingSpaces(keys[i]); + } + return builtin_policy_->CreateFilter(&trimmed[i], n, dst); + } +}; +``` + +Advanced applications may provide a filter policy that does not use a bloom +filter but uses some other mechanism for summarizing a set of keys. See +`leveldb/filter_policy.h` for detail. + +## Checksums + +leveldb associates checksums with all data it stores in the file system. There +are two separate controls provided over how aggressively these checksums are +verified: + +`ReadOptions::verify_checksums` may be set to true to force checksum +verification of all data that is read from the file system on behalf of a +particular read. By default, no such verification is done. + +`Options::paranoid_checks` may be set to true before opening a database to make +the database implementation raise an error as soon as it detects an internal +corruption. Depending on which portion of the database has been corrupted, the +error may be raised when the database is opened, or later by another database +operation. By default, paranoid checking is off so that the database can be used +even if parts of its persistent storage have been corrupted. + +If a database is corrupted (perhaps it cannot be opened when paranoid checking +is turned on), the `leveldb::RepairDB` function may be used to recover as much +of the data as possible + +## Approximate Sizes + +The `GetApproximateSizes` method can used to get the approximate number of bytes +of file system space used by one or more key ranges. + +```c++ +leveldb::Range ranges[2]; +ranges[0] = leveldb::Range("a", "c"); +ranges[1] = leveldb::Range("x", "z"); +uint64_t sizes[2]; +leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes); +``` + +The preceding call will set `sizes[0]` to the approximate number of bytes of +file system space used by the key range `[a..c)` and `sizes[1]` to the +approximate number of bytes used by the key range `[x..z)`. + +## Environment + +All file operations (and other operating system calls) issued by the leveldb +implementation are routed through a `leveldb::Env` object. Sophisticated clients +may wish to provide their own Env implementation to get better control. +For example, an application may introduce artificial delays in the file IO +paths to limit the impact of leveldb on other activities in the system. + +```c++ +class SlowEnv : public leveldb::Env { + ... implementation of the Env interface ... +}; + +SlowEnv env; +leveldb::Options options; +options.env = &env; +Status s = leveldb::DB::Open(options, ...); +``` + +## Porting + +leveldb may be ported to a new platform by providing platform specific +implementations of the types/methods/functions exported by +`leveldb/port/port.h`. See `leveldb/port/port_example.h` for more details. + +In addition, the new platform may need a new default `leveldb::Env` +implementation. See `leveldb/util/env_posix.h` for an example. + +## Other Information + +Details about the leveldb implementation may be found in the following +documents: + +1. [Implementation notes](impl.md) +2. [Format of an immutable Table file](table_format.md) +3. [Format of a log file](log_format.md) diff --git a/doc/log_format.md b/doc/log_format.md new file mode 100644 index 0000000000..f32cb5d7da --- /dev/null +++ b/doc/log_format.md @@ -0,0 +1,75 @@ +leveldb Log format +================== +The log file contents are a sequence of 32KB blocks. The only exception is that +the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it won't fit). +Any leftover bytes here form the trailer, which must consist entirely of zero +bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new non-zero +length record is added, the writer must emit a FIRST record (which contains zero +bytes of user data) to fill up the trailing seven bytes of the block and then +emit all of the user data in subsequent blocks. + +More types may be added in the future. Some Readers may skip record types they +do not understand, others may report that some data was skipped. + + FULL == 1 + FIRST == 2 + MIDDLE == 3 + LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been split into +multiple fragments (typically because of block boundaries). FIRST is the type +of the first fragment of a user record, LAST is the type of the last fragment of +a user record, and MIDDLE is the type of all interior fragments of a user +record. + +Example: consider a sequence of user records: + + A: length 1000 + B: length 97270 + C: length 8000 + +**A** will be stored as a FULL record in the first block. + +**B** will be split into three fragments: first fragment occupies the rest of +the first block, second fragment occupies the entirety of the second block, and +the third fragment occupies a prefix of the third block. This will leave six +bytes free in the third block, which will be left empty as the trailer. + +**C** will be stored as a FULL record in the fourth block. + +---- + +## Some benefits over the recordio format: + +1. We do not need any heuristics for resyncing - just go to next block boundary + and scan. If there is a corruption, skip to the next block. As a + side-benefit, we do not get confused when part of the contents of one log + file are embedded as a record inside another log file. + +2. Splitting at approximate boundaries (e.g., for mapreduce) is simple: find the + next block boundary and skip records until we hit a FULL or FIRST record. + +3. We do not need extra buffering for large records. + +## Some downsides compared to recordio format: + +1. No packing of tiny records. This could be fixed by adding a new record type, + so it is a shortcoming of the current implementation, not necessarily the + format. + +2. No compression. Again, this could be fixed by adding new record types. diff --git a/doc/log_format.txt b/doc/log_format.txt deleted file mode 100644 index 4cca5ef6ea..0000000000 --- a/doc/log_format.txt +++ /dev/null @@ -1,75 +0,0 @@ -The log file contents are a sequence of 32KB blocks. The only -exception is that the tail of the file may contain a partial block. - -Each block consists of a sequence of records: - block := record* trailer? - record := - checksum: uint32 // crc32c of type and data[] ; little-endian - length: uint16 // little-endian - type: uint8 // One of FULL, FIRST, MIDDLE, LAST - data: uint8[length] - -A record never starts within the last six bytes of a block (since it -won't fit). Any leftover bytes here form the trailer, which must -consist entirely of zero bytes and must be skipped by readers. - -Aside: if exactly seven bytes are left in the current block, and a new -non-zero length record is added, the writer must emit a FIRST record -(which contains zero bytes of user data) to fill up the trailing seven -bytes of the block and then emit all of the user data in subsequent -blocks. - -More types may be added in the future. Some Readers may skip record -types they do not understand, others may report that some data was -skipped. - -FULL == 1 -FIRST == 2 -MIDDLE == 3 -LAST == 4 - -The FULL record contains the contents of an entire user record. - -FIRST, MIDDLE, LAST are types used for user records that have been -split into multiple fragments (typically because of block boundaries). -FIRST is the type of the first fragment of a user record, LAST is the -type of the last fragment of a user record, and MIDDLE is the type of -all interior fragments of a user record. - -Example: consider a sequence of user records: - A: length 1000 - B: length 97270 - C: length 8000 -A will be stored as a FULL record in the first block. - -B will be split into three fragments: first fragment occupies the rest -of the first block, second fragment occupies the entirety of the -second block, and the third fragment occupies a prefix of the third -block. This will leave six bytes free in the third block, which will -be left empty as the trailer. - -C will be stored as a FULL record in the fourth block. - -=================== - -Some benefits over the recordio format: - -(1) We do not need any heuristics for resyncing - just go to next -block boundary and scan. If there is a corruption, skip to the next -block. As a side-benefit, we do not get confused when part of the -contents of one log file are embedded as a record inside another log -file. - -(2) Splitting at approximate boundaries (e.g., for mapreduce) is -simple: find the next block boundary and skip records until we -hit a FULL or FIRST record. - -(3) We do not need extra buffering for large records. - -Some downsides compared to recordio format: - -(1) No packing of tiny records. This could be fixed by adding a new -record type, so it is a shortcoming of the current implementation, -not necessarily the format. - -(2) No compression. Again, this could be fixed by adding new record types. diff --git a/doc/table_format.md b/doc/table_format.md new file mode 100644 index 0000000000..5fe7e72411 --- /dev/null +++ b/doc/table_format.md @@ -0,0 +1,107 @@ +leveldb File format +=================== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + + offset: varint64 + size: varint64 + +See [varints](https://developers.google.com/protocol-buffers/docs/encoding#varints) +for an explanation of varint64 format. + +1. The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in `block_builder.cc`, and then +optionally compressed. + +2. After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +`block_builder.cc` and then optionally compressed. + +3. A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +4. An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +5. At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q];// zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +## "filter" Meta Block + +If a `FilterPolicy` was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from `filter.` to the BlockHandle for the filter +block where `` is the string returned by the filter policy's +`Name()` method. + +The filter block stores a sequence of filters, where filter i contains +the output of `FilterPolicy::CreateFilter()` on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range `[ 0KB .. 2KB-1 ]`, all of the keys in X and Y will be +converted to a filter by calling `FilterPolicy::CreateFilter()`, and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +## "stats" Meta Block + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. + +TODO(postrelease): record following stats. + + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/doc/table_format.txt b/doc/table_format.txt deleted file mode 100644 index ca8f9b4460..0000000000 --- a/doc/table_format.txt +++ /dev/null @@ -1,104 +0,0 @@ -File format -=========== - - - [data block 1] - [data block 2] - ... - [data block N] - [meta block 1] - ... - [meta block K] - [metaindex block] - [index block] - [Footer] (fixed size; starts at file_size - sizeof(Footer)) - - -The file contains internal pointers. Each such pointer is called -a BlockHandle and contains the following information: - offset: varint64 - size: varint64 -See https://developers.google.com/protocol-buffers/docs/encoding#varints -for an explanation of varint64 format. - -(1) The sequence of key/value pairs in the file are stored in sorted -order and partitioned into a sequence of data blocks. These blocks -come one after another at the beginning of the file. Each data block -is formatted according to the code in block_builder.cc, and then -optionally compressed. - -(2) After the data blocks we store a bunch of meta blocks. The -supported meta block types are described below. More meta block types -may be added in the future. Each meta block is again formatted using -block_builder.cc and then optionally compressed. - -(3) A "metaindex" block. It contains one entry for every other meta -block where the key is the name of the meta block and the value is a -BlockHandle pointing to that meta block. - -(4) An "index" block. This block contains one entry per data block, -where the key is a string >= last key in that data block and before -the first key in the successive data block. The value is the -BlockHandle for the data block. - -(6) At the very end of the file is a fixed length footer that contains -the BlockHandle of the metaindex and index blocks as well as a magic number. - metaindex_handle: char[p]; // Block handle for metaindex - index_handle: char[q]; // Block handle for index - padding: char[40-p-q]; // zeroed bytes to make fixed length - // (40==2*BlockHandle::kMaxEncodedLength) - magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) - -"filter" Meta Block -------------------- - -If a "FilterPolicy" was specified when the database was opened, a -filter block is stored in each table. The "metaindex" block contains -an entry that maps from "filter." to the BlockHandle for the filter -block where "" is the string returned by the filter policy's -"Name()" method. - -The filter block stores a sequence of filters, where filter i contains -the output of FilterPolicy::CreateFilter() on all keys that are stored -in a block whose file offset falls within the range - - [ i*base ... (i+1)*base-1 ] - -Currently, "base" is 2KB. So for example, if blocks X and Y start in -the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be -converted to a filter by calling FilterPolicy::CreateFilter(), and the -resulting filter will be stored as the first filter in the filter -block. - -The filter block is formatted as follows: - - [filter 0] - [filter 1] - [filter 2] - ... - [filter N-1] - - [offset of filter 0] : 4 bytes - [offset of filter 1] : 4 bytes - [offset of filter 2] : 4 bytes - ... - [offset of filter N-1] : 4 bytes - - [offset of beginning of offset array] : 4 bytes - lg(base) : 1 byte - -The offset array at the end of the filter block allows efficient -mapping from a data block offset to the corresponding filter. - -"stats" Meta Block ------------------- - -This meta block contains a bunch of stats. The key is the name -of the statistic. The value contains the statistic. -TODO(postrelease): record following stats. - data size - index size - key size (uncompressed) - value size (uncompressed) - number of entries - number of data blocks diff --git a/table/filter_block.cc b/table/filter_block.cc index 4e78b954f8..1ed5134170 100644 --- a/table/filter_block.cc +++ b/table/filter_block.cc @@ -9,7 +9,7 @@ namespace leveldb { -// See doc/table_format.txt for an explanation of the filter block format. +// See doc/table_format.md for an explanation of the filter block format. // Generate new filter every 2KB of data static const size_t kFilterBaseLg = 11; From d0883b6006f92534dbcbea9268cb7a3cf9a13ccd Mon Sep 17 00:00:00 2001 From: cmumford Date: Wed, 1 Mar 2017 10:25:05 -0800 Subject: [PATCH 038/174] Fixed path to doc file: index.md. Prior index.html was using rawgit.com which doesn't process Markdown and therefore only serves the markdown source. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148902180 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d600c5f45d..a010c50858 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. # Documentation - [LevelDB library documentation](https://rawgit.com/google/leveldb/master/doc/index.md) is online and bundled with the source code. + [LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code. # Limitations From eb4f0972fdb5c2754c95fae198443a552a88d0b7 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 1 Mar 2017 10:57:52 -0800 Subject: [PATCH 039/174] leveldb: Fix compilation warnings in port_posix_sse.cc on x86 (32-bit). LE_LOAD64 is only used when _mm_crc32_u64 is available, on 64-bit x86 processors. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148906169 --- port/port_posix_sse.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/port/port_posix_sse.cc b/port/port_posix_sse.cc index 57ec8fee7e..1e519ba0b6 100644 --- a/port/port_posix_sse.cc +++ b/port/port_posix_sse.cc @@ -37,6 +37,8 @@ static inline uint32_t LE_LOAD32(const uint8_t *p) { return word; } +#if defined(_M_X64) || defined(__x86_64__) // LE_LOAD64 is only used on x64. + // Used to fetch a naturally-aligned 64-bit word in little endian byte-order static inline uint64_t LE_LOAD64(const uint8_t *p) { uint64_t dword; @@ -44,6 +46,8 @@ static inline uint64_t LE_LOAD64(const uint8_t *p) { return dword; } +#endif // defined(_M_X64) || defined(__x86_64__) + static inline bool HaveSSE42() { #if defined(_MSC_VER) int cpu_info[4]; From f3f139737c666e558864ca8b473959be203edaf6 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 1 Mar 2017 12:05:47 -0800 Subject: [PATCH 040/174] Separate Env tests from PosixEnv tests. env_test.cc defines EnvPosixTest which tests the Env implementation returned by Env::Default(). The naming is a bit unfortunate, as the tests in env_test.cc are written against the Env contract, and therefore are applicable to any Env implementation. An instance of the confusion caused by the naming is [] which added a dependency from env_test.cc to EnvPosixTestHelper, which is closely coupled to EnvPosix. This change disentangles EnvPosix-specific test code into a env_posix_test.cc file. The code there uses EnvPosixTestHelper and specifically targets the EnvPosix implementation. env_test.cc now implements EnvTest, and contains tests that are also applicable to other ports, which may define their own Env implementation. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148914642 --- Makefile | 4 +++ util/env_posix_test.cc | 66 ++++++++++++++++++++++++++++++++++++++++++ util/env_test.cc | 56 +++++------------------------------ 3 files changed, 78 insertions(+), 48 deletions(-) create mode 100644 util/env_posix_test.cc diff --git a/Makefile b/Makefile index 66e32261f6..5cfd6f94b1 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ TESTS = \ util/cache_test \ util/coding_test \ util/crc32c_test \ + util/env_posix_test \ util/env_test \ util/hash_test @@ -337,6 +338,9 @@ $(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) $(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(STATIC_OUTDIR)/env_posix_test:util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + $(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc new file mode 100644 index 0000000000..295f8ae440 --- /dev/null +++ b/util/env_posix_test.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" +#include "util/env_posix_test_helper.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; + +class EnvPosixTest { + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } + + static void SetFileLimits(int read_only_file_limit, int mmap_limit) { + EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); + EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } +}; + +TEST(EnvPosixTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "w"); + ASSERT_TRUE(f != NULL); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. + const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, + leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/util/env_test.cc b/util/env_test.cc index 0cb1577bc3..839ae56a1a 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -6,7 +6,6 @@ #include "port/port.h" #include "util/testharness.h" -#include "util/env_posix_test_helper.h" namespace leveldb { @@ -14,33 +13,28 @@ static const int kDelayMicros = 100000; static const int kReadOnlyFileLimit = 4; static const int kMMapLimit = 4; -class EnvPosixTest { +class EnvTest { private: port::Mutex mu_; std::string events_; public: Env* env_; - EnvPosixTest() : env_(Env::Default()) { } - - static void SetFileLimits(int read_only_file_limit, int mmap_limit) { - EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); - EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); - } + EnvTest() : env_(Env::Default()) { } }; static void SetBool(void* ptr) { reinterpret_cast(ptr)->NoBarrier_Store(ptr); } -TEST(EnvPosixTest, RunImmediately) { +TEST(EnvTest, RunImmediately) { port::AtomicPointer called (NULL); env_->Schedule(&SetBool, &called); - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); ASSERT_TRUE(called.NoBarrier_Load() != NULL); } -TEST(EnvPosixTest, RunMany) { +TEST(EnvTest, RunMany) { port::AtomicPointer last_id (NULL); struct CB { @@ -67,7 +61,7 @@ TEST(EnvPosixTest, RunMany) { env_->Schedule(&CB::Run, &cb3); env_->Schedule(&CB::Run, &cb4); - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); void* cur = last_id.Acquire_Load(); ASSERT_EQ(4, reinterpret_cast(cur)); } @@ -86,7 +80,7 @@ static void ThreadBody(void* arg) { s->mu.Unlock(); } -TEST(EnvPosixTest, StartThread) { +TEST(EnvTest, StartThread) { State state; state.val = 0; state.num_running = 3; @@ -100,47 +94,13 @@ TEST(EnvPosixTest, StartThread) { if (num == 0) { break; } - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); } ASSERT_EQ(state.val, 3); } -TEST(EnvPosixTest, TestOpenOnRead) { - // Write some test data to a single file that will be opened |n| times. - std::string test_dir; - ASSERT_OK(Env::Default()->GetTestDirectory(&test_dir)); - std::string test_file = test_dir + "/open_on_read.txt"; - - FILE* f = fopen(test_file.c_str(), "w"); - ASSERT_TRUE(f != NULL); - const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; - fputs(kFileData, f); - fclose(f); - - // Open test file some number above the sum of the two limits to force - // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. - const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; - leveldb::RandomAccessFile* files[kNumFiles] = {0}; - for (int i = 0; i < kNumFiles; i++) { - ASSERT_OK(Env::Default()->NewRandomAccessFile(test_file, &files[i])); - } - char scratch; - Slice read_result; - for (int i = 0; i < kNumFiles; i++) { - ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); - ASSERT_EQ(kFileData[i], read_result[0]); - } - for (int i = 0; i < kNumFiles; i++) { - delete files[i]; - } - ASSERT_OK(Env::Default()->DeleteFile(test_file)); -} - } // namespace leveldb int main(int argc, char** argv) { - // All tests currently run with the same read-only file limits. - leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, - leveldb::kMMapLimit); return leveldb::test::RunAllTests(); } From a53934a3ae1244679f812d998a4f16f2c7f309a6 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 1 Mar 2017 15:14:49 -0800 Subject: [PATCH 041/174] Increase leveldb version to 1.20. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=148937577 --- Makefile | 2 +- include/leveldb/db.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5cfd6f94b1..f7cc7d736c 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a else # Update db.h if you change these. SHARED_VERSION_MAJOR = 1 -SHARED_VERSION_MINOR = 19 +SHARED_VERSION_MINOR = 20 SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR) SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR) diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 9752cbad51..bfab10a0b7 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -14,7 +14,7 @@ namespace leveldb { // Update Makefile if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 19; +static const int kMinorVersion = 20; struct Options; struct ReadOptions; From 5b817400a0a5afe3badbb8859706a571882ababc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=9C=E5=86=BB?= <18814092650@163.com> Date: Fri, 10 Mar 2017 14:23:19 +0800 Subject: [PATCH 042/174] fix comment --- util/env_posix.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 84aabb20a7..1c2ba9a46d 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -306,7 +306,7 @@ class PosixFileLock : public FileLock { }; // Set of locked files. We keep a separate set instead of just -// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide +// relying on fcntl(F_SETLK) since fcntl(F_SETLK) does not provide // any protection against multiple uses from the same process. class PosixLockTable { private: From 471f0b84ec3420c7565511eb6e2fee8e0a0550e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=82=80?= Date: Mon, 22 May 2017 14:01:38 +0800 Subject: [PATCH 043/174] fix comment --- include/leveldb/env.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 99b6c21414..9f4bdfdd48 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -145,7 +145,7 @@ class Env { virtual void StartThread(void (*function)(void* arg), void* arg) = 0; // *path is set to a temporary directory that can be used for testing. It may - // or many not have just been created. The directory may or may not differ + // or may not have just been created. The directory may or may not differ // between runs of the same process, but subsequent calls will return the // same directory. virtual Status GetTestDirectory(std::string* path) = 0; From 69e2bd224b7f11e021527cb95bab18f1ee6e1b3b Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 23 May 2017 17:29:44 -0700 Subject: [PATCH 044/174] LevelDB: Add WriteBatch::ApproximateSize(). This can be used to report metrics on LevelDB usage. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156934930 --- db/write_batch.cc | 4 ++++ db/write_batch_test.cc | 17 +++++++++++++++++ include/leveldb/write_batch.h | 6 ++++++ 3 files changed, 27 insertions(+) diff --git a/db/write_batch.cc b/db/write_batch.cc index 33f4a4257e..7f8f3e8b7d 100644 --- a/db/write_batch.cc +++ b/db/write_batch.cc @@ -39,6 +39,10 @@ void WriteBatch::Clear() { rep_.resize(kHeader); } +size_t WriteBatch::ApproximateSize() { + return rep_.size(); +} + Status WriteBatch::Iterate(Handler* handler) const { Slice input(rep_); if (input.size() < kHeader) { diff --git a/db/write_batch_test.cc b/db/write_batch_test.cc index 9064e3d85e..8d38023acd 100644 --- a/db/write_batch_test.cc +++ b/db/write_batch_test.cc @@ -113,6 +113,23 @@ TEST(WriteBatchTest, Append) { PrintContents(&b1)); } +TEST(WriteBatchTest, ApproximateSize) { + WriteBatch batch; + size_t empty_size = batch.ApproximateSize(); + + batch.Put(Slice("foo"), Slice("bar")); + size_t one_key_size = batch.ApproximateSize(); + ASSERT_LT(empty_size, one_key_size); + + batch.Put(Slice("baz"), Slice("boo")); + size_t two_keys_size = batch.ApproximateSize(); + ASSERT_LT(one_key_size, two_keys_size); + + batch.Delete(Slice("box")); + size_t post_delete_size = batch.ApproximateSize(); + ASSERT_LT(two_keys_size, post_delete_size); +} + } // namespace leveldb int main(int argc, char** argv) { diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index ee9aab68e0..65a76d84d3 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -42,6 +42,12 @@ class WriteBatch { // Clear all updates buffered in this batch. void Clear(); + // The size of the database changes caused by this batch. + // + // This number is tied to implementation details, and may change across + // releases. It is intended for LevelDB usage metrics. + size_t ApproximateSize(); + // Support for iterating over the contents of a batch. class Handler { public: From 8415f00eeedd96934d3578572d3802900e61a556 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 10 Jul 2017 13:32:58 -0700 Subject: [PATCH 045/174] leveldb: Report missing CURRENT manifest file as database corruption. BTRFS reorders rename and write operations, so it is possible that a filesystem crash and recovery results in a situation where the file pointed to by CURRENT does not exist. DB::Open currently reports an I/O error in this case. Reporting database corruption is a better hint to the caller, which can attempt to recover the database or erase it and start over. This issue is not merely theoretical. It was reported as having showed up in the wild at https://github.com/google/leveldb/issues/195 and at https://crbug.com/738961. Also, asides from the BTRFS case described above, incorrect data in CURRENT seems like a possible corruption case that should be handled gracefully. The Env API changes here can be considered backwards compatible, because an implementation that returns Status::IOError instead of Status::NotFound will still get the same functionality as before. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161432630 --- db/recovery_test.cc | 21 ++++++++++++++-- db/version_set.cc | 4 +++ include/leveldb/env.h | 6 +++-- util/env_posix.cc | 58 +++++++++++++++++++++++-------------------- util/env_test.cc | 18 ++++++++++++++ 5 files changed, 76 insertions(+), 31 deletions(-) diff --git a/db/recovery_test.cc b/db/recovery_test.cc index 9596f4288a..e5a7c804a0 100644 --- a/db/recovery_test.cc +++ b/db/recovery_test.cc @@ -47,7 +47,7 @@ class RecoveryTest { db_ = NULL; } - void Open(Options* options = NULL) { + Status OpenWithStatus(Options* options = NULL) { Close(); Options opts; if (options != NULL) { @@ -59,7 +59,11 @@ class RecoveryTest { if (opts.env == NULL) { opts.env = env_; } - ASSERT_OK(DB::Open(opts, dbname_, &db_)); + return DB::Open(opts, dbname_, &db_); + } + + void Open(Options* options = NULL) { + ASSERT_OK(OpenWithStatus(options)); ASSERT_EQ(1, NumLogs()); } @@ -100,6 +104,10 @@ class RecoveryTest { return logs.size(); } + void DeleteManifestFile() { + ASSERT_OK(env_->DeleteFile(ManifestFileName())); + } + uint64_t FirstLogFile() { return GetFiles(kLogFile)[0]; } @@ -317,6 +325,15 @@ TEST(RecoveryTest, MultipleLogFiles) { ASSERT_EQ("there", Get("hi")); } +TEST(RecoveryTest, ManifestMissing) { + ASSERT_OK(Put("foo", "bar")); + Close(); + DeleteManifestFile(); + + Status status = OpenWithStatus(); + ASSERT_TRUE(status.IsCorruption()); +} + } // namespace leveldb int main(int argc, char** argv) { diff --git a/db/version_set.cc b/db/version_set.cc index b1256f90e1..020c37ea2c 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -925,6 +925,10 @@ Status VersionSet::Recover(bool *save_manifest) { SequentialFile* file; s = env_->NewSequentialFile(dscname, &file); if (!s.ok()) { + if (s.IsNotFound()) { + return Status::Corruption( + "CURRENT points to a non-existent file", s.ToString()); + } return s; } diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 99b6c21414..cb8b6a465b 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -43,7 +43,8 @@ class Env { // Create a brand new sequentially-readable file with the specified name. // On success, stores a pointer to the new file in *result and returns OK. // On failure stores NULL in *result and returns non-OK. If the file does - // not exist, returns a non-OK status. + // not exist, returns a non-OK status. Implementations should return a + // NotFound status when the file does not exist. // // The returned file will only be accessed by one thread at a time. virtual Status NewSequentialFile(const std::string& fname, @@ -53,7 +54,8 @@ class Env { // specified name. On success, stores a pointer to the new file in // *result and returns OK. On failure stores NULL in *result and // returns non-OK. If the file does not exist, returns a non-OK - // status. + // status. Implementations should return a NotFound status when the file does + // not exist. // // The returned file may be concurrently accessed by multiple threads. virtual Status NewRandomAccessFile(const std::string& fname, diff --git a/util/env_posix.cc b/util/env_posix.cc index 84aabb20a7..b21f515a37 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -34,8 +34,12 @@ namespace { static int open_read_only_file_limit = -1; static int mmap_limit = -1; -static Status IOError(const std::string& context, int err_number) { - return Status::IOError(context, strerror(err_number)); +static Status PosixError(const std::string& context, int err_number) { + if (err_number == ENOENT) { + return Status::NotFound(context, strerror(err_number)); + } else { + return Status::IOError(context, strerror(err_number)); + } } // Helper class to limit resource usage to avoid exhaustion. @@ -108,7 +112,7 @@ class PosixSequentialFile: public SequentialFile { // We leave status as ok if we hit the end of the file } else { // A partial read with an error: return a non-ok status - s = IOError(filename_, errno); + s = PosixError(filename_, errno); } } return s; @@ -116,7 +120,7 @@ class PosixSequentialFile: public SequentialFile { virtual Status Skip(uint64_t n) { if (fseek(file_, n, SEEK_CUR)) { - return IOError(filename_, errno); + return PosixError(filename_, errno); } return Status::OK(); } @@ -154,7 +158,7 @@ class PosixRandomAccessFile: public RandomAccessFile { if (temporary_fd_) { fd = open(filename_.c_str(), O_RDONLY); if (fd < 0) { - return IOError(filename_, errno); + return PosixError(filename_, errno); } } @@ -163,7 +167,7 @@ class PosixRandomAccessFile: public RandomAccessFile { *result = Slice(scratch, (r < 0) ? 0 : r); if (r < 0) { // An error: return a non-ok status - s = IOError(filename_, errno); + s = PosixError(filename_, errno); } if (temporary_fd_) { // Close the temporary file descriptor opened earlier. @@ -199,7 +203,7 @@ class PosixMmapReadableFile: public RandomAccessFile { Status s; if (offset + n > length_) { *result = Slice(); - s = IOError(filename_, EINVAL); + s = PosixError(filename_, EINVAL); } else { *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); } @@ -226,7 +230,7 @@ class PosixWritableFile : public WritableFile { virtual Status Append(const Slice& data) { size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); if (r != data.size()) { - return IOError(filename_, errno); + return PosixError(filename_, errno); } return Status::OK(); } @@ -234,7 +238,7 @@ class PosixWritableFile : public WritableFile { virtual Status Close() { Status result; if (fclose(file_) != 0) { - result = IOError(filename_, errno); + result = PosixError(filename_, errno); } file_ = NULL; return result; @@ -242,7 +246,7 @@ class PosixWritableFile : public WritableFile { virtual Status Flush() { if (fflush_unlocked(file_) != 0) { - return IOError(filename_, errno); + return PosixError(filename_, errno); } return Status::OK(); } @@ -263,10 +267,10 @@ class PosixWritableFile : public WritableFile { if (basename.starts_with("MANIFEST")) { int fd = open(dir.c_str(), O_RDONLY); if (fd < 0) { - s = IOError(dir, errno); + s = PosixError(dir, errno); } else { if (fsync(fd) < 0) { - s = IOError(dir, errno); + s = PosixError(dir, errno); } close(fd); } @@ -337,7 +341,7 @@ class PosixEnv : public Env { FILE* f = fopen(fname.c_str(), "r"); if (f == NULL) { *result = NULL; - return IOError(fname, errno); + return PosixError(fname, errno); } else { *result = new PosixSequentialFile(fname, f); return Status::OK(); @@ -350,7 +354,7 @@ class PosixEnv : public Env { Status s; int fd = open(fname.c_str(), O_RDONLY); if (fd < 0) { - s = IOError(fname, errno); + s = PosixError(fname, errno); } else if (mmap_limit_.Acquire()) { uint64_t size; s = GetFileSize(fname, &size); @@ -359,7 +363,7 @@ class PosixEnv : public Env { if (base != MAP_FAILED) { *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); } else { - s = IOError(fname, errno); + s = PosixError(fname, errno); } } close(fd); @@ -378,7 +382,7 @@ class PosixEnv : public Env { FILE* f = fopen(fname.c_str(), "w"); if (f == NULL) { *result = NULL; - s = IOError(fname, errno); + s = PosixError(fname, errno); } else { *result = new PosixWritableFile(fname, f); } @@ -391,7 +395,7 @@ class PosixEnv : public Env { FILE* f = fopen(fname.c_str(), "a"); if (f == NULL) { *result = NULL; - s = IOError(fname, errno); + s = PosixError(fname, errno); } else { *result = new PosixWritableFile(fname, f); } @@ -407,7 +411,7 @@ class PosixEnv : public Env { result->clear(); DIR* d = opendir(dir.c_str()); if (d == NULL) { - return IOError(dir, errno); + return PosixError(dir, errno); } struct dirent* entry; while ((entry = readdir(d)) != NULL) { @@ -420,7 +424,7 @@ class PosixEnv : public Env { virtual Status DeleteFile(const std::string& fname) { Status result; if (unlink(fname.c_str()) != 0) { - result = IOError(fname, errno); + result = PosixError(fname, errno); } return result; } @@ -428,7 +432,7 @@ class PosixEnv : public Env { virtual Status CreateDir(const std::string& name) { Status result; if (mkdir(name.c_str(), 0755) != 0) { - result = IOError(name, errno); + result = PosixError(name, errno); } return result; } @@ -436,7 +440,7 @@ class PosixEnv : public Env { virtual Status DeleteDir(const std::string& name) { Status result; if (rmdir(name.c_str()) != 0) { - result = IOError(name, errno); + result = PosixError(name, errno); } return result; } @@ -446,7 +450,7 @@ class PosixEnv : public Env { struct stat sbuf; if (stat(fname.c_str(), &sbuf) != 0) { *size = 0; - s = IOError(fname, errno); + s = PosixError(fname, errno); } else { *size = sbuf.st_size; } @@ -456,7 +460,7 @@ class PosixEnv : public Env { virtual Status RenameFile(const std::string& src, const std::string& target) { Status result; if (rename(src.c_str(), target.c_str()) != 0) { - result = IOError(src, errno); + result = PosixError(src, errno); } return result; } @@ -466,12 +470,12 @@ class PosixEnv : public Env { Status result; int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); if (fd < 0) { - result = IOError(fname, errno); + result = PosixError(fname, errno); } else if (!locks_.Insert(fname)) { close(fd); result = Status::IOError("lock " + fname, "already held by process"); } else if (LockOrUnlock(fd, true) == -1) { - result = IOError("lock " + fname, errno); + result = PosixError("lock " + fname, errno); close(fd); locks_.Remove(fname); } else { @@ -487,7 +491,7 @@ class PosixEnv : public Env { PosixFileLock* my_lock = reinterpret_cast(lock); Status result; if (LockOrUnlock(my_lock->fd_, false) == -1) { - result = IOError("unlock", errno); + result = PosixError("unlock", errno); } locks_.Remove(my_lock->name_); close(my_lock->fd_); @@ -524,7 +528,7 @@ class PosixEnv : public Env { FILE* f = fopen(fname.c_str(), "w"); if (f == NULL) { *result = NULL; - return IOError(fname, errno); + return PosixError(fname, errno); } else { *result = new PosixLogger(f, &PosixEnv::gettid); return Status::OK(); diff --git a/util/env_test.cc b/util/env_test.cc index 839ae56a1a..4d8ecc4a7f 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -99,6 +99,24 @@ TEST(EnvTest, StartThread) { ASSERT_EQ(state.val, 3); } +TEST(EnvTest, TestOpenNonExistentFile) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + + std::string non_existent_file = test_dir + "/non_existent_file"; + ASSERT_TRUE(!env_->FileExists(non_existent_file)); + + RandomAccessFile* random_access_file; + Status status = env_->NewRandomAccessFile( + non_existent_file, &random_access_file); + ASSERT_TRUE(status.IsNotFound()); + + SequentialFile* sequential_file; + status = env_->NewSequentialFile(non_existent_file, &sequential_file); + ASSERT_TRUE(status.IsNotFound()); +} + } // namespace leveldb int main(int argc, char** argv) { From 0b402e96a76b19cd98e82402de636449a2613228 Mon Sep 17 00:00:00 2001 From: scrubbed Date: Mon, 24 Jul 2017 10:54:00 -0700 Subject: [PATCH 046/174] Use __APPLE__ instead of OS_MACOS. The former is compiler-provided. Use __APPLE__ instead of OS_MACOS when testing for the Apple platform and remove the latter symbol from the BUILD file. This fixes incompatibility issues when using the library on an Apple device. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=162958094 --- build_detect_platform | 4 ++-- port/atomic_pointer.h | 4 ++-- port/port_posix.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build_detect_platform b/build_detect_platform index d2a20ce5b6..85d3594360 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -82,7 +82,7 @@ case "$TARGET_OS" in ;; Darwin) PLATFORM=OS_MACOSX - COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + COMMON_FLAGS="$MEMCMP_FLAG" PLATFORM_SHARED_EXT=dylib [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" @@ -150,7 +150,7 @@ case "$TARGET_OS" in ;; IOS) PLATFORM=IOS - COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" + COMMON_FLAGS="$MEMCMP_FLAG" [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PORT_FILE=port/port_posix.cc PORT_SSE_FILE=port/port_posix_sse.cc diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index 1c4c7aafc6..54f088585d 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -25,7 +25,7 @@ #ifdef OS_WIN #include #endif -#ifdef OS_MACOSX +#ifdef __APPLE__ #include #endif @@ -54,7 +54,7 @@ namespace port { #define LEVELDB_HAVE_MEMORY_BARRIER // Mac OS -#elif defined(OS_MACOSX) +#elif defined(__APPLE__) inline void MemoryBarrier() { OSMemoryBarrier(); } diff --git a/port/port_posix.h b/port/port_posix.h index d67ab68c27..24915512c2 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -8,7 +8,7 @@ #define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ #undef PLATFORM_IS_LITTLE_ENDIAN -#if defined(OS_MACOSX) +#if defined(__APPLE__) #include #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) #define PLATFORM_IS_LITTLE_ENDIAN \ @@ -50,7 +50,7 @@ #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) #endif -#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ +#if defined(__APPLE__) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ defined(OS_ANDROID) || defined(OS_HPUX) || defined(CYGWIN) // Use fread/fwrite/fflush on platforms without _unlocked variants @@ -59,7 +59,7 @@ #define fflush_unlocked fflush #endif -#if defined(OS_MACOSX) || defined(OS_FREEBSD) ||\ +#if defined(__APPLE__) || defined(OS_FREEBSD) ||\ defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) // Use fsync() on platforms without fdatasync() #define fdatasync fsync From 02f43c0fcde39823830493503e8a3f72fed43d24 Mon Sep 17 00:00:00 2001 From: davidair Date: Thu, 3 Aug 2017 13:42:12 -0700 Subject: [PATCH 047/174] Remove dead code. The dead code has been in the codebase since the initial commit and is generating a compiler warning when used in Xcode. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164174594 --- db/version_set.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/db/version_set.cc b/db/version_set.cc index 020c37ea2c..7022b274e1 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -1399,13 +1399,6 @@ void VersionSet::SetupOtherInputs(Compaction* c) { &c->grandparents_); } - if (false) { - Log(options_->info_log, "Compacting %d '%s' .. '%s'", - level, - smallest.DebugString().c_str(), - largest.DebugString().c_str()); - } - // Update the place where we will do the next compaction for this level. // We update this immediately instead of waiting for the VersionEdit // to be applied so that if the compaction fails, we will try a different From 2964b803b857932ff7499d7bebb61dc5514dab7c Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 23 Aug 2017 20:59:46 -0700 Subject: [PATCH 048/174] leveldb: Fix alignment code in SSE4.2-optimized CRC32C. When faced with a pointer that is misaligned by K bytes (pointer % 8 == K), the code previously moved forward by K bytes. In order to end up with an aligned pointer, the code must move by 8 - K bytes. This lands https://github.com/google/leveldb/pull/488 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=166295921 --- port/port_posix_sse.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/port/port_posix_sse.cc b/port/port_posix_sse.cc index 1e519ba0b6..08d9aee1e3 100644 --- a/port/port_posix_sse.cc +++ b/port/port_posix_sse.cc @@ -92,8 +92,12 @@ uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { } while (0) if (size > 16) { - // Process unaligned bytes - for (unsigned int i = reinterpret_cast(p) % 8; i; --i) { + // Point x at first 8-byte aligned byte in string. This must be inside the + // string, due to the size check above. + const uintptr_t pval = reinterpret_cast(p); + const uint8_t* x = reinterpret_cast(((pval + 7) >> 3) << 3); + // Process bytes until p is 8-byte aligned. + while (p != x) { STEP1; } From 09a3c8e7417547829b94bcdaa62cdf9e896f29a9 Mon Sep 17 00:00:00 2001 From: cmumford Date: Thu, 20 Jul 2017 15:00:13 -0700 Subject: [PATCH 049/174] Switched variable type from int to uint64_t in ConsumeDecimalNumber. An Android test was occasionally crashing with a SEGV in ConsumeDecimalNumber Switching a local variable from an int to uint64_t eliminated these crashes. Speculating this is either a compiler, runtime library, or emulator issue. Switching this type to uint64_t also eliminates a compiler warning about comparing an int with a uint64_t. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=166399695 --- util/logging.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/logging.cc b/util/logging.cc index ca6b324403..d48b6dd289 100644 --- a/util/logging.cc +++ b/util/logging.cc @@ -52,7 +52,8 @@ bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { char c = (*in)[0]; if (c >= '0' && c <= '9') { ++digits; - const int delta = (c - '0'); + // |delta| intentionally unit64_t to avoid Android crash (see log). + const uint64_t delta = (c - '0'); static const uint64_t kMaxUint64 = ~static_cast(0); if (v > kMaxUint64/10 || (v == kMaxUint64/10 && delta > kMaxUint64%10)) { From 141e7671359d5e6c65ff70460774b53b94371df1 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 1 Sep 2017 11:44:05 -0700 Subject: [PATCH 050/174] Simplify Table::Open() flow and remove a delete call. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=167303843 --- table/table.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/table/table.cc b/table/table.cc index decf8082cc..ff73ceedd9 100644 --- a/table/table.cc +++ b/table/table.cc @@ -55,22 +55,19 @@ Status Table::Open(const Options& options, if (!s.ok()) return s; // Read the index block - BlockContents contents; - Block* index_block = NULL; + BlockContents index_block_contents; if (s.ok()) { ReadOptions opt; if (options.paranoid_checks) { opt.verify_checksums = true; } - s = ReadBlock(file, opt, footer.index_handle(), &contents); - if (s.ok()) { - index_block = new Block(contents); - } + s = ReadBlock(file, opt, footer.index_handle(), &index_block_contents); } if (s.ok()) { // We've successfully read the footer and the index block: we're // ready to serve requests. + Block* index_block = new Block(index_block_contents); Rep* rep = new Table::Rep; rep->options = options; rep->file = file; @@ -81,8 +78,6 @@ Status Table::Open(const Options& options, rep->filter = NULL; *table = new Table(rep); (*table)->ReadMeta(footer); - } else { - delete index_block; } return s; From ea0a7586b8615fd39c6b8f5a8a21a1f242129c2f Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 1 Sep 2017 14:36:20 -0700 Subject: [PATCH 051/174] Remove confusing and unnecessary if. 12 lines above, there is an "if (!s.ok()) { return s; }" block of code. "s" is never modified between that block and the "if" removed by this CL, so "s.ok()" must be true. The code most likely intended to say "if (!builder->ok())", because the builder->Add() call above can modify the TableBuilder's status, as a side-effect. However, this approach would have required setting "s = builder.status()" in the "else" branch, near the "builder.Abandon()" call. So, removing the "if" outright is simpler than following that line of thought. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=167326229 --- db/builder.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/db/builder.cc b/db/builder.cc index f419882197..729f9fd4ac 100644 --- a/db/builder.cc +++ b/db/builder.cc @@ -41,14 +41,10 @@ Status BuildTable(const std::string& dbname, } // Finish and check for builder errors + s = builder->Finish(); if (s.ok()) { - s = builder->Finish(); - if (s.ok()) { - meta->file_size = builder->FileSize(); - assert(meta->file_size > 0); - } - } else { - builder->Abandon(); + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); } delete builder; From bcd9a8ea4a8aad23a3e101a23c61615bab2a093f Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 26 Sep 2017 13:32:01 -0700 Subject: [PATCH 052/174] Use portable CRC32C from google/crc32c. Benchmark results below. More results at https://github.com/google/crc32c/commit/354d61ef97eb73d4f0c1694cac6ec6116a5543e4. New, MacBookPro13,3 with Core i7 6920HQ: LevelDB: version 1.20 Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) WARNING: Snappy compression is not enabled ------------------------------------------------ fillseq : 2.952 micros/op; 37.5 MB/s fillsync : 43.932 micros/op; 2.5 MB/s (1000 ops) fillrandom : 3.856 micros/op; 28.7 MB/s overwrite : 4.053 micros/op; 27.3 MB/s readrandom : 4.234 micros/op; (1000000 of 1000000 found) readrandom : 3.923 micros/op; (1000000 of 1000000 found) readseq : 0.201 micros/op; 550.8 MB/s readreverse : 0.356 micros/op; 310.6 MB/s compact : 436800.000 micros/op; readrandom : 2.375 micros/op; (1000000 of 1000000 found) readseq : 0.151 micros/op; 734.3 MB/s readreverse : 0.298 micros/op; 370.7 MB/s fill100K : 554.075 micros/op; 172.1 MB/s (1000 ops) crc32c : 1.393 micros/op; 2805.0 MB/s (4K per op) snappycomp : 3902.000 micros/op; (snappy failure) snappyuncomp : 3821.000 micros/op; (snappy failure) acquireload : 13.088 micros/op; (each op is 1000 loads) Baseline, MacBookPro13,3 with Core i7 6920HQ: LevelDB: version 1.20 Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) WARNING: Snappy compression is not enabled ------------------------------------------------ fillseq : 3.000 micros/op; 36.9 MB/s fillsync : 46.721 micros/op; 2.4 MB/s (1000 ops) fillrandom : 3.922 micros/op; 28.2 MB/s overwrite : 4.080 micros/op; 27.1 MB/s readrandom : 4.409 micros/op; (1000000 of 1000000 found) readrandom : 3.895 micros/op; (1000000 of 1000000 found) readseq : 0.190 micros/op; 582.4 MB/s readreverse : 0.413 micros/op; 267.6 MB/s compact : 441076.000 micros/op; readrandom : 2.308 micros/op; (1000000 of 1000000 found) readseq : 0.170 micros/op; 651.2 MB/s readreverse : 0.302 micros/op; 366.2 MB/s fill100K : 614.289 micros/op; 155.3 MB/s (1000 ops) crc32c : 3.547 micros/op; 1101.2 MB/s (4K per op) snappycomp : 3393.000 micros/op; (snappy failure) snappyuncomp : 3171.000 micros/op; (snappy failure) acquireload : 12.761 micros/op; (each op is 1000 loads) ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170100372 --- util/crc32c.cc | 626 ++++++++++++++++++++++++++----------------------- 1 file changed, 328 insertions(+), 298 deletions(-) diff --git a/util/crc32c.cc b/util/crc32c.cc index edd61cfd6f..4f1d80fb08 100644 --- a/util/crc32c.cc +++ b/util/crc32c.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. // -// A portable implementation of crc32c, optimized to handle -// four bytes at a time. +// A portable implementation of crc32c. #include "util/crc32c.h" +#include #include #include "port/port.h" @@ -15,276 +15,253 @@ namespace leveldb { namespace crc32c { -static const uint32_t table0_[256] = { - 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, - 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, - 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, - 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, - 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, - 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, - 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, - 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, - 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, - 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, - 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, - 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, - 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, - 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, - 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, - 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, - 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, - 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, - 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, - 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, - 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, - 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, - 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, - 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, - 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, - 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, - 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, - 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, - 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, - 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, - 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, - 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, - 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, - 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, - 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, - 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, - 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, - 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, - 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, - 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, - 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, - 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, - 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, - 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, - 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, - 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, - 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, - 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, - 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, - 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, - 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, - 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, - 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, - 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, - 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, - 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, - 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, - 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, - 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, - 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, - 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, - 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, - 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, - 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 -}; -static const uint32_t table1_[256] = { - 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, - 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, - 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, - 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, - 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, - 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, - 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, - 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, - 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, - 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, - 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, - 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, - 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, - 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, - 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, - 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, - 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, - 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, - 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, - 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, - 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, - 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, - 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, - 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, - 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, - 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, - 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, - 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, - 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, - 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, - 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, - 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, - 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, - 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, - 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, - 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, - 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, - 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, - 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, - 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, - 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, - 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, - 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, - 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, - 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, - 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, - 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, - 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, - 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, - 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, - 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, - 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, - 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, - 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, - 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, - 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, - 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, - 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, - 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, - 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, - 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, - 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, - 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, - 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 -}; -static const uint32_t table2_[256] = { - 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, - 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, - 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, - 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, - 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, - 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, - 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, - 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, - 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, - 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, - 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, - 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, - 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, - 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, - 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, - 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, - 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, - 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, - 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, - 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, - 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, - 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, - 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, - 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, - 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, - 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, - 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, - 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, - 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, - 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, - 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, - 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, - 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, - 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, - 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, - 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, - 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, - 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, - 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, - 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, - 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, - 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, - 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, - 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, - 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, - 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, - 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, - 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, - 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, - 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, - 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, - 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, - 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, - 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, - 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, - 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, - 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, - 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, - 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, - 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, - 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, - 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, - 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, - 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 -}; -static const uint32_t table3_[256] = { - 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, - 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, - 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, - 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, - 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, - 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, - 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, - 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, - 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, - 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, - 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, - 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, - 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, - 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, - 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, - 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, - 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, - 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, - 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, - 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, - 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, - 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, - 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, - 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, - 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, - 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, - 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, - 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, - 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, - 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, - 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, - 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, - 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, - 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, - 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, - 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, - 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, - 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, - 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, - 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, - 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, - 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, - 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, - 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, - 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, - 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, - 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, - 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, - 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, - 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, - 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, - 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, - 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, - 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, - 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, - 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, - 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, - 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, - 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, - 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, - 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, - 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, - 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, - 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 -}; +namespace { -// Used to fetch a naturally-aligned 32-bit word in little endian byte-order -static inline uint32_t LE_LOAD32(const uint8_t *p) { - return DecodeFixed32(reinterpret_cast(p)); +const uint32_t kByteExtensionTable[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351}; + +const uint32_t kStrideExtensionTable0[256] = { + 0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1, + 0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76, + 0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526, + 0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478, + 0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b, + 0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229, + 0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a, + 0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664, + 0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34, + 0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3, + 0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69, + 0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37, + 0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924, + 0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0, + 0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3, + 0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad, + 0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b, + 0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc, + 0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac, + 0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2, + 0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1, + 0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7, + 0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4, + 0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa, + 0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa, + 0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d, + 0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb, + 0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5, + 0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6, + 0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572, + 0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061, + 0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f, + 0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5, + 0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262, + 0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32, + 0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c, + 0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f, + 0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d, + 0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e, + 0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970, + 0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120, + 0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7, + 0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433}; + +const uint32_t kStrideExtensionTable1[256] = { + 0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af, + 0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818, + 0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13, + 0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576, + 0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828, + 0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60, + 0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e, + 0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b, + 0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50, + 0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7, + 0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3, + 0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86, + 0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8, + 0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a, + 0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864, + 0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101, + 0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0, + 0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917, + 0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c, + 0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479, + 0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927, + 0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880, + 0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de, + 0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb, + 0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0, + 0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607, + 0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6, + 0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3, + 0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d, + 0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f, + 0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21, + 0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744, + 0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240, + 0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7, + 0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc, + 0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199, + 0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7, + 0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f, + 0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1, + 0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4, + 0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf, + 0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708, + 0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1}; + +const uint32_t kStrideExtensionTable2[256] = { + 0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4, + 0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418, + 0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37, + 0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0, + 0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9, + 0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f, + 0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276, + 0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81, + 0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae, + 0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42, + 0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328, + 0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf, + 0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6, + 0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c, + 0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605, + 0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2, + 0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1, + 0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d, + 0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972, + 0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185, + 0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c, + 0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0, + 0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9, + 0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e, + 0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361, + 0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d, + 0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce, + 0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339, + 0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20, + 0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa, + 0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3, + 0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614, + 0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e, + 0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092, + 0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd, + 0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a, + 0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53, + 0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5, + 0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc, + 0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b, + 0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124, + 0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8, + 0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d}; + +const uint32_t kStrideExtensionTable3[256] = { + 0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115, + 0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4, + 0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541, + 0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7, + 0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d, + 0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d, + 0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7, + 0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241, + 0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4, + 0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615, + 0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02, + 0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4, + 0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce, + 0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0, + 0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a, + 0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c, + 0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297, + 0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56, + 0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3, + 0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725, + 0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f, + 0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b, + 0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721, + 0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7, + 0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52, + 0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293, + 0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978, + 0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e, + 0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4, + 0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca, + 0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0, + 0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06, + 0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611, + 0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0, + 0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245, + 0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3, + 0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189, + 0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689, + 0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3, + 0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545, + 0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0, + 0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111, + 0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa}; + +// CRCs are pre- and post- conditioned by xoring with all ones. +static constexpr const uint32_t kCRC32Xor = static_cast(0xffffffffU); + +// Reads a little-endian 32-bit integer from a 32-bit-aligned buffer. +inline uint32_t ReadUint32LE(const uint8_t* buffer) { + return DecodeFixed32(reinterpret_cast(buffer)); } +// Returns the smallest address >= the given address that is aligned to N bytes. +// +// N must be a power of two. +template +constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) { + return reinterpret_cast( + (reinterpret_cast(pointer) + (N - 1)) + & ~static_cast(N - 1)); +} + +} // namespace + // Determine if the CPU running this program can accelerate the CRC32C // calculation. static bool CanAccelerateCRC32C() { @@ -302,48 +279,101 @@ uint32_t Extend(uint32_t crc, const char* buf, size_t size) { return port::AcceleratedCRC32C(crc, buf, size); } - const uint8_t *p = reinterpret_cast(buf); - const uint8_t *e = p + size; - uint32_t l = crc ^ 0xffffffffu; + const uint8_t* p = reinterpret_cast(buf); + const uint8_t* e = p + size; + uint32_t l = crc ^ kCRC32Xor; + +// Process one byte at a time. +#define STEP1 \ + do { \ + int c = (l & 0xff) ^ *p++; \ + l = kByteExtensionTable[c] ^ (l >> 8); \ + } while (0) -#define STEP1 do { \ - int c = (l & 0xff) ^ *p++; \ - l = table0_[c] ^ (l >> 8); \ -} while (0) -#define STEP4 do { \ - uint32_t c = l ^ LE_LOAD32(p); \ - p += 4; \ - l = table3_[c & 0xff] ^ \ - table2_[(c >> 8) & 0xff] ^ \ - table1_[(c >> 16) & 0xff] ^ \ - table0_[c >> 24]; \ -} while (0) +// Process one of the 4 strides of 4-byte data. +#define STEP4(s) \ + do { \ + crc##s = ReadUint32LE(p + s * 4) ^ kStrideExtensionTable3[crc##s & 0xff] ^ \ + kStrideExtensionTable2[(crc##s >> 8) & 0xff] ^ \ + kStrideExtensionTable1[(crc##s >> 16) & 0xff] ^ \ + kStrideExtensionTable0[crc##s >> 24]; \ + } while (0) - // Point x at first 4-byte aligned byte in string. This might be - // just past the end of the string. - const uintptr_t pval = reinterpret_cast(p); - const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); +// Process a 16-byte swath of 4 strides, each of which has 4 bytes of data. +#define STEP16 \ + do { \ + STEP4(0); \ + STEP4(1); \ + STEP4(2); \ + STEP4(3); \ + p += 16; \ + } while (0) + +// Process 4 bytes that were already loaded into a word. +#define STEP4W(w) \ + do { \ + w ^= l; \ + for (size_t i = 0; i < 4; ++i) { \ + w = (w >> 8) ^ kByteExtensionTable[w & 0xff]; \ + } \ + l = w; \ + } while (0) + + // Point x at first 4-byte aligned byte in the buffer. This might be past the + // end of the buffer. + const uint8_t* x = RoundUp<4>(p); if (x <= e) { - // Process bytes until finished or p is 4-byte aligned + // Process bytes p is 4-byte aligned. while (p != x) { STEP1; } } - // Process bytes 16 at a time - while ((e-p) >= 16) { - STEP4; STEP4; STEP4; STEP4; - } - // Process bytes 4 at a time - while ((e-p) >= 4) { - STEP4; + + if ((e - p) >= 16) { + // Load a 16-byte swath into the stride partial results. + uint32_t crc0 = ReadUint32LE(p + 0 * 4) ^ l; + uint32_t crc1 = ReadUint32LE(p + 1 * 4); + uint32_t crc2 = ReadUint32LE(p + 2 * 4); + uint32_t crc3 = ReadUint32LE(p + 3 * 4); + p += 16; + + // It is possible to get better speeds (at least on x86) by interleaving + // prefetching 256 bytes ahead with processing 64 bytes at a time. See the + // portable implementation in https://github.com/google/crc32c/. + + // Process one 16-byte swath at a time. + while ((e - p) >= 16) { + STEP16; + } + + // Advance one word at a time as far as possible. + while ((e - p) >= 4) { + STEP4(0); + uint32_t tmp = crc0; + crc0 = crc1; + crc1 = crc2; + crc2 = crc3; + crc3 = tmp; + p += 4; + } + + // Combine the 4 partial stride results. + l = 0; + STEP4W(crc0); + STEP4W(crc1); + STEP4W(crc2); + STEP4W(crc3); } - // Process the last few bytes + + // Process the last few bytes. while (p != e) { STEP1; } +#undef STEP4W +#undef STEP16 #undef STEP4 #undef STEP1 - return l ^ 0xffffffffu; + return l ^ kCRC32Xor; } } // namespace crc32c From 7e12c00ecf1bb725e212618e7026e4d34d6cd3bb Mon Sep 17 00:00:00 2001 From: sanjay Date: Mon, 2 Oct 2017 12:37:45 -0700 Subject: [PATCH 053/174] Fix issue 474: a race between the f*_unlocked() STDIO calls in env_posix.cc and concurrent application calls to fflush(NULL). The fix is to avoid using stdio in env_posix.cc but add our own buffering where we need it. Added a test to reproduce the bug. Added a test for Env reads/writes. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170738066 --- db/db_test.cc | 23 +++++--- util/env_posix.cc | 130 ++++++++++++++++++++++++++++++++-------------- util/env_test.cc | 50 ++++++++++++++++++ 3 files changed, 156 insertions(+), 47 deletions(-) diff --git a/db/db_test.cc b/db/db_test.cc index a0b08bc19c..edc3916e89 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -25,6 +25,13 @@ static std::string RandomString(Random* rnd, int len) { return r; } +static std::string RandomKey(Random* rnd) { + int len = (rnd->OneIn(3) + ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + namespace { class AtomicCounter { private: @@ -1394,6 +1401,15 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) { ASSERT_EQ("(->)(c->cv)", Contents()); } +TEST(DBTest, Fflush_Issue474) { + static const int kNum = 100000; + Random rnd(test::RandomSeed()); + for (int i = 0; i < kNum; i++) { + fflush(NULL); + ASSERT_OK(Put(RandomKey(&rnd), RandomString(&rnd, 100))); + } +} + TEST(DBTest, ComparatorCheck) { class NewComparator : public Comparator { public: @@ -1959,13 +1975,6 @@ class ModelDB: public DB { KVMap map_; }; -static std::string RandomKey(Random* rnd) { - int len = (rnd->OneIn(3) - ? 1 // Short sometimes to encourage collisions - : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); - return test::RandomKey(rnd, len); -} - static bool CompareIterators(int step, DB* model, DB* db, diff --git a/util/env_posix.cc b/util/env_posix.cc index b21f515a37..1a88802edc 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -34,6 +34,8 @@ namespace { static int open_read_only_file_limit = -1; static int mmap_limit = -1; +static const size_t kBufSize = 65536; + static Status PosixError(const std::string& context, int err_number) { if (err_number == ENOENT) { return Status::NotFound(context, strerror(err_number)); @@ -96,30 +98,32 @@ class Limiter { class PosixSequentialFile: public SequentialFile { private: std::string filename_; - FILE* file_; + int fd_; public: - PosixSequentialFile(const std::string& fname, FILE* f) - : filename_(fname), file_(f) { } - virtual ~PosixSequentialFile() { fclose(file_); } + PosixSequentialFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd) {} + virtual ~PosixSequentialFile() { close(fd_); } virtual Status Read(size_t n, Slice* result, char* scratch) { Status s; - size_t r = fread_unlocked(scratch, 1, n, file_); - *result = Slice(scratch, r); - if (r < n) { - if (feof(file_)) { - // We leave status as ok if we hit the end of the file - } else { - // A partial read with an error: return a non-ok status + while (true) { + ssize_t r = read(fd_, scratch, n); + if (r < 0) { + if (errno == EINTR) { + continue; // Retry + } s = PosixError(filename_, errno); + break; } + *result = Slice(scratch, r); + break; } return s; } virtual Status Skip(uint64_t n) { - if (fseek(file_, n, SEEK_CUR)) { + if (lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { return PosixError(filename_, errno); } return Status::OK(); @@ -213,42 +217,64 @@ class PosixMmapReadableFile: public RandomAccessFile { class PosixWritableFile : public WritableFile { private: + // buf_[0, pos_-1] contains data to be written to fd_. std::string filename_; - FILE* file_; + int fd_; + char buf_[kBufSize]; + size_t pos_; public: - PosixWritableFile(const std::string& fname, FILE* f) - : filename_(fname), file_(f) { } + PosixWritableFile(const std::string& fname, int fd) + : filename_(fname), fd_(fd), pos_(0) { } ~PosixWritableFile() { - if (file_ != NULL) { + if (fd_ >= 0) { // Ignoring any potential errors - fclose(file_); + FlushBuffered(); } } virtual Status Append(const Slice& data) { - size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); - if (r != data.size()) { - return PosixError(filename_, errno); + size_t n = data.size(); + const char* p = data.data(); + + // Fit as much as possible into buffer. + size_t copy = std::min(n, kBufSize - pos_); + memcpy(buf_ + pos_, p, copy); + p += copy; + n -= copy; + pos_ += copy; + if (n == 0) { + return Status::OK(); } - return Status::OK(); + + // Can't fit in buffer, so need to do at least one write. + Status s = FlushBuffered(); + if (!s.ok()) { + return s; + } + + // Small writes go to buffer, large writes are written directly. + if (n < kBufSize) { + memcpy(buf_, p, n); + pos_ = n; + return Status::OK(); + } + return WriteRaw(p, n); } virtual Status Close() { - Status result; - if (fclose(file_) != 0) { + Status result = FlushBuffered(); + const int r = close(fd_); + if (r < 0 && result.ok()) { result = PosixError(filename_, errno); } - file_ = NULL; + fd_ = -1; return result; } virtual Status Flush() { - if (fflush_unlocked(file_) != 0) { - return PosixError(filename_, errno); - } - return Status::OK(); + return FlushBuffered(); } Status SyncDirIfManifest() { @@ -284,12 +310,36 @@ class PosixWritableFile : public WritableFile { if (!s.ok()) { return s; } - if (fflush_unlocked(file_) != 0 || - fdatasync(fileno(file_)) != 0) { - s = Status::IOError(filename_, strerror(errno)); + s = FlushBuffered(); + if (s.ok()) { + if (fdatasync(fd_) != 0) { + s = PosixError(filename_, errno); + } } return s; } + + private: + Status FlushBuffered() { + Status s = WriteRaw(buf_, pos_); + pos_ = 0; + return s; + } + + Status WriteRaw(const char* p, size_t n) { + while (n > 0) { + ssize_t r = write(fd_, p, n); + if (r < 0) { + if (errno == EINTR) { + continue; // Retry + } + return PosixError(filename_, errno); + } + p += r; + n -= r; + } + return Status::OK(); + } }; static int LockOrUnlock(int fd, bool lock) { @@ -338,12 +388,12 @@ class PosixEnv : public Env { virtual Status NewSequentialFile(const std::string& fname, SequentialFile** result) { - FILE* f = fopen(fname.c_str(), "r"); - if (f == NULL) { + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { *result = NULL; return PosixError(fname, errno); } else { - *result = new PosixSequentialFile(fname, f); + *result = new PosixSequentialFile(fname, fd); return Status::OK(); } } @@ -379,12 +429,12 @@ class PosixEnv : public Env { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) { Status s; - FILE* f = fopen(fname.c_str(), "w"); - if (f == NULL) { + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { *result = NULL; s = PosixError(fname, errno); } else { - *result = new PosixWritableFile(fname, f); + *result = new PosixWritableFile(fname, fd); } return s; } @@ -392,12 +442,12 @@ class PosixEnv : public Env { virtual Status NewAppendableFile(const std::string& fname, WritableFile** result) { Status s; - FILE* f = fopen(fname.c_str(), "a"); - if (f == NULL) { + int fd = open(fname.c_str(), O_APPEND | O_RDWR | O_CREAT, 0644); + if (fd < 0) { *result = NULL; s = PosixError(fname, errno); } else { - *result = new PosixWritableFile(fname, f); + *result = new PosixWritableFile(fname, fd); } return s; } diff --git a/util/env_test.cc b/util/env_test.cc index 4d8ecc4a7f..53ec923bca 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -6,6 +6,7 @@ #include "port/port.h" #include "util/testharness.h" +#include "util/testutil.h" namespace leveldb { @@ -27,6 +28,55 @@ static void SetBool(void* ptr) { reinterpret_cast(ptr)->NoBarrier_Store(ptr); } + +TEST(EnvTest, ReadWrite) { + Random rnd(test::RandomSeed()); + + // Get file to use for testing. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/open_on_read.txt"; + WritableFile* wfile_tmp; + ASSERT_OK(env_->NewWritableFile(test_file_name, &wfile_tmp)); + std::unique_ptr wfile(wfile_tmp); + + // Fill a file with data generated via a sequence of randomly sized writes. + static const size_t kDataSize = 10 * 1048576; + std::string data; + while (data.size() < kDataSize) { + int len = rnd.Skewed(18); // Up to 2^18 - 1, but typically much smaller + std::string r; + test::RandomString(&rnd, len, &r); + ASSERT_OK(wfile->Append(r)); + data += r; + if (rnd.OneIn(10)) { + ASSERT_OK(wfile->Flush()); + } + } + ASSERT_OK(wfile->Sync()); + ASSERT_OK(wfile->Close()); + wfile.reset(); + + // Read all data using a sequence of randomly sized reads. + SequentialFile* rfile_tmp; + ASSERT_OK(env_->NewSequentialFile(test_file_name, &rfile_tmp)); + std::unique_ptr rfile(rfile_tmp); + std::string read_result; + std::string scratch; + while (read_result.size() < data.size()) { + int len = std::min(rnd.Skewed(18), data.size() - read_result.size()); + scratch.resize(std::max(len, 1)); // at least 1 so &scratch[0] is legal + Slice read; + ASSERT_OK(rfile->Read(len, &read, &scratch[0])); + if (len > 0) { + ASSERT_GT(read.size(), 0); + } + ASSERT_LE(read.size(), len); + read_result.append(read.data(), read.size()); + } + ASSERT_EQ(read_result, data); +} + TEST(EnvTest, RunImmediately) { port::AtomicPointer called (NULL); env_->Schedule(&SetBool, &called); From 1c75e88055e06da2939f9f4bd294625b76792815 Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 2 Oct 2017 13:57:41 -0700 Subject: [PATCH 054/174] Fix use of uninitialized value in LRUHandle. If leveldb::Options::block_cache is set to a cache of zero capacity then it is possible for LRUHandle::next to be used without having been set. Conditional jump or move depends on uninitialised value(s): leveldb::(anonymous namespace)::LRUHandle::key() const (cache.cc:58) leveldb::(anonymous namespace)::LRUCache::Unref(leveldb::(anonymous namespace)::LRUHandle*) (cache.cc:234) leveldb::(anonymous namespace)::LRUCache::Release(leveldb::Cache::Handle*) (cache.cc:266) leveldb::(anonymous namespace)::ShardedLRUCache::Release(leveldb::Cache::Handle*) (cache.cc:375) leveldb::CacheTest::Insert(int, int, int) (cache_test.cc:59) This bug forced a commit reversion in Chromium. For more information see https://bugs.chromium.org/p/chromium/issues/detail?id=761398#c4 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170749054 --- util/cache.cc | 5 ++++- util/cache_test.cc | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/util/cache.cc b/util/cache.cc index ce46886171..97b82ea6ff 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -288,7 +288,10 @@ Cache::Handle* LRUCache::Insert( LRU_Append(&in_use_, e); usage_ += charge; FinishErase(table_.Insert(e)); - } // else don't cache. (Tests use capacity_==0 to turn off caching.) + } else { + // don't cache. (It is valid to set capacity_==0 to turn off caching.) + e->next = NULL; + } while (usage_ > capacity_ && lru_.next != &lru_) { LRUHandle* old = lru_.next; diff --git a/util/cache_test.cc b/util/cache_test.cc index 468f7a6425..246ab8efdd 100644 --- a/util/cache_test.cc +++ b/util/cache_test.cc @@ -219,6 +219,14 @@ TEST(CacheTest, Prune) { ASSERT_EQ(-1, Lookup(2)); } +TEST(CacheTest, ZeroSizeCache) { + delete cache_; + cache_ = NewLRUCache(0); + + Insert(1, 100); + ASSERT_EQ(-1, Lookup(1)); +} + } // namespace leveldb int main(int argc, char** argv) { From 2372ac574fdeb1235e70cdd86a2681d1ce05cf65 Mon Sep 17 00:00:00 2001 From: sanjay Date: Mon, 2 Oct 2017 16:04:02 -0700 Subject: [PATCH 055/174] Fix file writing bug in CL 170738066. If the file already existed, we should have truncated it. This was not detected by leveldb tests since leveldb code avoids reusing same files, but there was code elsewhere that was directly using leveldb files and relying on this behavior. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170769101 --- util/env_posix.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 1a88802edc..8d84ac7295 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -429,7 +429,7 @@ class PosixEnv : public Env { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) { Status s; - int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + int fd = open(fname.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); if (fd < 0) { *result = NULL; s = PosixError(fname, errno); @@ -442,7 +442,7 @@ class PosixEnv : public Env { virtual Status NewAppendableFile(const std::string& fname, WritableFile** result) { Status s; - int fd = open(fname.c_str(), O_APPEND | O_RDWR | O_CREAT, 0644); + int fd = open(fname.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); if (fd < 0) { *result = NULL; s = PosixError(fname, errno); From 4447f9caced2bd09585c90f1b203c3aa8f4bbc40 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 3 Oct 2017 10:33:01 -0700 Subject: [PATCH 056/174] Remove handling for unused LRUHandle representation special case. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170876103 --- util/cache.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/util/cache.cc b/util/cache.cc index 97b82ea6ff..bd914aea32 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -53,13 +53,11 @@ struct LRUHandle { char key_data[1]; // Beginning of key Slice key() const { - // For cheaper lookups, we allow a temporary Handle object - // to store a pointer to a key in "value". - if (next == this) { - return *(reinterpret_cast(value)); - } else { - return Slice(key_data, key_length); - } + // next_ is only equal to this if the LRU handle is the list head of an + // empty list. List heads never have meaningful keys. + assert(next != this); + + return Slice(key_data, key_length); } }; @@ -288,11 +286,10 @@ Cache::Handle* LRUCache::Insert( LRU_Append(&in_use_, e); usage_ += charge; FinishErase(table_.Insert(e)); - } else { - // don't cache. (It is valid to set capacity_==0 to turn off caching.) + } else { // don't cache. (capacity_==0 is supported and turns off caching.) + // next is read by key() in an assert, so it must be initialized e->next = NULL; } - while (usage_ > capacity_ && lru_.next != &lru_) { LRUHandle* old = lru_.next; assert(old->refs == 1); From d9a9e02edf2b8187aa481416b36c49710026ab37 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 3 Oct 2017 10:38:02 -0700 Subject: [PATCH 057/174] leveldb: Add tests for CL 170769101. This also removes std::unique_ptr introduced in CL 170738066, because it's C++11-only, and the open source version still supports older versions at the moment. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170876919 --- util/env_test.cc | 71 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/util/env_test.cc b/util/env_test.cc index 53ec923bca..87177528af 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -36,9 +36,8 @@ TEST(EnvTest, ReadWrite) { std::string test_dir; ASSERT_OK(env_->GetTestDirectory(&test_dir)); std::string test_file_name = test_dir + "/open_on_read.txt"; - WritableFile* wfile_tmp; - ASSERT_OK(env_->NewWritableFile(test_file_name, &wfile_tmp)); - std::unique_ptr wfile(wfile_tmp); + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); // Fill a file with data generated via a sequence of randomly sized writes. static const size_t kDataSize = 10 * 1048576; @@ -47,27 +46,26 @@ TEST(EnvTest, ReadWrite) { int len = rnd.Skewed(18); // Up to 2^18 - 1, but typically much smaller std::string r; test::RandomString(&rnd, len, &r); - ASSERT_OK(wfile->Append(r)); + ASSERT_OK(writable_file->Append(r)); data += r; if (rnd.OneIn(10)) { - ASSERT_OK(wfile->Flush()); + ASSERT_OK(writable_file->Flush()); } } - ASSERT_OK(wfile->Sync()); - ASSERT_OK(wfile->Close()); - wfile.reset(); + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Close()); + delete writable_file; // Read all data using a sequence of randomly sized reads. - SequentialFile* rfile_tmp; - ASSERT_OK(env_->NewSequentialFile(test_file_name, &rfile_tmp)); - std::unique_ptr rfile(rfile_tmp); + SequentialFile* sequential_file; + ASSERT_OK(env_->NewSequentialFile(test_file_name, &sequential_file)); std::string read_result; std::string scratch; while (read_result.size() < data.size()) { int len = std::min(rnd.Skewed(18), data.size() - read_result.size()); scratch.resize(std::max(len, 1)); // at least 1 so &scratch[0] is legal Slice read; - ASSERT_OK(rfile->Read(len, &read, &scratch[0])); + ASSERT_OK(sequential_file->Read(len, &read, &scratch[0])); if (len > 0) { ASSERT_GT(read.size(), 0); } @@ -75,6 +73,7 @@ TEST(EnvTest, ReadWrite) { read_result.append(read.data(), read.size()); } ASSERT_EQ(read_result, data); + delete sequential_file; } TEST(EnvTest, RunImmediately) { @@ -167,6 +166,54 @@ TEST(EnvTest, TestOpenNonExistentFile) { ASSERT_TRUE(status.IsNotFound()); } +TEST(EnvTest, ReopenWritableFile) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/reopen_writable_file.txt"; + env_->DeleteFile(test_file_name); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); + std::string data("hello world!"); + ASSERT_OK(writable_file->Append(data)); + ASSERT_OK(writable_file->Close()); + delete writable_file; + + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); + data = "42"; + ASSERT_OK(writable_file->Append(data)); + ASSERT_OK(writable_file->Close()); + delete writable_file; + + ASSERT_OK(ReadFileToString(env_, test_file_name, &data)); + ASSERT_EQ(std::string("42"), data); + env_->DeleteFile(test_file_name); +} + +TEST(EnvTest, ReopenAppendableFile) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/reopen_appendable_file.txt"; + env_->DeleteFile(test_file_name); + + WritableFile* appendable_file; + ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file)); + std::string data("hello world!"); + ASSERT_OK(appendable_file->Append(data)); + ASSERT_OK(appendable_file->Close()); + delete appendable_file; + + ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file)); + data = "42"; + ASSERT_OK(appendable_file->Append(data)); + ASSERT_OK(appendable_file->Close()); + delete appendable_file; + + ASSERT_OK(ReadFileToString(env_, test_file_name, &data)); + ASSERT_EQ(std::string("hello world!42"), data); + env_->DeleteFile(test_file_name); +} + } // namespace leveldb int main(int argc, char** argv) { From 8ae7998aabae4f208d77afcb930dafabade1b28d Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 3 Oct 2017 13:41:46 -0700 Subject: [PATCH 058/174] Fix FD leak in POSIX Env. Deleting a PosixWritableFile without calling Close() leaks the file descriptor. While the API description in include/leveldb/env.h does not specify whether the caller is responsible for Close()ing the file before deleting it, all other Env file implementations do release underlying resources when destroyed, even if Close() is not called. The leak shows up when running db_tests on Mac Travis, or on a vanilla MacOS install. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=170906843 --- util/env_posix.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 8d84ac7295..8327978402 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -230,7 +230,7 @@ class PosixWritableFile : public WritableFile { ~PosixWritableFile() { if (fd_ >= 0) { // Ignoring any potential errors - FlushBuffered(); + Close(); } } From 542590d2a8eee3838f40b01405baa6d2f6f8c700 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 4 Oct 2017 10:04:21 -0700 Subject: [PATCH 059/174] leveldb: Include in util/env_test.cc. CL 170738066 introduced std::min and std::max to env_test.cc. These require the header. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171024062 --- util/env_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/env_test.cc b/util/env_test.cc index 87177528af..0bf71211e6 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -4,6 +4,8 @@ #include "leveldb/env.h" +#include + #include "port/port.h" #include "util/testharness.h" #include "util/testutil.h" From 4a7e7f50dcf661cfffe71737650b0fb18e195d18 Mon Sep 17 00:00:00 2001 From: cmumford Date: Wed, 4 Oct 2017 11:26:45 -0700 Subject: [PATCH 060/174] Add LEVELDB_EXPORT macro to export public symbols. gcc defaults to exporting all symbols, but other linkers do not. Adding the LEVELDB_EXPORT macro allows a project to set LEVELDB_SHARED_LIBRARY when building/linking with leveldb as a shared library. This is to allow leveldb to be created as a shared library on all platforms support by Chrome and enables a fix for https://bugs.chromium.org/p/chromium/issues/detail?id=764810. This also has the benefit of reducing the shared library size from 418863 to 380367 bytes (64-bit Linux). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171037148 --- Makefile | 28 ++-- build_detect_platform | 2 +- include/leveldb/c.h | 265 +++++++++++++++----------------- include/leveldb/cache.h | 7 +- include/leveldb/comparator.h | 5 +- include/leveldb/db.h | 13 +- include/leveldb/dumpfile.h | 4 +- include/leveldb/env.h | 27 ++-- include/leveldb/export.h | 33 ++++ include/leveldb/filter_policy.h | 6 +- include/leveldb/iterator.h | 7 +- include/leveldb/options.h | 7 +- include/leveldb/slice.h | 3 +- include/leveldb/status.h | 3 +- include/leveldb/table.h | 3 +- include/leveldb/table_builder.h | 3 +- include/leveldb/write_batch.h | 3 +- 17 files changed, 226 insertions(+), 193 deletions(-) create mode 100644 include/leveldb/export.h diff --git a/Makefile b/Makefile index f7cc7d736c..4bd02a662e 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ else STATIC_OUTDIR=out-static SHARED_OUTDIR=out-shared STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES)) -SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench) +SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench c_test) endif STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o)) @@ -98,6 +98,7 @@ SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL) +TEST_STATIC_OBJS := $(STATIC_OUTDIR)/port/port_posix.o $(STATIC_OUTDIR)/port/port_posix_sse.o $(STATIC_OUTDIR)/util/crc32c.o $(STATIC_OUTDIR)/util/histogram.o STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS))) STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS))) @@ -112,6 +113,8 @@ ifneq ($(PLATFORM_SHARED_EXT),) # Many leveldb test apps use non-exported API's. Only build a subset for testing. SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS) +SHARED_CXXFLAGS += -DLEVELDB_SHARED_LIBRARY +SHARED_BUILD_CXXFLAGS += $(SHARED_CXXFLAGS) -DLEVELDB_COMPILE_LIBRARY ifneq ($(PLATFORM_SHARED_VERSIONED),true) SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) @@ -386,13 +389,20 @@ $(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $( $(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) $(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS) -$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL) - $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) +$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL) $(TEST_STATIC_OBJS) + $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_CXXFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(TEST_STATIC_OBJS) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) -.PHONY: run-shared -run-shared: $(SHARED_OUTDIR)/db_bench +$(SHARED_OUTDIR)/c_test:$(SHARED_OUTDIR)/db/c_test.o $(SHARED_LIBS) $(TESTUTIL) $(TEST_STATIC_OBJS) + $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_CXXFLAGS) $(SHARED_OUTDIR)/db/c_test.o $(TESTUTIL) $(TEST_STATIC_OBJS) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) + +.PHONY: run-shared-db_bench +run-shared-db_bench: $(SHARED_OUTDIR)/db_bench LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench +.PHONY: run-shared-c_test +run-shared-c_test: $(SHARED_OUTDIR)/c_test + LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/c_test + $(SIMULATOR_OUTDIR)/%.o: %.cc xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@ @@ -412,13 +422,13 @@ $(STATIC_OUTDIR)/%.o: %.c $(CC) $(CFLAGS) -c $< -o $@ $(SHARED_OUTDIR)/%.o: %.cc - $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ $(SHARED_OUTDIR)/%.o: %.c - $(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ $(STATIC_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc - $(CXX) $(CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ $(SHARED_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc - $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ diff --git a/build_detect_platform b/build_detect_platform index 85d3594360..997730c299 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -61,7 +61,7 @@ PLATFORM_LDFLAGS= PLATFORM_LIBS= PLATFORM_SHARED_EXT="so" PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," -PLATFORM_SHARED_CFLAGS="-fPIC" +PLATFORM_SHARED_CFLAGS="-fPIC -fvisibility=hidden" PLATFORM_SHARED_VERSIONED=true PLATFORM_SSEFLAGS= diff --git a/include/leveldb/c.h b/include/leveldb/c.h index 1048fe3b86..7ac8121d54 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -47,6 +47,7 @@ extern "C" { #include #include #include +#include "leveldb/export.h" /* Exported types */ @@ -69,204 +70,178 @@ typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; /* DB operations */ -extern leveldb_t* leveldb_open( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT leveldb_t* leveldb_open(const leveldb_options_t* options, + const char* name, char** errptr); -extern void leveldb_close(leveldb_t* db); +LEVELDB_EXPORT void leveldb_close(leveldb_t* db); -extern void leveldb_put( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - const char* val, size_t vallen, - char** errptr); +LEVELDB_EXPORT void leveldb_put(leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, const char* val, + size_t vallen, char** errptr); -extern void leveldb_delete( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - char** errptr); +LEVELDB_EXPORT void leveldb_delete(leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); -extern void leveldb_write( - leveldb_t* db, - const leveldb_writeoptions_t* options, - leveldb_writebatch_t* batch, - char** errptr); +LEVELDB_EXPORT void leveldb_write(leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, char** errptr); /* Returns NULL if not found. A malloc()ed array otherwise. Stores the length of the array in *vallen. */ -extern char* leveldb_get( - leveldb_t* db, - const leveldb_readoptions_t* options, - const char* key, size_t keylen, - size_t* vallen, - char** errptr); +LEVELDB_EXPORT char* leveldb_get(leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, size_t* vallen, + char** errptr); -extern leveldb_iterator_t* leveldb_create_iterator( - leveldb_t* db, - const leveldb_readoptions_t* options); +LEVELDB_EXPORT leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, const leveldb_readoptions_t* options); -extern const leveldb_snapshot_t* leveldb_create_snapshot( - leveldb_t* db); +LEVELDB_EXPORT const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db); -extern void leveldb_release_snapshot( - leveldb_t* db, - const leveldb_snapshot_t* snapshot); +LEVELDB_EXPORT void leveldb_release_snapshot( + leveldb_t* db, const leveldb_snapshot_t* snapshot); /* Returns NULL if property name is unknown. Else returns a pointer to a malloc()-ed null-terminated value. */ -extern char* leveldb_property_value( - leveldb_t* db, - const char* propname); - -extern void leveldb_approximate_sizes( - leveldb_t* db, - int num_ranges, - const char* const* range_start_key, const size_t* range_start_key_len, - const char* const* range_limit_key, const size_t* range_limit_key_len, - uint64_t* sizes); - -extern void leveldb_compact_range( - leveldb_t* db, - const char* start_key, size_t start_key_len, - const char* limit_key, size_t limit_key_len); +LEVELDB_EXPORT char* leveldb_property_value(leveldb_t* db, + const char* propname); + +LEVELDB_EXPORT void leveldb_approximate_sizes( + leveldb_t* db, int num_ranges, const char* const* range_start_key, + const size_t* range_start_key_len, const char* const* range_limit_key, + const size_t* range_limit_key_len, uint64_t* sizes); + +LEVELDB_EXPORT void leveldb_compact_range(leveldb_t* db, const char* start_key, + size_t start_key_len, + const char* limit_key, + size_t limit_key_len); /* Management operations */ -extern void leveldb_destroy_db( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT void leveldb_destroy_db(const leveldb_options_t* options, + const char* name, char** errptr); -extern void leveldb_repair_db( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT void leveldb_repair_db(const leveldb_options_t* options, + const char* name, char** errptr); /* Iterator */ -extern void leveldb_iter_destroy(leveldb_iterator_t*); -extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); -extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); -extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); -extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); -extern void leveldb_iter_next(leveldb_iterator_t*); -extern void leveldb_iter_prev(leveldb_iterator_t*); -extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); -extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); -extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); +LEVELDB_EXPORT void leveldb_iter_destroy(leveldb_iterator_t*); +LEVELDB_EXPORT unsigned char leveldb_iter_valid(const leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek_to_first(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek_to_last(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek(leveldb_iterator_t*, const char* k, + size_t klen); +LEVELDB_EXPORT void leveldb_iter_next(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_prev(leveldb_iterator_t*); +LEVELDB_EXPORT const char* leveldb_iter_key(const leveldb_iterator_t*, + size_t* klen); +LEVELDB_EXPORT const char* leveldb_iter_value(const leveldb_iterator_t*, + size_t* vlen); +LEVELDB_EXPORT void leveldb_iter_get_error(const leveldb_iterator_t*, + char** errptr); /* Write batch */ -extern leveldb_writebatch_t* leveldb_writebatch_create(); -extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); -extern void leveldb_writebatch_clear(leveldb_writebatch_t*); -extern void leveldb_writebatch_put( - leveldb_writebatch_t*, - const char* key, size_t klen, - const char* val, size_t vlen); -extern void leveldb_writebatch_delete( - leveldb_writebatch_t*, - const char* key, size_t klen); -extern void leveldb_writebatch_iterate( - leveldb_writebatch_t*, - void* state, +LEVELDB_EXPORT leveldb_writebatch_t* leveldb_writebatch_create(); +LEVELDB_EXPORT void leveldb_writebatch_destroy(leveldb_writebatch_t*); +LEVELDB_EXPORT void leveldb_writebatch_clear(leveldb_writebatch_t*); +LEVELDB_EXPORT void leveldb_writebatch_put(leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +LEVELDB_EXPORT void leveldb_writebatch_delete(leveldb_writebatch_t*, + const char* key, size_t klen); +LEVELDB_EXPORT void leveldb_writebatch_iterate( + leveldb_writebatch_t*, void* state, void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), void (*deleted)(void*, const char* k, size_t klen)); /* Options */ -extern leveldb_options_t* leveldb_options_create(); -extern void leveldb_options_destroy(leveldb_options_t*); -extern void leveldb_options_set_comparator( - leveldb_options_t*, - leveldb_comparator_t*); -extern void leveldb_options_set_filter_policy( - leveldb_options_t*, - leveldb_filterpolicy_t*); -extern void leveldb_options_set_create_if_missing( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_error_if_exists( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_paranoid_checks( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); -extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); -extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); -extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); -extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); -extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); -extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); +LEVELDB_EXPORT leveldb_options_t* leveldb_options_create(); +LEVELDB_EXPORT void leveldb_options_destroy(leveldb_options_t*); +LEVELDB_EXPORT void leveldb_options_set_comparator(leveldb_options_t*, + leveldb_comparator_t*); +LEVELDB_EXPORT void leveldb_options_set_filter_policy(leveldb_options_t*, + leveldb_filterpolicy_t*); +LEVELDB_EXPORT void leveldb_options_set_create_if_missing(leveldb_options_t*, + unsigned char); +LEVELDB_EXPORT void leveldb_options_set_error_if_exists(leveldb_options_t*, + unsigned char); +LEVELDB_EXPORT void leveldb_options_set_paranoid_checks(leveldb_options_t*, + unsigned char); +LEVELDB_EXPORT void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +LEVELDB_EXPORT void leveldb_options_set_info_log(leveldb_options_t*, + leveldb_logger_t*); +LEVELDB_EXPORT void leveldb_options_set_write_buffer_size(leveldb_options_t*, + size_t); +LEVELDB_EXPORT void leveldb_options_set_max_open_files(leveldb_options_t*, int); +LEVELDB_EXPORT void leveldb_options_set_cache(leveldb_options_t*, + leveldb_cache_t*); +LEVELDB_EXPORT void leveldb_options_set_block_size(leveldb_options_t*, size_t); +LEVELDB_EXPORT void leveldb_options_set_block_restart_interval( + leveldb_options_t*, int); enum { leveldb_no_compression = 0, leveldb_snappy_compression = 1 }; -extern void leveldb_options_set_compression(leveldb_options_t*, int); +LEVELDB_EXPORT void leveldb_options_set_compression(leveldb_options_t*, int); /* Comparator */ -extern leveldb_comparator_t* leveldb_comparator_create( - void* state, - void (*destructor)(void*), - int (*compare)( - void*, - const char* a, size_t alen, - const char* b, size_t blen), +LEVELDB_EXPORT leveldb_comparator_t* leveldb_comparator_create( + void* state, void (*destructor)(void*), + int (*compare)(void*, const char* a, size_t alen, const char* b, + size_t blen), const char* (*name)(void*)); -extern void leveldb_comparator_destroy(leveldb_comparator_t*); +LEVELDB_EXPORT void leveldb_comparator_destroy(leveldb_comparator_t*); /* Filter policy */ -extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( - void* state, - void (*destructor)(void*), - char* (*create_filter)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length), - unsigned char (*key_may_match)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length), +LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, void (*destructor)(void*), + char* (*create_filter)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)(void*, const char* key, size_t length, + const char* filter, size_t filter_length), const char* (*name)(void*)); -extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); +LEVELDB_EXPORT void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); -extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( +LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( int bits_per_key); /* Read options */ -extern leveldb_readoptions_t* leveldb_readoptions_create(); -extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); -extern void leveldb_readoptions_set_verify_checksums( - leveldb_readoptions_t*, - unsigned char); -extern void leveldb_readoptions_set_fill_cache( +LEVELDB_EXPORT leveldb_readoptions_t* leveldb_readoptions_create(); +LEVELDB_EXPORT void leveldb_readoptions_destroy(leveldb_readoptions_t*); +LEVELDB_EXPORT void leveldb_readoptions_set_verify_checksums( leveldb_readoptions_t*, unsigned char); -extern void leveldb_readoptions_set_snapshot( - leveldb_readoptions_t*, - const leveldb_snapshot_t*); +LEVELDB_EXPORT void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t*, + unsigned char); +LEVELDB_EXPORT void leveldb_readoptions_set_snapshot(leveldb_readoptions_t*, + const leveldb_snapshot_t*); /* Write options */ -extern leveldb_writeoptions_t* leveldb_writeoptions_create(); -extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); -extern void leveldb_writeoptions_set_sync( - leveldb_writeoptions_t*, unsigned char); +LEVELDB_EXPORT leveldb_writeoptions_t* leveldb_writeoptions_create(); +LEVELDB_EXPORT void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +LEVELDB_EXPORT void leveldb_writeoptions_set_sync(leveldb_writeoptions_t*, + unsigned char); /* Cache */ -extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); -extern void leveldb_cache_destroy(leveldb_cache_t* cache); +LEVELDB_EXPORT leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +LEVELDB_EXPORT void leveldb_cache_destroy(leveldb_cache_t* cache); /* Env */ -extern leveldb_env_t* leveldb_create_default_env(); -extern void leveldb_env_destroy(leveldb_env_t*); +LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(); +LEVELDB_EXPORT void leveldb_env_destroy(leveldb_env_t*); /* Utility */ @@ -275,13 +250,13 @@ extern void leveldb_env_destroy(leveldb_env_t*); in this file. Note that in certain cases (typically on Windows), you may need to call this routine instead of free(ptr) to dispose of malloc()-ed memory returned by this library. */ -extern void leveldb_free(void* ptr); +LEVELDB_EXPORT void leveldb_free(void* ptr); /* Return the major version number for this release. */ -extern int leveldb_major_version(); +LEVELDB_EXPORT int leveldb_major_version(); /* Return the minor version number for this release. */ -extern int leveldb_minor_version(); +LEVELDB_EXPORT int leveldb_minor_version(); #ifdef __cplusplus } /* end extern "C" */ diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h index 6819d5bc49..ed18baaa50 100644 --- a/include/leveldb/cache.h +++ b/include/leveldb/cache.h @@ -19,17 +19,18 @@ #define STORAGE_LEVELDB_INCLUDE_CACHE_H_ #include +#include "leveldb/export.h" #include "leveldb/slice.h" namespace leveldb { -class Cache; +class LEVELDB_EXPORT Cache; // Create a new cache with a fixed size capacity. This implementation // of Cache uses a least-recently-used eviction policy. -extern Cache* NewLRUCache(size_t capacity); +LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity); -class Cache { +class LEVELDB_EXPORT Cache { public: Cache() { } diff --git a/include/leveldb/comparator.h b/include/leveldb/comparator.h index 556b984c76..9b09684a6b 100644 --- a/include/leveldb/comparator.h +++ b/include/leveldb/comparator.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ #include +#include "leveldb/export.h" namespace leveldb { @@ -15,7 +16,7 @@ class Slice; // used as keys in an sstable or a database. A Comparator implementation // must be thread-safe since leveldb may invoke its methods concurrently // from multiple threads. -class Comparator { +class LEVELDB_EXPORT Comparator { public: virtual ~Comparator(); @@ -56,7 +57,7 @@ class Comparator { // Return a builtin comparator that uses lexicographic byte-wise // ordering. The result remains the property of this module and // must not be deleted. -extern const Comparator* BytewiseComparator(); +LEVELDB_EXPORT const Comparator* BytewiseComparator(); } // namespace leveldb diff --git a/include/leveldb/db.h b/include/leveldb/db.h index bfab10a0b7..9a18c92f1a 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -7,6 +7,7 @@ #include #include +#include "leveldb/export.h" #include "leveldb/iterator.h" #include "leveldb/options.h" @@ -24,13 +25,13 @@ class WriteBatch; // Abstract handle to particular state of a DB. // A Snapshot is an immutable object and can therefore be safely // accessed from multiple threads without any external synchronization. -class Snapshot { +class LEVELDB_EXPORT Snapshot { protected: virtual ~Snapshot(); }; // A range of keys -struct Range { +struct LEVELDB_EXPORT Range { Slice start; // Included in the range Slice limit; // Not included in the range @@ -41,7 +42,7 @@ struct Range { // A DB is a persistent ordered map from keys to values. // A DB is safe for concurrent access from multiple threads without // any external synchronization. -class DB { +class LEVELDB_EXPORT DB { public: // Open the database with the specified "name". // Stores a pointer to a heap-allocated database in *dbptr and returns @@ -150,13 +151,15 @@ class DB { // Destroy the contents of the specified database. // Be very careful using this method. -Status DestroyDB(const std::string& name, const Options& options); +LEVELDB_EXPORT Status DestroyDB(const std::string& name, + const Options& options); // If a DB cannot be opened, you may attempt to call this method to // resurrect as much of the contents of the database as possible. // Some data may be lost, so be careful when calling this function // on a database that contains important information. -Status RepairDB(const std::string& dbname, const Options& options); +LEVELDB_EXPORT Status RepairDB(const std::string& dbname, + const Options& options); } // namespace leveldb diff --git a/include/leveldb/dumpfile.h b/include/leveldb/dumpfile.h index 3f97fda16b..659774163b 100644 --- a/include/leveldb/dumpfile.h +++ b/include/leveldb/dumpfile.h @@ -7,6 +7,7 @@ #include #include "leveldb/env.h" +#include "leveldb/export.h" #include "leveldb/status.h" namespace leveldb { @@ -18,7 +19,8 @@ namespace leveldb { // // Returns a non-OK result if fname does not name a leveldb storage // file, or if the file cannot be read. -Status DumpFile(Env* env, const std::string& fname, WritableFile* dst); +LEVELDB_EXPORT Status DumpFile(Env* env, const std::string& fname, + WritableFile* dst); } // namespace leveldb diff --git a/include/leveldb/env.h b/include/leveldb/env.h index cb8b6a465b..d698169e65 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -13,10 +13,11 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ #define STORAGE_LEVELDB_INCLUDE_ENV_H_ -#include -#include #include #include +#include +#include +#include "leveldb/export.h" #include "leveldb/status.h" namespace leveldb { @@ -28,7 +29,7 @@ class SequentialFile; class Slice; class WritableFile; -class Env { +class LEVELDB_EXPORT Env { public: Env() { } virtual ~Env(); @@ -169,7 +170,7 @@ class Env { }; // A file abstraction for reading sequentially through a file -class SequentialFile { +class LEVELDB_EXPORT SequentialFile { public: SequentialFile() { } virtual ~SequentialFile(); @@ -200,7 +201,7 @@ class SequentialFile { }; // A file abstraction for randomly reading the contents of a file. -class RandomAccessFile { +class LEVELDB_EXPORT RandomAccessFile { public: RandomAccessFile() { } virtual ~RandomAccessFile(); @@ -226,7 +227,7 @@ class RandomAccessFile { // A file abstraction for sequential writing. The implementation // must provide buffering since callers may append small fragments // at a time to the file. -class WritableFile { +class LEVELDB_EXPORT WritableFile { public: WritableFile() { } virtual ~WritableFile(); @@ -243,7 +244,7 @@ class WritableFile { }; // An interface for writing log messages. -class Logger { +class LEVELDB_EXPORT Logger { public: Logger() { } virtual ~Logger(); @@ -259,7 +260,7 @@ class Logger { // Identifies a locked file. -class FileLock { +class LEVELDB_EXPORT FileLock { public: FileLock() { } virtual ~FileLock(); @@ -277,17 +278,17 @@ extern void Log(Logger* info_log, const char* format, ...) ; // A utility routine: write "data" to the named file. -extern Status WriteStringToFile(Env* env, const Slice& data, - const std::string& fname); +LEVELDB_EXPORT Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); // A utility routine: read contents of named file into *data -extern Status ReadFileToString(Env* env, const std::string& fname, - std::string* data); +LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); // An implementation of Env that forwards all calls to another Env. // May be useful to clients who wish to override just part of the // functionality of another Env. -class EnvWrapper : public Env { +class LEVELDB_EXPORT EnvWrapper : public Env { public: // Initialize an EnvWrapper that delegates all calls to *t explicit EnvWrapper(Env* t) : target_(t) { } diff --git a/include/leveldb/export.h b/include/leveldb/export.h new file mode 100644 index 0000000000..39b20972a6 --- /dev/null +++ b/include/leveldb/export.h @@ -0,0 +1,33 @@ +// Copyright (c) 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_EXPORT_H_ +#define STORAGE_LEVELDB_INCLUDE_EXPORT_H_ + +#if !defined(LEVELDB_EXPORT) + +#if defined(LEVELDB_SHARED_LIBRARY) +#if defined(OS_WIN) + +#if defined(LEVELDB_COMPILE_LIBRARY) +#define LEVELDB_EXPORT __declspec(dllexport) +#else +#define LEVELDB_EXPORT __declspec(dllimport) +#endif // defined(LEVELDB_COMPILE_LIBRARY) + +#else // defined(OS_WIN) +#if defined(LEVELDB_COMPILE_LIBRARY) +#define LEVELDB_EXPORT __attribute__((visibility("default"))) +#else +#define LEVELDB_EXPORT +#endif +#endif // defined(OS_WIN) + +#else // defined(LEVELDB_SHARED_LIBRARY) +#define LEVELDB_EXPORT +#endif + +#endif // !defined(LEVELDB_EXPORT) + +#endif // STORAGE_LEVELDB_INCLUDE_EXPORT_H_ diff --git a/include/leveldb/filter_policy.h b/include/leveldb/filter_policy.h index 1fba08001f..c3a5d208a6 100644 --- a/include/leveldb/filter_policy.h +++ b/include/leveldb/filter_policy.h @@ -17,12 +17,13 @@ #define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ #include +#include "leveldb/export.h" namespace leveldb { class Slice; -class FilterPolicy { +class LEVELDB_EXPORT FilterPolicy { public: virtual ~FilterPolicy(); @@ -63,8 +64,7 @@ class FilterPolicy { // ignores trailing spaces, it would be incorrect to use a // FilterPolicy (like NewBloomFilterPolicy) that does not ignore // trailing spaces in keys. -extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); - +LEVELDB_EXPORT const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); } #endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/include/leveldb/iterator.h b/include/leveldb/iterator.h index da631ed9d8..ff3b56629e 100644 --- a/include/leveldb/iterator.h +++ b/include/leveldb/iterator.h @@ -15,12 +15,13 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ #define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#include "leveldb/export.h" #include "leveldb/slice.h" #include "leveldb/status.h" namespace leveldb { -class Iterator { +class LEVELDB_EXPORT Iterator { public: Iterator(); virtual ~Iterator(); @@ -90,10 +91,10 @@ class Iterator { }; // Return an empty iterator (yields nothing). -extern Iterator* NewEmptyIterator(); +LEVELDB_EXPORT Iterator* NewEmptyIterator(); // Return an empty iterator with the specified status. -extern Iterator* NewErrorIterator(const Status& status); +LEVELDB_EXPORT Iterator* NewErrorIterator(const Status& status); } // namespace leveldb diff --git a/include/leveldb/options.h b/include/leveldb/options.h index 976e38122a..1c4292157c 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ #include +#include "leveldb/export.h" namespace leveldb { @@ -28,7 +29,7 @@ enum CompressionType { }; // Options to control the behavior of a database (passed to DB::Open) -struct Options { +struct LEVELDB_EXPORT Options { // ------------------- // Parameters that affect behavior @@ -158,7 +159,7 @@ struct Options { }; // Options that control read operations -struct ReadOptions { +struct LEVELDB_EXPORT ReadOptions { // If true, all data read from underlying storage will be // verified against corresponding checksums. // Default: false @@ -184,7 +185,7 @@ struct ReadOptions { }; // Options that control write operations -struct WriteOptions { +struct LEVELDB_EXPORT WriteOptions { // If true, the write will be flushed from the operating system // buffer cache (by calling WritableFile::Sync()) before the write // is considered complete. If this flag is true, writes will be diff --git a/include/leveldb/slice.h b/include/leveldb/slice.h index bc367986f7..b56a4e4f35 100644 --- a/include/leveldb/slice.h +++ b/include/leveldb/slice.h @@ -19,10 +19,11 @@ #include #include #include +#include "leveldb/export.h" namespace leveldb { -class Slice { +class LEVELDB_EXPORT Slice { public: // Create an empty slice. Slice() : data_(""), size_(0) { } diff --git a/include/leveldb/status.h b/include/leveldb/status.h index d9575f9753..42ad4bbbcc 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -14,11 +14,12 @@ #define STORAGE_LEVELDB_INCLUDE_STATUS_H_ #include +#include "leveldb/export.h" #include "leveldb/slice.h" namespace leveldb { -class Status { +class LEVELDB_EXPORT Status { public: // Create a success status. Status() : state_(NULL) { } diff --git a/include/leveldb/table.h b/include/leveldb/table.h index a9746c3f5e..2f54c4ea3c 100644 --- a/include/leveldb/table.h +++ b/include/leveldb/table.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_H_ #include +#include "leveldb/export.h" #include "leveldb/iterator.h" namespace leveldb { @@ -21,7 +22,7 @@ class TableCache; // A Table is a sorted map from strings to strings. Tables are // immutable and persistent. A Table may be safely accessed from // multiple threads without external synchronization. -class Table { +class LEVELDB_EXPORT Table { public: // Attempt to open the table that is stored in bytes [0..file_size) // of "file", and read the metadata entries necessary to allow diff --git a/include/leveldb/table_builder.h b/include/leveldb/table_builder.h index 5fd1dc71f1..3c3acc7327 100644 --- a/include/leveldb/table_builder.h +++ b/include/leveldb/table_builder.h @@ -14,6 +14,7 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ #include +#include "leveldb/export.h" #include "leveldb/options.h" #include "leveldb/status.h" @@ -23,7 +24,7 @@ class BlockBuilder; class BlockHandle; class WritableFile; -class TableBuilder { +class LEVELDB_EXPORT TableBuilder { public: // Create a builder that will store the contents of the table it is // building in *file. Does not close the file. It is up to the diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 65a76d84d3..40ceb03210 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -22,13 +22,14 @@ #define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ #include +#include "leveldb/export.h" #include "leveldb/status.h" namespace leveldb { class Slice; -class WriteBatch { +class LEVELDB_EXPORT WriteBatch { public: WriteBatch(); ~WriteBatch(); From 25767d066ca995c055f04b78a31a6e518087e667 Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 5 Oct 2017 08:08:48 -0700 Subject: [PATCH 061/174] leveldb: Remove *_unlocked feature detection from POSIX port. CL 170738066 removed all instances of fread_unlocked, fwrite_unlocked and fflush_unlocked calls from the codebase, so the feature detection can be removed as well. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171154269 --- port/port_posix.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/port/port_posix.h b/port/port_posix.h index 24915512c2..1f4f6fc1a6 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -50,15 +50,6 @@ #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) #endif -#if defined(__APPLE__) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ - defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ - defined(OS_ANDROID) || defined(OS_HPUX) || defined(CYGWIN) -// Use fread/fwrite/fflush on platforms without _unlocked variants -#define fread_unlocked fread -#define fwrite_unlocked fwrite -#define fflush_unlocked fflush -#endif - #if defined(__APPLE__) || defined(OS_FREEBSD) ||\ defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) // Use fsync() on platforms without fdatasync() From ca216e493f32278f50a823811ab95f64cf0f839b Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 5 Oct 2017 11:47:39 -0700 Subject: [PATCH 062/174] leveldb: Rename SNAPPY to HAVE_SNAPPY. This follows the general naming convention for preprocessor macros used to detect feature (library / header file / symbol) presence. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171184641 --- build_detect_platform | 12 ++++++------ port/port_posix.h | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build_detect_platform b/build_detect_platform index 997730c299..d1bb17e370 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -21,8 +21,8 @@ # The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: # # -DLEVELDB_ATOMIC_PRESENT if is present -# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms -# -DSNAPPY if the Snappy library is present +# -DLEVELDB_PLATFORM_POSIX=1 for Posix-based platforms +# -DHAVE_SNAPPY=1 if the Snappy library is present # OUTPUT=$1 @@ -133,7 +133,7 @@ case "$TARGET_OS" in ;; OS_ANDROID_CROSSCOMPILE) PLATFORM=OS_ANDROID - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" + COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX=1" PLATFORM_LDFLAGS="" # All pthread features are in the Android C library PORT_FILE=port/port_posix.cc PORT_SSE_FILE=port/port_posix_sse.cc @@ -196,10 +196,10 @@ else int main() {} EOF if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT" + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX=1 -DLEVELDB_ATOMIC_PRESENT" PLATFORM_CXXFLAGS="-std=c++0x" else - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX=1" fi # Test whether Snappy library is installed @@ -209,7 +209,7 @@ EOF int main() {} EOF if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY" + COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SNAPPY=1" PLATFORM_LIBS="$PLATFORM_LIBS -lsnappy" fi diff --git a/port/port_posix.h b/port/port_posix.h index 1f4f6fc1a6..2bc5223d5b 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -39,9 +39,9 @@ #endif #include -#ifdef SNAPPY +#ifdef HAVE_SNAPPY #include -#endif +#endif // defined(HAVE_SNAPPY) #include #include #include "port/atomic_pointer.h" @@ -106,33 +106,33 @@ extern void InitOnce(OnceType* once, void (*initializer)()); inline bool Snappy_Compress(const char* input, size_t length, ::std::string* output) { -#ifdef SNAPPY +#ifdef HAVE_SNAPPY output->resize(snappy::MaxCompressedLength(length)); size_t outlen; snappy::RawCompress(input, length, &(*output)[0], &outlen); output->resize(outlen); return true; -#endif +#endif // defined(HAVE_SNAPPY) return false; } inline bool Snappy_GetUncompressedLength(const char* input, size_t length, size_t* result) { -#ifdef SNAPPY +#ifdef HAVE_SNAPPY return snappy::GetUncompressedLength(input, length, result); #else return false; -#endif +#endif // defined(HAVE_SNAPPY) } inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { -#ifdef SNAPPY +#ifdef HAVE_SNAPPY return snappy::RawUncompress(input, length, output); #else return false; -#endif +#endif // defined(HAVE_SNAPPY) } inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { From 5c39524f3639e6bf6ab49215152d24273e662986 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 10 Oct 2017 06:05:17 -0700 Subject: [PATCH 063/174] Replace SSE-optimized CRC32C in POSIX port with external library. Maintaining a hardware-accelerated CRC32C implementation tailored for all modern platforms deserves a repository of its own. We extracted the implementation here into https://github.com/google/crc32c and improved it in that repository. This CL removes the SSE-optimized implementation from this codebase, and adds the ability to use the google/crc32c library, if it is present on the system. The benchmarks below show the performance impact of the change. In summary, open source builds that use the google/crc32c library can expect a 3x improvement in CRC32C throughput, whereas builds that do not use the library will see a 50% drop in CRC32C throughput. This translates in much smaller changes in overall leveldb performance. Baseline, MacBookPro13,3 with Core i7 6920HQ: LevelDB: version 1.20 Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 3.064 micros/op; 36.1 MB/s fillsync : 57.861 micros/op; 1.9 MB/s (1000 ops) fillrandom : 3.887 micros/op; 28.5 MB/s overwrite : 4.140 micros/op; 26.7 MB/s readrandom : 7.433 micros/op; (1000000 of 1000000 found) readrandom : 6.825 micros/op; (1000000 of 1000000 found) readseq : 0.244 micros/op; 453.4 MB/s readreverse : 0.387 micros/op; 285.8 MB/s compact : 449707.000 micros/op; readrandom : 4.196 micros/op; (1000000 of 1000000 found) readseq : 0.228 micros/op; 485.8 MB/s readreverse : 0.320 micros/op; 345.2 MB/s fill100K : 562.556 micros/op; 169.6 MB/s (1000 ops) crc32c : 0.768 micros/op; 5085.0 MB/s (4K per op) snappycomp : 4.220 micros/op; 925.7 MB/s (output: 55.1%) snappyuncomp : 0.635 micros/op; 6155.7 MB/s acquireload : 13.054 micros/op; (each op is 1000 loads) New with crc32c, MacBookPro13,3 with Core i7 6920HQ: LevelDB: version 1.20 Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 2.820 micros/op; 39.2 MB/s fillsync : 51.988 micros/op; 2.1 MB/s (1000 ops) fillrandom : 3.747 micros/op; 29.5 MB/s overwrite : 4.047 micros/op; 27.3 MB/s readrandom : 7.287 micros/op; (1000000 of 1000000 found) readrandom : 6.927 micros/op; (1000000 of 1000000 found) readseq : 0.253 micros/op; 437.5 MB/s readreverse : 0.411 micros/op; 269.2 MB/s compact : 440405.000 micros/op; readrandom : 4.159 micros/op; (1000000 of 1000000 found) readseq : 0.230 micros/op; 481.1 MB/s readreverse : 0.320 micros/op; 345.9 MB/s fill100K : 558.222 micros/op; 170.9 MB/s (1000 ops) crc32c : 0.214 micros/op; 18263.5 MB/s (4K per op) snappycomp : 4.471 micros/op; 873.7 MB/s (output: 55.1%) snappyuncomp : 0.833 micros/op; 4688.5 MB/s acquireload : 13.289 micros/op; (each op is 1000 loads) New without crc32c, MacBookPro13,3 with Core i7 6920HQ LevelDB: version 1.20 Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) ------------------------------------------------ fillseq : 3.094 micros/op; 35.8 MB/s fillsync : 52.160 micros/op; 2.1 MB/s (1000 ops) fillrandom : 4.090 micros/op; 27.0 MB/s overwrite : 4.006 micros/op; 27.6 MB/s readrandom : 6.584 micros/op; (1000000 of 1000000 found) readrandom : 6.676 micros/op; (1000000 of 1000000 found) readseq : 0.280 micros/op; 395.2 MB/s readreverse : 0.391 micros/op; 283.2 MB/s compact : 433911.000 micros/op; readrandom : 4.261 micros/op; (1000000 of 1000000 found) readseq : 0.251 micros/op; 440.5 MB/s readreverse : 0.356 micros/op; 310.9 MB/s fill100K : 584.023 micros/op; 163.3 MB/s (1000 ops) crc32c : 1.384 micros/op; 2822.3 MB/s (4K per op) snappycomp : 4.763 micros/op; 820.1 MB/s (output: 55.1%) snappyuncomp : 0.766 micros/op; 5098.6 MB/s acquireload : 12.931 micros/op; (each op is 1000 loads) ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171667771 --- Makefile | 10 +--- build_detect_platform | 43 +++++-------- port/port_posix.h | 15 ++++- port/port_posix_sse.cc | 133 ----------------------------------------- 4 files changed, 29 insertions(+), 172 deletions(-) delete mode 100644 port/port_posix_sse.cc diff --git a/Makefile b/Makefile index 4bd02a662e..82dd84a9a4 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL) -TEST_STATIC_OBJS := $(STATIC_OUTDIR)/port/port_posix.o $(STATIC_OUTDIR)/port/port_posix_sse.o $(STATIC_OUTDIR)/util/crc32c.o $(STATIC_OUTDIR)/util/histogram.o +TEST_STATIC_OBJS := $(STATIC_OUTDIR)/port/port_posix.o $(STATIC_OUTDIR)/util/crc32c.o $(STATIC_OUTDIR)/util/histogram.o STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS))) STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS))) @@ -425,10 +425,4 @@ $(SHARED_OUTDIR)/%.o: %.cc $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ $(SHARED_OUTDIR)/%.o: %.c - $(CC) $(CFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ - -$(STATIC_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc - $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ - -$(SHARED_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc - $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/build_detect_platform b/build_detect_platform index d1bb17e370..b7a859f830 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -22,6 +22,7 @@ # # -DLEVELDB_ATOMIC_PRESENT if is present # -DLEVELDB_PLATFORM_POSIX=1 for Posix-based platforms +# -DHAVE_CRC32C=1 if the CRC32C library is present # -DHAVE_SNAPPY=1 if the Snappy library is present # @@ -63,7 +64,6 @@ PLATFORM_SHARED_EXT="so" PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," PLATFORM_SHARED_CFLAGS="-fPIC -fvisibility=hidden" PLATFORM_SHARED_VERSIONED=true -PLATFORM_SSEFLAGS= MEMCMP_FLAG= if [ "$CXX" = "g++" ]; then @@ -78,7 +78,6 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" PLATFORM_LDFLAGS="-lpthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; Darwin) PLATFORM=OS_MACOSX @@ -87,56 +86,48 @@ case "$TARGET_OS" in [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; Linux) PLATFORM=OS_LINUX COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; SunOS) PLATFORM=OS_SOLARIS COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" PLATFORM_LIBS="-lpthread -lrt" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; FreeBSD) PLATFORM=OS_FREEBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" PLATFORM_LIBS="-lpthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; NetBSD) PLATFORM=OS_NETBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" PLATFORM_LIBS="-lpthread -lgcc_s" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; OpenBSD) PLATFORM=OS_OPENBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; DragonFly) PLATFORM=OS_DRAGONFLYBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" PLATFORM_LIBS="-lpthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc ;; OS_ANDROID_CROSSCOMPILE) PLATFORM=OS_ANDROID COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX=1" PLATFORM_LDFLAGS="" # All pthread features are in the Android C library PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc CROSS_COMPILE=true ;; HP-UX) @@ -144,7 +135,6 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc # man ld: +h internal_name PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," ;; @@ -153,7 +143,6 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG" [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PORT_FILE=port/port_posix.cc - PORT_SSE_FILE=port/port_posix_sse.cc PLATFORM_SHARED_EXT= PLATFORM_SHARED_LDFLAGS= PLATFORM_SHARED_CFLAGS= @@ -180,7 +169,7 @@ set +f # re-enable globbing # The sources consist of the portable files, plus the platform-specific port # file. -echo "SOURCES=$PORTABLE_FILES $PORT_FILE $PORT_SSE_FILE" >> $OUTPUT +echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT if [ "$CROSS_COMPILE" = "true" ]; then @@ -202,8 +191,20 @@ EOF COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX=1" fi + # Test whether CRC32C library is installed + # https://github.com/google/crc32c + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DHAVE_CRC32C=1" + PLATFORM_LIBS="$PLATFORM_LIBS -lcrc32c" + fi + + # Test whether Snappy library is installed - # http://code.google.com/p/snappy/ + # https://github.com/google/snappy $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < int main() {} @@ -223,22 +224,9 @@ EOF rm -f $CXXOUTPUT 2>/dev/null - # Test if gcc SSE 4.2 is supported - $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -msse4.2 2>/dev/null </dev/null fi -# Use the SSE 4.2 CRC32C intrinsics iff runtime checks indicate compiler supports them. -if [ -n "$PLATFORM_SSEFLAGS" ]; then - PLATFORM_SSEFLAGS="$PLATFORM_SSEFLAGS -DLEVELDB_PLATFORM_POSIX_SSE" -fi - PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" @@ -249,7 +237,6 @@ echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT -echo "PLATFORM_SSEFLAGS=$PLATFORM_SSEFLAGS" >> $OUTPUT echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT diff --git a/port/port_posix.h b/port/port_posix.h index 2bc5223d5b..2a30be3a1d 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -39,6 +39,9 @@ #endif #include +#if defined(HAVE_CRC32C) +#include +#endif // defined(HAVE_CRC32C) #ifdef HAVE_SNAPPY #include #endif // defined(HAVE_SNAPPY) @@ -139,9 +142,15 @@ inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { return false; } -uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); +inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { +#if defined(HAVE_CRC32C) + return ::crc32c::Extend(crc, reinterpret_cast(buf), size); +#else + return 0; +#endif // defined(HAVE_CRC32C) +} -} // namespace port -} // namespace leveldb +} // namespace port +} // namespace leveldb #endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/port/port_posix_sse.cc b/port/port_posix_sse.cc deleted file mode 100644 index 08d9aee1e3..0000000000 --- a/port/port_posix_sse.cc +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. -// -// A portable implementation of crc32c, optimized to handle -// four bytes at a time. -// -// In a separate source file to allow this accelerated CRC32C function to be -// compiled with the appropriate compiler flags to enable x86 SSE 4.2 -// instructions. - -#include -#include -#include "port/port.h" - -#if defined(LEVELDB_PLATFORM_POSIX_SSE) - -#if defined(_MSC_VER) -#include -#elif defined(__GNUC__) && defined(__SSE4_2__) -#include -#include -#endif - -#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) - -namespace leveldb { -namespace port { - -#if defined(LEVELDB_PLATFORM_POSIX_SSE) - -// Used to fetch a naturally-aligned 32-bit word in little endian byte-order -static inline uint32_t LE_LOAD32(const uint8_t *p) { - // SSE is x86 only, so ensured that |p| is always little-endian. - uint32_t word; - memcpy(&word, p, sizeof(word)); - return word; -} - -#if defined(_M_X64) || defined(__x86_64__) // LE_LOAD64 is only used on x64. - -// Used to fetch a naturally-aligned 64-bit word in little endian byte-order -static inline uint64_t LE_LOAD64(const uint8_t *p) { - uint64_t dword; - memcpy(&dword, p, sizeof(dword)); - return dword; -} - -#endif // defined(_M_X64) || defined(__x86_64__) - -static inline bool HaveSSE42() { -#if defined(_MSC_VER) - int cpu_info[4]; - __cpuid(cpu_info, 1); - return (cpu_info[2] & (1 << 20)) != 0; -#elif defined(__GNUC__) - unsigned int eax, ebx, ecx, edx; - __get_cpuid(1, &eax, &ebx, &ecx, &edx); - return (ecx & (1 << 20)) != 0; -#else - return false; -#endif -} - -#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) - -// For further improvements see Intel publication at: -// http://download.intel.com/design/intarch/papers/323405.pdf -uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { -#if !defined(LEVELDB_PLATFORM_POSIX_SSE) - return 0; -#else - static bool have = HaveSSE42(); - if (!have) { - return 0; - } - - const uint8_t *p = reinterpret_cast(buf); - const uint8_t *e = p + size; - uint32_t l = crc ^ 0xffffffffu; - -#define STEP1 do { \ - l = _mm_crc32_u8(l, *p++); \ -} while (0) -#define STEP4 do { \ - l = _mm_crc32_u32(l, LE_LOAD32(p)); \ - p += 4; \ -} while (0) -#define STEP8 do { \ - l = _mm_crc32_u64(l, LE_LOAD64(p)); \ - p += 8; \ -} while (0) - - if (size > 16) { - // Point x at first 8-byte aligned byte in string. This must be inside the - // string, due to the size check above. - const uintptr_t pval = reinterpret_cast(p); - const uint8_t* x = reinterpret_cast(((pval + 7) >> 3) << 3); - // Process bytes until p is 8-byte aligned. - while (p != x) { - STEP1; - } - - // _mm_crc32_u64 is only available on x64. -#if defined(_M_X64) || defined(__x86_64__) - // Process 8 bytes at a time - while ((e-p) >= 8) { - STEP8; - } - // Process 4 bytes at a time - if ((e-p) >= 4) { - STEP4; - } -#else // !(defined(_M_X64) || defined(__x86_64__)) - // Process 4 bytes at a time - while ((e-p) >= 4) { - STEP4; - } -#endif // defined(_M_X64) || defined(__x86_64__) - } - // Process the last few bytes - while (p != e) { - STEP1; - } -#undef STEP8 -#undef STEP4 -#undef STEP1 - return l ^ 0xffffffffu; -#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) -} - -} // namespace port -} // namespace leveldb From 23162ca1c6d891a9c5fe0e0fab1193cd54ed1b4f Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 10 Oct 2017 11:29:00 -0700 Subject: [PATCH 064/174] Fix typo (forgotten reference operator) in test. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=171708408 --- db/fault_injection_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index 875dfe81ee..f8b24405a1 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -34,7 +34,7 @@ class FaultInjectionTestEnv; namespace { // Assume a filename, and not a directory name like "/foo/bar/" -static std::string GetDirName(const std::string filename) { +static std::string GetDirName(const std::string& filename) { size_t found = filename.find_last_of("/\\"); if (found == std::string::npos) { return ""; From 0509414f858ae7c7225e29f3659a709afb324355 Mon Sep 17 00:00:00 2001 From: cmumford Date: Tue, 17 Oct 2017 13:05:47 -0700 Subject: [PATCH 065/174] leveldb::DestroyDB will now delete empty directories. Env's that filtered out dot files ("." and "..") would return an empty vector of children causing DestroyDB to do nothing. This fixes https://github.com/google/leveldb/issues/215 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172501335 --- db/db_impl.cc | 8 ++--- db/db_test.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++ include/leveldb/db.h | 3 ++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index f43ad76794..a9044c261f 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -1537,15 +1537,15 @@ Snapshot::~Snapshot() { Status DestroyDB(const std::string& dbname, const Options& options) { Env* env = options.env; std::vector filenames; - // Ignore error in case directory does not exist - env->GetChildren(dbname, &filenames); - if (filenames.empty()) { + Status result = env->GetChildren(dbname, &filenames); + if (!result.ok()) { + // Ignore error in case directory does not exist return Status::OK(); } FileLock* lock; const std::string lockname = LockFileName(dbname); - Status result = env->LockFile(lockname, &lock); + result = env->LockFile(lockname, &lock); if (result.ok()) { uint64_t number; FileType type; diff --git a/db/db_test.cc b/db/db_test.cc index edc3916e89..c818113313 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -61,6 +61,36 @@ void DelayMilliseconds(int millis) { } } +// Test Env to override default Env behavior for testing. +class TestEnv : public EnvWrapper { + public: + explicit TestEnv(Env* base) : EnvWrapper(base), ignore_dot_files_(false) {} + + void SetIgnoreDotFiles(bool ignored) { ignore_dot_files_ = ignored; } + + Status GetChildren(const std::string& dir, + std::vector* result) override { + Status s = target()->GetChildren(dir, result); + if (!s.ok() || !ignore_dot_files_) { + return s; + } + + std::vector::iterator it = result->begin(); + while (it != result->end()) { + if ((*it == ".") || (*it == "..")) { + it = result->erase(it); + } else { + ++it; + } + } + + return s; + } + + private: + bool ignore_dot_files_; +}; + // Special Env used to delay background operations class SpecialEnv : public EnvWrapper { public: @@ -1561,6 +1591,58 @@ TEST(DBTest, DBOpen_Options) { db = NULL; } +TEST(DBTest, DestroyEmptyDir) { + std::string dbname = test::TmpDir() + "/db_empty_dir"; + TestEnv env(Env::Default()); + env.DeleteDir(dbname); + ASSERT_TRUE(!env.FileExists(dbname)); + + Options opts; + opts.env = &env; + + ASSERT_OK(env.CreateDir(dbname)); + ASSERT_TRUE(env.FileExists(dbname)); + std::vector children; + ASSERT_OK(env.GetChildren(dbname, &children)); + // The POSIX env does not filter out '.' and '..' special files. + ASSERT_EQ(2, children.size()); + ASSERT_OK(DestroyDB(dbname, opts)); + ASSERT_TRUE(!env.FileExists(dbname)); + + // Should also be destroyed if Env is filtering out dot files. + env.SetIgnoreDotFiles(true); + ASSERT_OK(env.CreateDir(dbname)); + ASSERT_TRUE(env.FileExists(dbname)); + ASSERT_OK(env.GetChildren(dbname, &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(DestroyDB(dbname, opts)); + ASSERT_TRUE(!env.FileExists(dbname)); +} + +TEST(DBTest, DestroyOpenDB) { + std::string dbname = test::TmpDir() + "/open_db_dir"; + env_->DeleteDir(dbname); + ASSERT_TRUE(!env_->FileExists(dbname)); + + Options opts; + opts.create_if_missing = true; + DB* db = NULL; + ASSERT_OK(DB::Open(opts, dbname, &db)); + ASSERT_TRUE(db != NULL); + + // Must fail to destroy an open db. + ASSERT_TRUE(env_->FileExists(dbname)); + ASSERT_TRUE(!DestroyDB(dbname, Options()).ok()); + ASSERT_TRUE(env_->FileExists(dbname)); + + delete db; + db = NULL; + + // Should succeed destroying a closed db. + ASSERT_OK(DestroyDB(dbname, Options())); + ASSERT_TRUE(!env_->FileExists(dbname)); +} + TEST(DBTest, Locking) { DB* db2 = NULL; Status s = DB::Open(CurrentOptions(), dbname_, &db2); diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 9a18c92f1a..e99f45c1e7 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -151,6 +151,9 @@ class LEVELDB_EXPORT DB { // Destroy the contents of the specified database. // Be very careful using this method. +// +// Note: For backwards compatibility, if DestroyDB is unable to list the +// database files, Status::OK() will still be returned masking this failure. LEVELDB_EXPORT Status DestroyDB(const std::string& name, const Options& options); From 3da4d8b9899257386aeb5ffa345a6477c62ff7bf Mon Sep 17 00:00:00 2001 From: cmumford Date: Thu, 19 Oct 2017 10:33:42 -0700 Subject: [PATCH 066/174] Deleted unused assignments in Reader. Deleted two unused assignments: 1. offset_in_block in Reader::SkipToInitialBlock(). 2. in_fragmented_record in Reader::ReadRecord(). Reasons for the change: 1. offset_in_block is not read again after the if condition. 2. The kFullRecordType switch branch returns, so in_fragmented_record isn't read again. 3. The kFirstType switch branch sets in_fragmented_record to true after the if, so the write in the if is ignored. Change contributed by @C0deAi on GitHub. This fixes https://github.com/google/leveldb/issues/517 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172763897 --- db/log_reader.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/db/log_reader.cc b/db/log_reader.cc index a6d304545d..48ae863e5d 100644 --- a/db/log_reader.cc +++ b/db/log_reader.cc @@ -34,12 +34,11 @@ Reader::~Reader() { } bool Reader::SkipToInitialBlock() { - size_t offset_in_block = initial_offset_ % kBlockSize; + const size_t offset_in_block = initial_offset_ % kBlockSize; uint64_t block_start_location = initial_offset_ - offset_in_block; // Don't search a block if we'd be in the trailer if (offset_in_block > kBlockSize - 6) { - offset_in_block = 0; block_start_location += kBlockSize; } @@ -99,9 +98,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { // it could emit an empty kFirstType record at the tail end // of a block followed by a kFullType or kFirstType record // at the beginning of the next block. - if (scratch->empty()) { - in_fragmented_record = false; - } else { + if (!scratch->empty()) { ReportCorruption(scratch->size(), "partial record without end(1)"); } } @@ -117,9 +114,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { // it could emit an empty kFirstType record at the tail end // of a block followed by a kFullType or kFirstType record // at the beginning of the next block. - if (scratch->empty()) { - in_fragmented_record = false; - } else { + if (!scratch->empty()) { ReportCorruption(scratch->size(), "partial record without end(2)"); } } From b5d4a22e64c7a6615b412f464026c808b58b1d34 Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 23 Oct 2017 16:28:45 -0700 Subject: [PATCH 067/174] Fixed style guide link in CONTRIBUTING.md The C++ style guide URL was wrong. This fixes issue #394. Reported by GitHub user @Loki-Astari. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=173188573 --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd600ff46b..a74572a596 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,6 @@ the CLA. ## Writing Code ## -If your contribution contains code, please make sure that it follows -[the style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). +If your contribution contains code, please make sure that it follows +[the style guide](http://google.github.io/styleguide/cppguide.html). Otherwise we will have to ask you to make changes, and that's no fun for anyone. From 47cb9e2a211e1d7157078ba7bab536beb29e56dc Mon Sep 17 00:00:00 2001 From: cmumford Date: Wed, 25 Oct 2017 16:13:51 -0700 Subject: [PATCH 068/174] Add leveldb_options_set_max_file_size to the C API. When the max file size option was added in CL 134391640 the C API was not modified to support this. This change was contributed by GitHub user @olt and fixes issue #439. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=173466388 --- db/c.cc | 4 ++++ db/c_test.c | 1 + include/leveldb/c.h | 2 ++ 3 files changed, 7 insertions(+) diff --git a/db/c.cc b/db/c.cc index 08ff0ad90a..d2423859e4 100644 --- a/db/c.cc +++ b/db/c.cc @@ -446,6 +446,10 @@ void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { opt->rep.block_restart_interval = n; } +void leveldb_options_set_max_file_size(leveldb_options_t* opt, size_t s) { + opt->rep.max_file_size = s; +} + void leveldb_options_set_compression(leveldb_options_t* opt, int t) { opt->rep.compression = static_cast(t); } diff --git a/db/c_test.c b/db/c_test.c index 7cd5ee0207..4a141513bc 100644 --- a/db/c_test.c +++ b/db/c_test.c @@ -189,6 +189,7 @@ int main(int argc, char** argv) { leveldb_options_set_max_open_files(options, 10); leveldb_options_set_block_size(options, 1024); leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_max_file_size(options, 3 << 20); leveldb_options_set_compression(options, leveldb_no_compression); roptions = leveldb_readoptions_create(); diff --git a/include/leveldb/c.h b/include/leveldb/c.h index 7ac8121d54..dc6f255338 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -184,6 +184,8 @@ LEVELDB_EXPORT void leveldb_options_set_cache(leveldb_options_t*, LEVELDB_EXPORT void leveldb_options_set_block_size(leveldb_options_t*, size_t); LEVELDB_EXPORT void leveldb_options_set_block_restart_interval( leveldb_options_t*, int); +LEVELDB_EXPORT void leveldb_options_set_max_file_size(leveldb_options_t*, + size_t); enum { leveldb_no_compression = 0, From 41172a24016bc29fc795ed504737392587f54e3d Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 13 Feb 2018 22:31:50 -0800 Subject: [PATCH 069/174] Enable thread safety annotations in open source version. The thread safety annotations used by leveldb got opensourced in Abseil [1]. This CL replaces leveldb's stubs with the relevant definitions from [1], and adds annotations to the Mutex classes in the POSIX port. [1] https://github.com/abseil/abseil-cpp/blob/master/absl/base/thread_annotations.h --- build_detect_platform | 19 ++++++++- port/port_example.h | 10 +++-- port/port_posix.h | 9 +++-- port/thread_annotations.h | 81 +++++++++++++++++++++++++++++++-------- 4 files changed, 94 insertions(+), 25 deletions(-) diff --git a/build_detect_platform b/build_detect_platform index b7a859f830..07684299eb 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -202,7 +202,6 @@ EOF PLATFORM_LIBS="$PLATFORM_LIBS -lcrc32c" fi - # Test whether Snappy library is installed # https://github.com/google/snappy $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null </dev/null + # Test whether -Wthread-safety is available. See + # https://clang.llvm.org/docs/ThreadSafetyAnalysis.html + # -Werror is necessary because unknown attributes only generate warnings. + $CXX $CXXFLAGS -Wthread-safety -Werror -x c++ - -o $CXXOUTPUT 2>/dev/null </dev/null fi diff --git a/port/port_example.h b/port/port_example.h index 97bd669a5e..b5190e6132 100644 --- a/port/port_example.h +++ b/port/port_example.h @@ -10,6 +10,8 @@ #ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ #define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#include "port/thread_annotations.h" + namespace leveldb { namespace port { @@ -23,23 +25,23 @@ static const bool kLittleEndian = true /* or some other expression */; // ------------------ Threading ------------------- // A Mutex represents an exclusive lock. -class Mutex { +class LOCKABLE Mutex { public: Mutex(); ~Mutex(); // Lock the mutex. Waits until other lockers have exited. // Will deadlock if the mutex is already locked by this thread. - void Lock(); + void Lock() EXCLUSIVE_LOCK_FUNCTION(); // Unlock the mutex. // REQUIRES: This mutex was locked by this thread. - void Unlock(); + void Unlock() UNLOCK_FUNCTION(); // Optionally crash if this thread does not hold this mutex. // The implementation must be fast, especially if NDEBUG is // defined. The implementation is allowed to skip all checks. - void AssertHeld(); + void AssertHeld() ASSERT_EXCLUSIVE_LOCK(); }; class CondVar { diff --git a/port/port_posix.h b/port/port_posix.h index 2a30be3a1d..d30f0f247c 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -48,6 +48,7 @@ #include #include #include "port/atomic_pointer.h" +#include "port/thread_annotations.h" #ifndef PLATFORM_IS_LITTLE_ENDIAN #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) @@ -73,14 +74,14 @@ static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; class CondVar; -class Mutex { +class LOCKABLE Mutex { public: Mutex(); ~Mutex(); - void Lock(); - void Unlock(); - void AssertHeld() { } + void Lock() EXCLUSIVE_LOCK_FUNCTION(); + void Unlock() UNLOCK_FUNCTION(); + void AssertHeld() ASSERT_EXCLUSIVE_LOCK() { } private: friend class CondVar; diff --git a/port/thread_annotations.h b/port/thread_annotations.h index 9470ef587c..b737c69014 100644 --- a/port/thread_annotations.h +++ b/port/thread_annotations.h @@ -5,56 +5,107 @@ #ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ #define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ -// Some environments provide custom macros to aid in static thread-safety -// analysis. Provide empty definitions of such macros unless they are already -// defined. +// Use Clang's thread safety analysis annotations when available. In other +// environments, the macros receive empty definitions. +// Usage documentation: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html + +#if !defined(THREAD_ANNOTATION_ATTRIBUTE__) + +#if defined(__clang__) + +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#endif // !defined(THREAD_ANNOTATION_ATTRIBUTE__) + +#ifndef GUARDED_BY +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#endif + +#ifndef PT_GUARDED_BY +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +#endif + +#ifndef ACQUIRED_AFTER +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +#endif + +#ifndef ACQUIRED_BEFORE +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#endif #ifndef EXCLUSIVE_LOCKS_REQUIRED -#define EXCLUSIVE_LOCKS_REQUIRED(...) +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) #endif #ifndef SHARED_LOCKS_REQUIRED -#define SHARED_LOCKS_REQUIRED(...) +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) #endif #ifndef LOCKS_EXCLUDED -#define LOCKS_EXCLUDED(...) +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) #endif #ifndef LOCK_RETURNED -#define LOCK_RETURNED(x) +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) #endif #ifndef LOCKABLE -#define LOCKABLE +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) #endif #ifndef SCOPED_LOCKABLE -#define SCOPED_LOCKABLE +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) #endif #ifndef EXCLUSIVE_LOCK_FUNCTION -#define EXCLUSIVE_LOCK_FUNCTION(...) +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) #endif #ifndef SHARED_LOCK_FUNCTION -#define SHARED_LOCK_FUNCTION(...) +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) #endif #ifndef EXCLUSIVE_TRYLOCK_FUNCTION -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) #endif #ifndef SHARED_TRYLOCK_FUNCTION -#define SHARED_TRYLOCK_FUNCTION(...) +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) #endif #ifndef UNLOCK_FUNCTION -#define UNLOCK_FUNCTION(...) +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) #endif #ifndef NO_THREAD_SAFETY_ANALYSIS -#define NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) +#endif + +#ifndef ASSERT_EXCLUSIVE_LOCK +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) +#endif + +#ifndef ASSERT_SHARED_LOCK +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) #endif #endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ From 8c8024ea33d8efc8c415597fb7fa1745002961d6 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 9 Mar 2018 08:36:08 -0800 Subject: [PATCH 070/174] Switch HAVE_ library detection macros to 0/1. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188488298 --- build_detect_platform | 4 ++++ port/port_posix.h | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/build_detect_platform b/build_detect_platform index 07684299eb..82d73a6359 100755 --- a/build_detect_platform +++ b/build_detect_platform @@ -200,6 +200,8 @@ EOF if [ "$?" = 0 ]; then COMMON_FLAGS="$COMMON_FLAGS -DHAVE_CRC32C=1" PLATFORM_LIBS="$PLATFORM_LIBS -lcrc32c" + else + COMMON_FLAGS="$COMMON_FLAGS -DHAVE_CRC32C=0" fi # Test whether Snappy library is installed @@ -211,6 +213,8 @@ EOF if [ "$?" = 0 ]; then COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SNAPPY=1" PLATFORM_LIBS="$PLATFORM_LIBS -lsnappy" + else + COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SNAPPY=0" fi # Test whether tcmalloc is available diff --git a/port/port_posix.h b/port/port_posix.h index d30f0f247c..5bfc12e6f6 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -39,12 +39,12 @@ #endif #include -#if defined(HAVE_CRC32C) +#if HAVE_CRC32C #include -#endif // defined(HAVE_CRC32C) -#ifdef HAVE_SNAPPY +#endif // HAVE_CRC32C +#if HAVE_SNAPPY #include -#endif // defined(HAVE_SNAPPY) +#endif // HAVE_SNAPPY #include #include #include "port/atomic_pointer.h" @@ -110,33 +110,33 @@ extern void InitOnce(OnceType* once, void (*initializer)()); inline bool Snappy_Compress(const char* input, size_t length, ::std::string* output) { -#ifdef HAVE_SNAPPY +#if HAVE_SNAPPY output->resize(snappy::MaxCompressedLength(length)); size_t outlen; snappy::RawCompress(input, length, &(*output)[0], &outlen); output->resize(outlen); return true; -#endif // defined(HAVE_SNAPPY) +#endif // HAVE_SNAPPY return false; } inline bool Snappy_GetUncompressedLength(const char* input, size_t length, size_t* result) { -#ifdef HAVE_SNAPPY +#if HAVE_SNAPPY return snappy::GetUncompressedLength(input, length, result); #else return false; -#endif // defined(HAVE_SNAPPY) +#endif // HAVE_SNAPPY } inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { -#ifdef HAVE_SNAPPY +#if HAVE_SNAPPY return snappy::RawUncompress(input, length, output); #else return false; -#endif // defined(HAVE_SNAPPY) +#endif // HAVE_SNAPPY } inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { @@ -144,11 +144,11 @@ inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { } inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { -#if defined(HAVE_CRC32C) +#if HAVE_CRC32C return ::crc32c::Extend(crc, reinterpret_cast(buf), size); #else return 0; -#endif // defined(HAVE_CRC32C) +#endif // HAVE_CRC32C } } // namespace port From 623d014a54f8cf9b74ad6aaba9181ca1e65c43a1 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 9 Mar 2018 10:32:55 -0800 Subject: [PATCH 071/174] Expose Env::GetTempDirectory() for use in C test. This removes the use of the non-portable headers and in c_test.c. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188503102 --- db/c.cc | 13 ++++++++++++- db/c_test.c | 19 ++++--------------- include/leveldb/c.h | 3 +++ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/db/c.cc b/db/c.cc index d2423859e4..0ccf08c4f3 100644 --- a/db/c.cc +++ b/db/c.cc @@ -5,7 +5,6 @@ #include "leveldb/c.h" #include -#include #include "leveldb/cache.h" #include "leveldb/comparator.h" #include "leveldb/db.h" @@ -584,6 +583,18 @@ void leveldb_env_destroy(leveldb_env_t* env) { delete env; } +char* leveldb_env_get_test_directory(leveldb_env_t* env) { + std::string result; + if (!env->rep->GetTestDirectory(&result).ok()) { + return NULL; + } + + char* buffer = static_cast(malloc(result.size() + 1)); + memcpy(buffer, result.data(), result.size()); + buffer[result.size()] = '\0'; + return buffer; +} + void leveldb_free(void* ptr) { free(ptr); } diff --git a/db/c_test.c b/db/c_test.c index 4a141513bc..7284de5edf 100644 --- a/db/c_test.c +++ b/db/c_test.c @@ -8,24 +8,14 @@ #include #include #include -#include -#include const char* phase = ""; -static char dbname[200]; static void StartPhase(const char* name) { fprintf(stderr, "=== Test %s\n", name); phase = name; } -static const char* GetTempDir(void) { - const char* ret = getenv("TEST_TMPDIR"); - if (ret == NULL || ret[0] == '\0') - ret = "/tmp"; - return ret; -} - #define CheckNoError(err) \ if ((err) != NULL) { \ fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ @@ -162,21 +152,19 @@ int main(int argc, char** argv) { leveldb_options_t* options; leveldb_readoptions_t* roptions; leveldb_writeoptions_t* woptions; + char* dbname; char* err = NULL; int run = -1; CheckCondition(leveldb_major_version() >= 1); CheckCondition(leveldb_minor_version() >= 1); - snprintf(dbname, sizeof(dbname), - "%s/leveldb_c_test-%d", - GetTempDir(), - ((int) geteuid())); - StartPhase("create_objects"); cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); env = leveldb_create_default_env(); cache = leveldb_cache_create_lru(100000); + dbname = leveldb_env_get_test_directory(env); + CheckCondition(dbname != NULL); options = leveldb_options_create(); leveldb_options_set_comparator(options, cmp); @@ -382,6 +370,7 @@ int main(int argc, char** argv) { leveldb_options_destroy(options); leveldb_readoptions_destroy(roptions); leveldb_writeoptions_destroy(woptions); + leveldb_free(dbname); leveldb_cache_destroy(cache); leveldb_comparator_destroy(cmp); leveldb_env_destroy(env); diff --git a/include/leveldb/c.h b/include/leveldb/c.h index dc6f255338..1124153b73 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -245,6 +245,9 @@ LEVELDB_EXPORT void leveldb_cache_destroy(leveldb_cache_t* cache); LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(); LEVELDB_EXPORT void leveldb_env_destroy(leveldb_env_t*); +/* If not NULL, the returned buffer must be released using leveldb_free(). */ +LEVELDB_EXPORT char* leveldb_env_get_test_directory(leveldb_env_t*); + /* Utility */ /* Calls free(ptr). From 49f35d3fc940a1e2d599d6ee3306eeb31a205e4b Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 9 Mar 2018 15:29:12 -0800 Subject: [PATCH 072/174] leveldb: Update Travis CI configuration for open source build. This aligns the Travis CI configuration with google/crc32c and google/snappy, to simplify maintenance. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188547648 --- .travis.yml | 57 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5bd74c454..77ea0a3189 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,54 @@ +# Build matrix / environment variable are explained on: +# http://about.travis-ci.org/docs/user/build-configuration/ +# This file can be validated on: http://lint.travis-ci.org/ + +sudo: false +dist: trusty language: cpp + compiler: -- clang -- gcc + - gcc + - clang os: -- linux -- osx -sudo: false -before_install: -- echo $LANG -- echo $LC_ALL + - linux + - osx + +addons: + apt: + # List of whitelisted in travis packages for ubuntu-trusty can be found here: + # https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-trusty + # List of whitelisted in travis apt-sources: + # https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - gcc-7 + - g++-7 + - clang-5.0 + - libgoogle-perftools-dev + - libkyotocabinet-dev + - libsnappy-dev + - libsqlite3-dev + +install: +# Travis doesn't have a DSL for installing homebrew packages yet. Status tracked +# in https://github.com/travis-ci/travis-ci/issues/5377 +# The Travis VM image for Mac already has a link at /usr/local/include/c++, +# causing Homebrew's gcc@7 installation to error out. This was reported to +# Homebrew maintainers at https://github.com/Homebrew/brew/issues/1742 and +# removing the link emerged as a workaround. +- if [ "$TRAVIS_OS_NAME" == "osx" ]; then + brew update; + if [ -L /usr/local/include/c++ ]; then rm /usr/local/include/c++; fi; + brew install gcc@7; + brew install crc32c gperftools kyoto-cabinet snappy sqlite3; + fi +# /usr/bin/gcc is stuck to old versions on both Linux and OSX. +- if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi +- echo ${CC} +- echo ${CXX} +- ${CXX} --version + script: - make -j 4 check From 594cc987af2e0af6417c4ac2b947ee8cdad59e5e Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 9 Mar 2018 16:10:08 -0800 Subject: [PATCH 073/174] Bypass OSMemoryBarrier() warning on Mac. This is a stopgap for removing warnings on Mac builds, so -Werror can be turned on. C++11 will be required in the nearby future, which guarantees support. Once that happens, the simplified version of this will match https://github.com/google/leveldb/pull/503 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188553251 --- port/atomic_pointer.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index 54f088585d..43dab2ab3d 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -21,13 +21,12 @@ #include #ifdef LEVELDB_ATOMIC_PRESENT #include +#elif defined(__APPLE__) +#include #endif #ifdef OS_WIN #include #endif -#ifdef __APPLE__ -#include -#endif #if defined(_M_X64) || defined(__x86_64__) #define ARCH_CPU_X86_FAMILY 1 @@ -56,7 +55,11 @@ namespace port { // Mac OS #elif defined(__APPLE__) inline void MemoryBarrier() { +#if defined(LEVELDB_ATOMIC_PRESENT) + std::atomic_thread_fence(std::memory_order_seq_cst); +#else OSMemoryBarrier(); +#endif // defined(LEVELDB_ATOMIC_PRESENT) } #define LEVELDB_HAVE_MEMORY_BARRIER From 7fd7c0072159abbca2660d91fc0667d5c17c4d16 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 12 Mar 2018 08:59:43 -0700 Subject: [PATCH 074/174] Remove unused function ExtractValueType. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188728505 --- db/dbformat.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/db/dbformat.h b/db/dbformat.h index ea897b13c0..2e93669090 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -100,14 +100,6 @@ inline Slice ExtractUserKey(const Slice& internal_key) { return Slice(internal_key.data(), internal_key.size() - 8); } -inline ValueType ExtractValueType(const Slice& internal_key) { - assert(internal_key.size() >= 8); - const size_t n = internal_key.size(); - uint64_t num = DecodeFixed64(internal_key.data() + n - 8); - unsigned char c = num & 0xff; - return static_cast(c); -} - // A comparator for internal keys that uses a specified comparator for // the user key portion and breaks ties by decreasing sequence number. class InternalKeyComparator : public Comparator { From ddab751002588fe58955357d68d12b062e038d0d Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 12 Mar 2018 09:02:38 -0700 Subject: [PATCH 075/174] Add tests for {Old}InfoLogFileName(). This change was contributed by GitHub user @LopatkinEvgeniy in https://github.com/google/leveldb/pull/559. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188728845 --- db/filename_test.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/db/filename_test.cc b/db/filename_test.cc index a32556deaf..0bde538e27 100644 --- a/db/filename_test.cc +++ b/db/filename_test.cc @@ -114,6 +114,18 @@ TEST(FileNameTest, Construction) { ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); ASSERT_EQ(999, number); ASSERT_EQ(kTempFile, type); + + fname = InfoLogFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kInfoLogFile, type); + + fname = OldInfoLogFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kInfoLogFile, type); } } // namespace leveldb From aece2068d7375f987685b8b145288c5557f9ce50 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 12 Mar 2018 09:14:44 -0700 Subject: [PATCH 076/174] Remove extern from function declarations. External linkage is the default for function declarations in C++. This also fixes ClangTidy errors generated by removing the "extern" keyword as described above. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188730416 --- db/builder.h | 12 ++++++------ db/db_impl.h | 8 ++++---- db/db_iter.h | 11 +++++------ db/dbformat.h | 6 ++---- db/filename.cc | 24 ++++++++++++------------ db/filename.h | 30 ++++++++++++++---------------- db/version_set.h | 17 ++++++++--------- include/leveldb/env.h | 2 +- port/port_example.h | 16 ++++++++-------- port/port_posix.h | 2 +- table/format.h | 8 ++++---- table/merger.h | 2 +- table/two_level_iterator.h | 2 +- util/coding.h | 36 ++++++++++++++++++------------------ util/crc32c.h | 2 +- util/hash.h | 4 ++-- util/logging.h | 10 +++++----- util/testharness.h | 9 ++++----- util/testutil.cc | 4 ++-- util/testutil.h | 8 ++++---- 20 files changed, 103 insertions(+), 110 deletions(-) diff --git a/db/builder.h b/db/builder.h index 62431fcf44..02897309a2 100644 --- a/db/builder.h +++ b/db/builder.h @@ -22,12 +22,12 @@ class VersionEdit; // *meta will be filled with metadata about the generated table. // If no data is present in *iter, meta->file_size will be set to // zero, and no Table file will be produced. -extern Status BuildTable(const std::string& dbname, - Env* env, - const Options& options, - TableCache* table_cache, - Iterator* iter, - FileMetaData* meta); +Status BuildTable(const std::string& dbname, + Env* env, + const Options& options, + TableCache* table_cache, + Iterator* iter, + FileMetaData* meta); } // namespace leveldb diff --git a/db/db_impl.h b/db/db_impl.h index 8ff323e728..3861b8663a 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -201,10 +201,10 @@ class DBImpl : public DB { // Sanitize db options. The caller should delete result.info_log if // it is not equal to src.info_log. -extern Options SanitizeOptions(const std::string& db, - const InternalKeyComparator* icmp, - const InternalFilterPolicy* ipolicy, - const Options& src); +Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); } // namespace leveldb diff --git a/db/db_iter.h b/db/db_iter.h index 04927e937b..262840edd3 100644 --- a/db/db_iter.h +++ b/db/db_iter.h @@ -16,12 +16,11 @@ class DBImpl; // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. -extern Iterator* NewDBIterator( - DBImpl* db, - const Comparator* user_key_comparator, - Iterator* internal_iter, - SequenceNumber sequence, - uint32_t seed); +Iterator* NewDBIterator(DBImpl* db, + const Comparator* user_key_comparator, + Iterator* internal_iter, + SequenceNumber sequence, + uint32_t seed); } // namespace leveldb diff --git a/db/dbformat.h b/db/dbformat.h index 2e93669090..c4d9575f2c 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -84,15 +84,13 @@ inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { } // Append the serialization of "key" to *result. -extern void AppendInternalKey(std::string* result, - const ParsedInternalKey& key); +void AppendInternalKey(std::string* result, const ParsedInternalKey& key); // Attempt to parse an internal key from "internal_key". On success, // stores the parsed data in "*result", and returns true. // // On error, returns false, leaves "*result" in an undefined state. -extern bool ParseInternalKey(const Slice& internal_key, - ParsedInternalKey* result); +bool ParseInternalKey(const Slice& internal_key, ParsedInternalKey* result); // Returns the user key portion of an internal key. inline Slice ExtractUserKey(const Slice& internal_key) { diff --git a/db/filename.cc b/db/filename.cc index da32946d99..6539bbe75a 100644 --- a/db/filename.cc +++ b/db/filename.cc @@ -12,31 +12,31 @@ namespace leveldb { // A utility routine: write "data" to the named file and Sync() it. -extern Status WriteStringToFileSync(Env* env, const Slice& data, - const std::string& fname); +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); -static std::string MakeFileName(const std::string& name, uint64_t number, +static std::string MakeFileName(const std::string& dbname, uint64_t number, const char* suffix) { char buf[100]; snprintf(buf, sizeof(buf), "/%06llu.%s", static_cast(number), suffix); - return name + buf; + return dbname + buf; } -std::string LogFileName(const std::string& name, uint64_t number) { +std::string LogFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "log"); + return MakeFileName(dbname, number, "log"); } -std::string TableFileName(const std::string& name, uint64_t number) { +std::string TableFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "ldb"); + return MakeFileName(dbname, number, "ldb"); } -std::string SSTTableFileName(const std::string& name, uint64_t number) { +std::string SSTTableFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "sst"); + return MakeFileName(dbname, number, "sst"); } std::string DescriptorFileName(const std::string& dbname, uint64_t number) { @@ -77,10 +77,10 @@ std::string OldInfoLogFileName(const std::string& dbname) { // dbname/LOG.old // dbname/MANIFEST-[0-9]+ // dbname/[0-9]+.(log|sst|ldb) -bool ParseFileName(const std::string& fname, +bool ParseFileName(const std::string& filename, uint64_t* number, FileType* type) { - Slice rest(fname); + Slice rest(filename); if (rest == "CURRENT") { *number = 0; *type = kCurrentFile; diff --git a/db/filename.h b/db/filename.h index 87a752605d..62cb3ef92d 100644 --- a/db/filename.h +++ b/db/filename.h @@ -30,55 +30,53 @@ enum FileType { // Return the name of the log file with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string LogFileName(const std::string& dbname, uint64_t number); +std::string LogFileName(const std::string& dbname, uint64_t number); // Return the name of the sstable with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string TableFileName(const std::string& dbname, uint64_t number); +std::string TableFileName(const std::string& dbname, uint64_t number); // Return the legacy file name for an sstable with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); +std::string SSTTableFileName(const std::string& dbname, uint64_t number); // Return the name of the descriptor file for the db named by // "dbname" and the specified incarnation number. The result will be // prefixed with "dbname". -extern std::string DescriptorFileName(const std::string& dbname, - uint64_t number); +std::string DescriptorFileName(const std::string& dbname, uint64_t number); // Return the name of the current file. This file contains the name // of the current manifest file. The result will be prefixed with // "dbname". -extern std::string CurrentFileName(const std::string& dbname); +std::string CurrentFileName(const std::string& dbname); // Return the name of the lock file for the db named by // "dbname". The result will be prefixed with "dbname". -extern std::string LockFileName(const std::string& dbname); +std::string LockFileName(const std::string& dbname); // Return the name of a temporary file owned by the db named "dbname". // The result will be prefixed with "dbname". -extern std::string TempFileName(const std::string& dbname, uint64_t number); +std::string TempFileName(const std::string& dbname, uint64_t number); // Return the name of the info log file for "dbname". -extern std::string InfoLogFileName(const std::string& dbname); +std::string InfoLogFileName(const std::string& dbname); // Return the name of the old info log file for "dbname". -extern std::string OldInfoLogFileName(const std::string& dbname); +std::string OldInfoLogFileName(const std::string& dbname); // If filename is a leveldb file, store the type of the file in *type. // The number encoded in the filename is stored in *number. If the // filename was successfully parsed, returns true. Else return false. -extern bool ParseFileName(const std::string& filename, - uint64_t* number, - FileType* type); +bool ParseFileName(const std::string& filename, + uint64_t* number, + FileType* type); // Make the CURRENT file point to the descriptor file with the // specified number. -extern Status SetCurrentFile(Env* env, const std::string& dbname, - uint64_t descriptor_number); - +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); } // namespace leveldb diff --git a/db/version_set.h b/db/version_set.h index c4e7ac360b..80d448a7ed 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -39,9 +39,9 @@ class WritableFile; // Return the smallest index i such that files[i]->largest >= key. // Return files.size() if there is no such file. // REQUIRES: "files" contains a sorted list of non-overlapping files. -extern int FindFile(const InternalKeyComparator& icmp, - const std::vector& files, - const Slice& key); +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, + const Slice& key); // Returns true iff some file in "files" overlaps the user key range // [*smallest,*largest]. @@ -49,12 +49,11 @@ extern int FindFile(const InternalKeyComparator& icmp, // largest==NULL represents a key largest than all keys in the DB. // REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges // in sorted order. -extern bool SomeFileOverlapsRange( - const InternalKeyComparator& icmp, - bool disjoint_sorted_files, - const std::vector& files, - const Slice* smallest_user_key, - const Slice* largest_user_key); +bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); class Version { public: diff --git a/include/leveldb/env.h b/include/leveldb/env.h index d698169e65..9d8ebfe038 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -271,7 +271,7 @@ class LEVELDB_EXPORT FileLock { }; // Log the specified data to *info_log if info_log is non-NULL. -extern void Log(Logger* info_log, const char* format, ...) +void Log(Logger* info_log, const char* format, ...) # if defined(__GNUC__) || defined(__clang__) __attribute__((__format__ (__printf__, 2, 3))) # endif diff --git a/port/port_example.h b/port/port_example.h index b5190e6132..88fc9cbc7b 100644 --- a/port/port_example.h +++ b/port/port_example.h @@ -70,7 +70,7 @@ class CondVar { // port::InitOnce(&init_control, &Initializer); typedef intptr_t OnceType; #define LEVELDB_ONCE_INIT 0 -extern void InitOnce(port::OnceType*, void (*initializer)()); +void InitOnce(port::OnceType*, void (*initializer)()); // A type that holds a pointer that can be read or written atomically // (i.e., without word-tearing.) @@ -105,14 +105,14 @@ class AtomicPointer { // Store the snappy compression of "input[0,input_length-1]" in *output. // Returns false if snappy is not supported by this port. -extern bool Snappy_Compress(const char* input, size_t input_length, - std::string* output); +bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); // If input[0,input_length-1] looks like a valid snappy compressed // buffer, store the size of the uncompressed data in *result and // return true. Else return false. -extern bool Snappy_GetUncompressedLength(const char* input, size_t length, - size_t* result); +bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); // Attempt to snappy uncompress input[0,input_length-1] into *output. // Returns true if successful, false if the input is invalid lightweight @@ -121,15 +121,15 @@ extern bool Snappy_GetUncompressedLength(const char* input, size_t length, // REQUIRES: at least the first "n" bytes of output[] must be writable // where "n" is the result of a successful call to // Snappy_GetUncompressedLength. -extern bool Snappy_Uncompress(const char* input_data, size_t input_length, - char* output); +bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); // ------------------ Miscellaneous ------------------- // If heap profiling is not supported, returns false. // Else repeatedly calls (*func)(arg, data, n) and then returns true. // The concatenation of all "data[0,n-1]" fragments is the heap profile. -extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); +bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); // Extend the CRC to include the first n bytes of buf. // diff --git a/port/port_posix.h b/port/port_posix.h index 5bfc12e6f6..ce52042b59 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -106,7 +106,7 @@ class CondVar { typedef pthread_once_t OnceType; #define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT -extern void InitOnce(OnceType* once, void (*initializer)()); +void InitOnce(OnceType* once, void (*initializer)()); inline bool Snappy_Compress(const char* input, size_t length, ::std::string* output) { diff --git a/table/format.h b/table/format.h index 6c0b80c017..144ff5528f 100644 --- a/table/format.h +++ b/table/format.h @@ -91,10 +91,10 @@ struct BlockContents { // Read the block identified by "handle" from "file". On failure // return non-OK. On success fill *result and return OK. -extern Status ReadBlock(RandomAccessFile* file, - const ReadOptions& options, - const BlockHandle& handle, - BlockContents* result); +Status ReadBlock(RandomAccessFile* file, + const ReadOptions& options, + const BlockHandle& handle, + BlockContents* result); // Implementation details follow. Clients should ignore, diff --git a/table/merger.h b/table/merger.h index 91ddd80faa..bafdf5aef7 100644 --- a/table/merger.h +++ b/table/merger.h @@ -18,7 +18,7 @@ class Iterator; // key is present in K child iterators, it will be yielded K times. // // REQUIRES: n >= 0 -extern Iterator* NewMergingIterator( +Iterator* NewMergingIterator( const Comparator* comparator, Iterator** children, int n); } // namespace leveldb diff --git a/table/two_level_iterator.h b/table/two_level_iterator.h index 629ca34525..a93ba899a0 100644 --- a/table/two_level_iterator.h +++ b/table/two_level_iterator.h @@ -20,7 +20,7 @@ struct ReadOptions; // // Uses a supplied function to convert an index_iter value into // an iterator over the contents of the corresponding block. -extern Iterator* NewTwoLevelIterator( +Iterator* NewTwoLevelIterator( Iterator* index_iter, Iterator* (*block_function)( void* arg, diff --git a/util/coding.h b/util/coding.h index 3993c4a755..5c4d40e2e3 100644 --- a/util/coding.h +++ b/util/coding.h @@ -19,38 +19,38 @@ namespace leveldb { // Standard Put... routines append to a string -extern void PutFixed32(std::string* dst, uint32_t value); -extern void PutFixed64(std::string* dst, uint64_t value); -extern void PutVarint32(std::string* dst, uint32_t value); -extern void PutVarint64(std::string* dst, uint64_t value); -extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); +void PutFixed32(std::string* dst, uint32_t value); +void PutFixed64(std::string* dst, uint64_t value); +void PutVarint32(std::string* dst, uint32_t value); +void PutVarint64(std::string* dst, uint64_t value); +void PutLengthPrefixedSlice(std::string* dst, const Slice& value); // Standard Get... routines parse a value from the beginning of a Slice // and advance the slice past the parsed value. -extern bool GetVarint32(Slice* input, uint32_t* value); -extern bool GetVarint64(Slice* input, uint64_t* value); -extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); +bool GetVarint32(Slice* input, uint32_t* value); +bool GetVarint64(Slice* input, uint64_t* value); +bool GetLengthPrefixedSlice(Slice* input, Slice* result); // Pointer-based variants of GetVarint... These either store a value // in *v and return a pointer just past the parsed value, or return // NULL on error. These routines only look at bytes in the range // [p..limit-1] -extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); -extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); +const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); +const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); // Returns the length of the varint32 or varint64 encoding of "v" -extern int VarintLength(uint64_t v); +int VarintLength(uint64_t v); // Lower-level versions of Put... that write directly into a character buffer // REQUIRES: dst has enough space for the value being written -extern void EncodeFixed32(char* dst, uint32_t value); -extern void EncodeFixed64(char* dst, uint64_t value); +void EncodeFixed32(char* dst, uint32_t value); +void EncodeFixed64(char* dst, uint64_t value); // Lower-level versions of Put... that write directly into a character buffer // and return a pointer just past the last byte written. // REQUIRES: dst has enough space for the value being written -extern char* EncodeVarint32(char* dst, uint32_t value); -extern char* EncodeVarint64(char* dst, uint64_t value); +char* EncodeVarint32(char* dst, uint32_t value); +char* EncodeVarint64(char* dst, uint64_t value); // Lower-level versions of Get... that read directly from a character buffer // without any bounds checking. @@ -83,9 +83,9 @@ inline uint64_t DecodeFixed64(const char* ptr) { } // Internal routine for use by fallback path of GetVarint32Ptr -extern const char* GetVarint32PtrFallback(const char* p, - const char* limit, - uint32_t* value); +const char* GetVarint32PtrFallback(const char* p, + const char* limit, + uint32_t* value); inline const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* value) { diff --git a/util/crc32c.h b/util/crc32c.h index 1d7e5c075d..7864688b1c 100644 --- a/util/crc32c.h +++ b/util/crc32c.h @@ -14,7 +14,7 @@ namespace crc32c { // Return the crc32c of concat(A, data[0,n-1]) where init_crc is the // crc32c of some string A. Extend() is often used to maintain the // crc32c of a stream of data. -extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); +uint32_t Extend(uint32_t init_crc, const char* data, size_t n); // Return the crc32c of data[0,n-1] inline uint32_t Value(const char* data, size_t n) { diff --git a/util/hash.h b/util/hash.h index 8889d56be8..74bdb6e7b2 100644 --- a/util/hash.h +++ b/util/hash.h @@ -12,8 +12,8 @@ namespace leveldb { -extern uint32_t Hash(const char* data, size_t n, uint32_t seed); +uint32_t Hash(const char* data, size_t n, uint32_t seed); -} +} // namespace leveldb #endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/util/logging.h b/util/logging.h index 1b450d2480..13351a2d03 100644 --- a/util/logging.h +++ b/util/logging.h @@ -19,24 +19,24 @@ class Slice; class WritableFile; // Append a human-readable printout of "num" to *str -extern void AppendNumberTo(std::string* str, uint64_t num); +void AppendNumberTo(std::string* str, uint64_t num); // Append a human-readable printout of "value" to *str. // Escapes any non-printable characters found in "value". -extern void AppendEscapedStringTo(std::string* str, const Slice& value); +void AppendEscapedStringTo(std::string* str, const Slice& value); // Return a human-readable printout of "num" -extern std::string NumberToString(uint64_t num); +std::string NumberToString(uint64_t num); // Return a human-readable version of "value". // Escapes any non-printable characters found in "value". -extern std::string EscapeString(const Slice& value); +std::string EscapeString(const Slice& value); // Parse a human-readable number from "*in" into *value. On success, // advances "*in" past the consumed number and sets "*val" to the // numeric value. Otherwise, returns false and leaves *in in an // unspecified state. -extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); +bool ConsumeDecimalNumber(Slice* in, uint64_t* val); } // namespace leveldb diff --git a/util/testharness.h b/util/testharness.h index da4fe68bb4..029e5c744a 100644 --- a/util/testharness.h +++ b/util/testharness.h @@ -27,15 +27,15 @@ namespace test { // // Returns 0 if all tests pass. // Dies or returns a non-zero value if some test fails. -extern int RunAllTests(); +int RunAllTests(); // Return the directory to use for temporary storage. -extern std::string TmpDir(); +std::string TmpDir(); // Return a randomization seed for this run. Typically returns the // same number on repeated invocations of this binary, but automated // runs may be able to vary the seed. -extern int RandomSeed(); +int RandomSeed(); // An instance of Tester is allocated to hold temporary state during // the execution of an assertion. @@ -129,8 +129,7 @@ void TCONCAT(_Test_,name)::_Run() // Register the specified test. Typically not used directly, but // invoked via the macro expansion of TEST. -extern bool RegisterTest(const char* base, const char* name, void (*func)()); - +bool RegisterTest(const char* base, const char* name, void (*func)()); } // namespace test } // namespace leveldb diff --git a/util/testutil.cc b/util/testutil.cc index bee56bf75f..9d8079ce57 100644 --- a/util/testutil.cc +++ b/util/testutil.cc @@ -31,8 +31,8 @@ std::string RandomKey(Random* rnd, int len) { } -extern Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst) { +Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst) { int raw = static_cast(len * compressed_fraction); if (raw < 1) raw = 1; std::string raw_data; diff --git a/util/testutil.h b/util/testutil.h index d7e4583702..8726bf7d84 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -14,17 +14,17 @@ namespace test { // Store in *dst a random string of length "len" and return a Slice that // references the generated data. -extern Slice RandomString(Random* rnd, int len, std::string* dst); +Slice RandomString(Random* rnd, int len, std::string* dst); // Return a random key with the specified length that may contain interesting // characters (e.g. \x00, \xff, etc.). -extern std::string RandomKey(Random* rnd, int len); +std::string RandomKey(Random* rnd, int len); // Store in *dst a string of length "len" that will compress to // "N*compressed_fraction" bytes and return a Slice that references // the generated data. -extern Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst); +Slice CompressibleString(Random* rnd, double compressed_fraction, + size_t len, std::string* dst); // A wrapper that allows injection of errors. class ErrorEnv : public EnvWrapper { From 8143c12f3fc483b1ba61cdce11f9c1faf6d01bea Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 16 Mar 2018 06:23:29 -0700 Subject: [PATCH 077/174] Fix includes in util/testharness.h. This CL removes unused headers included by util/testharness.h, adds precise includes where the build breaks, and fixes style errors in the edited files. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189331061 --- db/db_bench.cc | 3 +-- util/coding.h | 6 ++++-- util/coding_test.cc | 3 ++- util/testharness.cc | 6 +++++- util/testharness.h | 40 ++++++++++++++++++++-------------------- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/db/db_bench.cc b/db/db_bench.cc index 3ad19a512b..920f119757 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -5,11 +5,10 @@ #include #include #include -#include "db/db_impl.h" -#include "db/version_set.h" #include "leveldb/cache.h" #include "leveldb/db.h" #include "leveldb/env.h" +#include "leveldb/filter_policy.h" #include "leveldb/write_batch.h" #include "port/port.h" #include "util/crc32c.h" diff --git a/util/coding.h b/util/coding.h index 5c4d40e2e3..1fb3d66154 100644 --- a/util/coding.h +++ b/util/coding.h @@ -12,7 +12,9 @@ #include #include + #include + #include "leveldb/slice.h" #include "port/port.h" @@ -35,8 +37,8 @@ bool GetLengthPrefixedSlice(Slice* input, Slice* result); // in *v and return a pointer just past the parsed value, or return // NULL on error. These routines only look at bytes in the range // [p..limit-1] -const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); -const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); +const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* v); +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v); // Returns the length of the varint32 or varint64 encoding of "v" int VarintLength(uint64_t v); diff --git a/util/coding_test.cc b/util/coding_test.cc index 521541ea61..22f6adce27 100644 --- a/util/coding_test.cc +++ b/util/coding_test.cc @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "util/coding.h" +#include +#include "util/coding.h" #include "util/testharness.h" namespace leveldb { diff --git a/util/testharness.cc b/util/testharness.cc index 402fab34d7..95f025f8fd 100644 --- a/util/testharness.cc +++ b/util/testharness.cc @@ -4,11 +4,15 @@ #include "util/testharness.h" -#include #include #include #include +#include +#include + +#include "leveldb/env.h" + namespace leveldb { namespace test { diff --git a/util/testharness.h b/util/testharness.h index 029e5c744a..8ee7972b89 100644 --- a/util/testharness.h +++ b/util/testharness.h @@ -7,10 +7,10 @@ #include #include + #include -#include "leveldb/env.h" -#include "leveldb/slice.h" -#include "util/random.h" + +#include "leveldb/status.h" namespace leveldb { namespace test { @@ -74,10 +74,10 @@ class Tester { return *this; } -#define BINARY_OP(name,op) \ +#define BINARY_OP(name, op) \ template \ Tester& name(const X& x, const Y& y) { \ - if (! (x op y)) { \ + if (!(x op y)) { \ ss_ << " failed: " << x << (" " #op " ") << y; \ ok_ = false; \ } \ @@ -111,21 +111,21 @@ class Tester { #define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) #define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) -#define TCONCAT(a,b) TCONCAT1(a,b) -#define TCONCAT1(a,b) a##b - -#define TEST(base,name) \ -class TCONCAT(_Test_,name) : public base { \ - public: \ - void _Run(); \ - static void _RunIt() { \ - TCONCAT(_Test_,name) t; \ - t._Run(); \ - } \ -}; \ -bool TCONCAT(_Test_ignored_,name) = \ - ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ -void TCONCAT(_Test_,name)::_Run() +#define TCONCAT(a, b) TCONCAT1(a, b) +#define TCONCAT1(a, b) a##b + +#define TEST(base, name) \ +class TCONCAT(_Test_, name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_, name) t; \ + t._Run(); \ + } \ +}; \ +bool TCONCAT(_Test_ignored_, name) = \ + ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_, name)::_RunIt); \ +void TCONCAT(_Test_, name)::_Run() // Register the specified test. Typically not used directly, but // invoked via the macro expansion of TEST. From 0fa5a4f7b1ad9dc16b705bcad1f3ca913f187325 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 16 Mar 2018 10:06:35 -0700 Subject: [PATCH 078/174] Extend thread safety annotations. This CL makes it easier to reason about thread safety by: 1) Adding Clang thread safety annotations according to comments. 2) Expanding a couple of variable names, without adding extra lines of code. 3) Adding const in a couple of places. 4) Replacing an always-non-null const pointer with a reference. 5) Fixing style warnings in the modified files. This CL does not annotate the DBImpl members that claim to be protected by the instance mutex, but are accessed without the mutex being held. Those members (and their unprotected accesses) will be addressed in future CLs. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189354657 --- db/db_impl.cc | 68 ++++++++++++++++++++++------------------ db/db_impl.h | 41 ++++++++++++------------ db/memtable.h | 1 - db/repair.cc | 2 +- db/table_cache.cc | 6 ++-- db/table_cache.h | 4 +-- helpers/memenv/memenv.cc | 18 ++++++----- util/cache.cc | 16 +++++----- 8 files changed, 85 insertions(+), 71 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index a9044c261f..8484e46ea7 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -4,12 +4,14 @@ #include "db/db_impl.h" +#include +#include + #include #include #include -#include -#include #include + #include "db/builder.h" #include "db/db_iter.h" #include "db/dbformat.h" @@ -82,7 +84,7 @@ struct DBImpl::CompactionState { }; // Fix user-supplied options to be reasonable -template +template static void ClipToRange(T* ptr, V minvalue, V maxvalue) { if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; if (static_cast(*ptr) < minvalue) *ptr = minvalue; @@ -114,6 +116,11 @@ Options SanitizeOptions(const std::string& dbname, return result; } +static int TableCacheSize(const Options& sanitized_options) { + // Reserve ten files or so for other uses and give the rest to TableCache. + return sanitized_options.max_open_files - kNumNonTableCacheFiles; +} + DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) : env_(raw_options.env), internal_comparator_(raw_options.comparator), @@ -123,9 +130,10 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) owns_info_log_(options_.info_log != raw_options.info_log), owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), + table_cache_(new TableCache(dbname_, options_, TableCacheSize(options_))), db_lock_(NULL), shutting_down_(NULL), - bg_cv_(&mutex_), + background_work_finished_signal_(&mutex_), mem_(NULL), imm_(NULL), logfile_(NULL), @@ -133,24 +141,19 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) log_(NULL), seed_(0), tmp_batch_(new WriteBatch), - bg_compaction_scheduled_(false), - manual_compaction_(NULL) { + background_compaction_scheduled_(false), + manual_compaction_(NULL), + versions_(new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_)) { has_imm_.Release_Store(NULL); - - // Reserve ten files or so for other uses and give the rest to TableCache. - const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; - table_cache_ = new TableCache(dbname_, &options_, table_cache_size); - - versions_ = new VersionSet(dbname_, &options_, table_cache_, - &internal_comparator_); } DBImpl::~DBImpl() { // Wait for background work to finish mutex_.Lock(); shutting_down_.Release_Store(this); // Any non-NULL value is ok - while (bg_compaction_scheduled_) { - bg_cv_.Wait(); + while (background_compaction_scheduled_) { + background_work_finished_signal_.Wait(); } mutex_.Unlock(); @@ -216,6 +219,8 @@ void DBImpl::MaybeIgnoreError(Status* s) const { } void DBImpl::DeleteObsoleteFiles() { + mutex_.AssertHeld(); + if (!bg_error_.ok()) { // After a background error, we don't know whether a new version may // or may not have been committed, so we cannot safely garbage collect. @@ -227,7 +232,7 @@ void DBImpl::DeleteObsoleteFiles() { versions_->AddLiveFiles(&live); std::vector filenames; - env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose uint64_t number; FileType type; for (size_t i = 0; i < filenames.size(); i++) { @@ -263,7 +268,7 @@ void DBImpl::DeleteObsoleteFiles() { table_cache_->Evict(number); } Log(options_.info_log, "Delete type=%d #%lld\n", - int(type), + static_cast(type), static_cast(number)); env_->DeleteFile(dbname_ + "/" + filenames[i]); } @@ -575,13 +580,14 @@ void DBImpl::CompactRange(const Slice* begin, const Slice* end) { } } } - TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap for (int level = 0; level < max_level_with_files; level++) { TEST_CompactRange(level, begin, end); } } -void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { +void DBImpl::TEST_CompactRange(int level, const Slice* begin, + const Slice* end) { assert(level >= 0); assert(level + 1 < config::kNumLevels); @@ -609,7 +615,7 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { manual_compaction_ = &manual; MaybeScheduleCompaction(); } else { // Running either my compaction or another compaction. - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } } if (manual_compaction_ == &manual) { @@ -625,7 +631,7 @@ Status DBImpl::TEST_CompactMemTable() { // Wait until the compaction completes MutexLock l(&mutex_); while (imm_ != NULL && bg_error_.ok()) { - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } if (imm_ != NULL) { s = bg_error_; @@ -638,13 +644,13 @@ void DBImpl::RecordBackgroundError(const Status& s) { mutex_.AssertHeld(); if (bg_error_.ok()) { bg_error_ = s; - bg_cv_.SignalAll(); + background_work_finished_signal_.SignalAll(); } } void DBImpl::MaybeScheduleCompaction() { mutex_.AssertHeld(); - if (bg_compaction_scheduled_) { + if (background_compaction_scheduled_) { // Already scheduled } else if (shutting_down_.Acquire_Load()) { // DB is being deleted; no more background compactions @@ -655,7 +661,7 @@ void DBImpl::MaybeScheduleCompaction() { !versions_->NeedsCompaction()) { // No work to be done } else { - bg_compaction_scheduled_ = true; + background_compaction_scheduled_ = true; env_->Schedule(&DBImpl::BGWork, this); } } @@ -666,7 +672,7 @@ void DBImpl::BGWork(void* db) { void DBImpl::BackgroundCall() { MutexLock l(&mutex_); - assert(bg_compaction_scheduled_); + assert(background_compaction_scheduled_); if (shutting_down_.Acquire_Load()) { // No more background work when shutting down. } else if (!bg_error_.ok()) { @@ -675,12 +681,12 @@ void DBImpl::BackgroundCall() { BackgroundCompaction(); } - bg_compaction_scheduled_ = false; + background_compaction_scheduled_ = false; // Previous compaction may have produced too many files in a level, // so reschedule another compaction if needed. MaybeScheduleCompaction(); - bg_cv_.SignalAll(); + background_work_finished_signal_.SignalAll(); } void DBImpl::BackgroundCompaction() { @@ -920,7 +926,8 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { mutex_.Lock(); if (imm_ != NULL) { CompactMemTable(); - bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + // Wake up MakeRoomForWrite() if necessary. + background_work_finished_signal_.SignalAll(); } mutex_.Unlock(); imm_micros += (env_->NowMicros() - imm_start); @@ -1267,6 +1274,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { // REQUIRES: Writer list must be non-empty // REQUIRES: First writer must have a non-NULL batch WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + mutex_.AssertHeld(); assert(!writers_.empty()); Writer* first = writers_.front(); WriteBatch* result = first->batch; @@ -1346,11 +1354,11 @@ Status DBImpl::MakeRoomForWrite(bool force) { // We have filled up the current memtable, but the previous // one is still being compacted, so we wait. Log(options_.info_log, "Current memtable full; waiting...\n"); - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { // There are too many level-0 files. Log(options_.info_log, "Too many L0 files; waiting...\n"); - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } else { // Attempt to switch to a new memtable and trigger compaction of old assert(versions_->PrevLogNumber() == 0); diff --git a/db/db_impl.h b/db/db_impl.h index 3861b8663a..634411283a 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -84,7 +84,7 @@ class DBImpl : public DB { void MaybeIgnoreError(Status* s) const; // Delete any unneeded files and stale in-memory entries. - void DeleteObsoleteFiles(); + void DeleteObsoleteFiles() EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Compact the in-memory write buffer to disk. Switches to a new // log-file/memtable and writes a new descriptor iff successful. @@ -100,14 +100,15 @@ class DBImpl : public DB { Status MakeRoomForWrite(bool force /* compact even if there is room? */) EXCLUSIVE_LOCKS_REQUIRED(mutex_); - WriteBatch* BuildBatchGroup(Writer** last_writer); + WriteBatch* BuildBatchGroup(Writer** last_writer) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); void RecordBackgroundError(const Status& s); void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); static void BGWork(void* db); void BackgroundCall(); - void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); void CleanupCompaction(CompactionState* compact) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status DoCompactionWork(CompactionState* compact) @@ -123,12 +124,12 @@ class DBImpl : public DB { const InternalKeyComparator internal_comparator_; const InternalFilterPolicy internal_filter_policy_; const Options options_; // options_.comparator == &internal_comparator_ - bool owns_info_log_; - bool owns_cache_; + const bool owns_info_log_; + const bool owns_cache_; const std::string dbname_; // table_cache_ provides its own synchronization - TableCache* table_cache_; + TableCache* const table_cache_; // Lock over the persistent DB state. Non-NULL iff successfully acquired. FileLock* db_lock_; @@ -136,27 +137,27 @@ class DBImpl : public DB { // State below is protected by mutex_ port::Mutex mutex_; port::AtomicPointer shutting_down_; - port::CondVar bg_cv_; // Signalled when background work finishes + port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_); MemTable* mem_; - MemTable* imm_; // Memtable being compacted - port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted + port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ WritableFile* logfile_; - uint64_t logfile_number_; + uint64_t logfile_number_ GUARDED_BY(mutex_); log::Writer* log_; - uint32_t seed_; // For sampling. + uint32_t seed_ GUARDED_BY(mutex_); // For sampling. // Queue of writers. - std::deque writers_; - WriteBatch* tmp_batch_; + std::deque writers_ GUARDED_BY(mutex_); + WriteBatch* tmp_batch_ GUARDED_BY(mutex_); - SnapshotList snapshots_; + SnapshotList snapshots_ GUARDED_BY(mutex_); // Set of table files to protect from deletion because they are // part of ongoing compactions. - std::set pending_outputs_; + std::set pending_outputs_ GUARDED_BY(mutex_); // Has a background compaction been scheduled or is running? - bool bg_compaction_scheduled_; + bool background_compaction_scheduled_ GUARDED_BY(mutex_); // Information for a manual compaction struct ManualCompaction { @@ -166,12 +167,12 @@ class DBImpl : public DB { const InternalKey* end; // NULL means end of key range InternalKey tmp_storage; // Used to keep track of compaction progress }; - ManualCompaction* manual_compaction_; + ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); - VersionSet* versions_; + VersionSet* const versions_; // Have we encountered a background error in paranoid mode? - Status bg_error_; + Status bg_error_ GUARDED_BY(mutex_); // Per level compaction stats. stats_[level] stores the stats for // compactions that produced data for the specified "level". @@ -188,7 +189,7 @@ class DBImpl : public DB { this->bytes_written += c.bytes_written; } }; - CompactionStats stats_[config::kNumLevels]; + CompactionStats stats_[config::kNumLevels] GUARDED_BY(mutex_); // No copying allowed DBImpl(const DBImpl&); diff --git a/db/memtable.h b/db/memtable.h index 9f41567cde..f2a6736cf9 100644 --- a/db/memtable.h +++ b/db/memtable.h @@ -14,7 +14,6 @@ namespace leveldb { class InternalKeyComparator; -class Mutex; class MemTableIterator; class MemTable { diff --git a/db/repair.cc b/db/repair.cc index 4cd4bb047f..c10da82a4c 100644 --- a/db/repair.cc +++ b/db/repair.cc @@ -54,7 +54,7 @@ class Repairer { owns_cache_(options_.block_cache != options.block_cache), next_file_number_(1) { // TableCache can be small since we expect each table to be opened once. - table_cache_ = new TableCache(dbname_, &options_, 10); + table_cache_ = new TableCache(dbname_, options_, 10); } ~Repairer() { diff --git a/db/table_cache.cc b/db/table_cache.cc index e3d82cd3ea..6cf005b91f 100644 --- a/db/table_cache.cc +++ b/db/table_cache.cc @@ -30,9 +30,9 @@ static void UnrefEntry(void* arg1, void* arg2) { } TableCache::TableCache(const std::string& dbname, - const Options* options, + const Options& options, int entries) - : env_(options->env), + : env_(options.env), dbname_(dbname), options_(options), cache_(NewLRUCache(entries)) { @@ -61,7 +61,7 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, } } if (s.ok()) { - s = Table::Open(*options_, file, file_size, &table); + s = Table::Open(options_, file, file_size, &table); } if (!s.ok()) { diff --git a/db/table_cache.h b/db/table_cache.h index 8cf4aaf12d..e9191dc9a7 100644 --- a/db/table_cache.h +++ b/db/table_cache.h @@ -20,7 +20,7 @@ class Env; class TableCache { public: - TableCache(const std::string& dbname, const Options* options, int entries); + TableCache(const std::string& dbname, const Options& options, int entries); ~TableCache(); // Return an iterator for the specified file number (the corresponding @@ -50,7 +50,7 @@ class TableCache { private: Env* const env_; const std::string dbname_; - const Options* options_; + const Options& options_; Cache* cache_; Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index 9a98884daf..ee7abd48e4 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -4,14 +4,17 @@ #include "helpers/memenv/memenv.h" +#include + +#include +#include +#include + #include "leveldb/env.h" #include "leveldb/status.h" #include "port/port.h" +#include "port/thread_annotations.h" #include "util/mutexlock.h" -#include -#include -#include -#include namespace leveldb { @@ -135,7 +138,7 @@ class FileState { void operator=(const FileState&); port::Mutex refs_mutex_; - int refs_; // Protected by refs_mutex_; + int refs_ GUARDED_BY(refs_mutex_); // The following fields are not protected by any mutex. They are only mutable // while the file is being written, and concurrent access is not allowed @@ -312,7 +315,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - void DeleteFileInternal(const std::string& fname) { + void DeleteFileInternal(const std::string& fname) + EXCLUSIVE_LOCKS_REQUIRED(mutex_) { if (file_map_.find(fname) == file_map_.end()) { return; } @@ -386,7 +390,7 @@ class InMemoryEnv : public EnvWrapper { // Map from filenames to FileState objects, representing a simple file system. typedef std::map FileSystem; port::Mutex mutex_; - FileSystem file_map_; // Protected by mutex_. + FileSystem file_map_ GUARDED_BY(mutex_); }; } // namespace diff --git a/util/cache.cc b/util/cache.cc index bd914aea32..10b7103d50 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -8,6 +8,7 @@ #include "leveldb/cache.h" #include "port/port.h" +#include "port/thread_annotations.h" #include "util/hash.h" #include "util/mutexlock.h" @@ -174,25 +175,25 @@ class LRUCache { void LRU_Append(LRUHandle*list, LRUHandle* e); void Ref(LRUHandle* e); void Unref(LRUHandle* e); - bool FinishErase(LRUHandle* e); + bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Initialized before use. size_t capacity_; // mutex_ protects the following state. mutable port::Mutex mutex_; - size_t usage_; + size_t usage_ GUARDED_BY(mutex_); // Dummy head of LRU list. // lru.prev is newest entry, lru.next is oldest entry. // Entries have refs==1 and in_cache==true. - LRUHandle lru_; + LRUHandle lru_ GUARDED_BY(mutex_); // Dummy head of in-use list. // Entries are in use by clients, and have refs >= 2 and in_cache==true. - LRUHandle in_use_; + LRUHandle in_use_ GUARDED_BY(mutex_); - HandleTable table_; + HandleTable table_ GUARDED_BY(mutex_); }; LRUCache::LRUCache() @@ -227,11 +228,12 @@ void LRUCache::Ref(LRUHandle* e) { void LRUCache::Unref(LRUHandle* e) { assert(e->refs > 0); e->refs--; - if (e->refs == 0) { // Deallocate. + if (e->refs == 0) { // Deallocate. assert(!e->in_cache); (*e->deleter)(e->key(), e->value); free(e); - } else if (e->in_cache && e->refs == 1) { // No longer in use; move to lru_ list. + } else if (e->in_cache && e->refs == 1) { + // No longer in use; move to lru_ list. LRU_Remove(e); LRU_Append(&lru_, e); } From 739c25100e46576cdcdfff2d6f43f9f7008103c7 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 16 Mar 2018 19:10:13 -0700 Subject: [PATCH 079/174] Add CMake build support. Fixes https://github.com/google/leveldb/issues/466 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189425354 --- .travis.yml | 17 +- CMakeLists.txt | 412 ++++++++++++++++++++++++++++++++++++++ cmake/LeveldbConfig.cmake | 1 + db/dumpfile.cc | 3 + helpers/memenv/memenv.h | 4 +- include/leveldb/export.h | 6 +- port/port_config.h.in | 36 ++++ port/port_posix.h | 61 ++---- 8 files changed, 493 insertions(+), 47 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 cmake/LeveldbConfig.cmake create mode 100644 port/port_config.h.in diff --git a/.travis.yml b/.travis.yml index 77ea0a3189..5999274a97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,10 @@ os: - linux - osx +env: + - BUILD_TYPE=Debug + - BUILD_TYPE=RelWithDebInfo + addons: apt: # List of whitelisted in travis packages for ubuntu-trusty can be found here: @@ -23,6 +27,7 @@ addons: - ubuntu-toolchain-r-test - llvm-toolchain-trusty-5.0 packages: + - cmake - gcc-7 - g++-7 - clang-5.0 @@ -49,6 +54,16 @@ install: - echo ${CC} - echo ${CXX} - ${CXX} --version +- cmake --version + +before_script: +- mkdir -p build && cd build +- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE +- cmake --build . +- cd .. script: -- make -j 4 check +- cd build ; ctest --verbose ; cd .. +- "if [ -f build/db_bench ] ; then build/db_bench ; fi" +- "if [ -f build/db_bench_sqlite3 ] ; then build/db_bench_sqlite3 ; fi" +- "if [ -f build/db_bench_tree_db ] ; then build/db_bench_tree_db ; fi" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..df486eaaf0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,412 @@ +# Copyright 2017 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +cmake_minimum_required(VERSION 3.9) +project(Leveldb VERSION 0.1.0 LANGUAGES C CXX) + +# This project can take advantage of C++11. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED OFF) +set(CMAKE_CXX_EXTENSIONS OFF) + +option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON) +option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON) +option(LEVELDB_INSTALL "Install LevelDB's header and library" ON) + +include(TestBigEndian) +test_big_endian(LEVELDB_IS_BIG_ENDIAN) + +include(CheckIncludeFile) +check_include_file("unistd.h" HAVE_UNISTD_H) + +include(CheckIncludeFileCXX) +check_include_file_cxx("atomic" LEVELDB_ATOMIC_PRESENT) + +include(CheckLibraryExists) +check_library_exists(crc32c crc32c_value "" HAVE_CRC32C) +check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) +check_library_exists(tcmalloc malloc "" HAVE_TCMALLOC) + +include(CheckSymbolExists) +check_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) + +include(CheckCXXSourceCompiles) + +# Test whether -Wthread-safety is available. See +# https://clang.llvm.org/docs/ThreadSafetyAnalysis.html +# -Werror is necessary because unknown attributes only generate warnings. +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +list(APPEND CMAKE_REQUIRED_FLAGS -Werror -Wthread-safety) +check_cxx_source_compiles(" +struct __attribute__((lockable)) Lock { + void Acquire() __attribute__((exclusive_lock_function())); + void Release() __attribute__((unlock_function())); +}; +struct ThreadSafeType { + Lock lock_; + int data_ __attribute__((guarded_by(lock_))); +}; +int main() { return 0; } +" HAVE_CLANG_THREAD_SAFETY) +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) + +set(LEVELDB_PUBLIC_INCLUDE_DIR "include/leveldb") +set(LEVELDB_PORT_CONFIG_DIR "include/port") + +configure_file( + "${PROJECT_SOURCE_DIR}/port/port_config.h.in" + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" +) + +include_directories( + "${PROJECT_BINARY_DIR}/include" + "${PROJECT_SOURCE_DIR}" +) + +if(BUILD_SHARED_LIBS) + # Only export LEVELDB_EXPORT symbols from the shared library. + add_compile_options(-fvisibility=hidden) +endif(BUILD_SHARED_LIBS) + +# POSIX code is specified separately so we can leave it out in the future. +add_library(leveldb_port_posix OBJECT "") +target_sources(leveldb_port_posix + PRIVATE + "${PROJECT_SOURCE_DIR}/port/port_posix.cc" + + PUBLIC + # The headers below are dependencies for leveldb, but aren't needed by users + # that link to the installed version of leveldb and rely on its public API. + $ + $ + $ + $ +) +if(BUILD_SHARED_LIBS) + set_property(TARGET leveldb_port_posix PROPERTY POSITION_INDEPENDENT_CODE ON) +endif(BUILD_SHARED_LIBS) + +add_library(leveldb "") +target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/db/builder.cc" + "${PROJECT_SOURCE_DIR}/db/builder.h" + "${PROJECT_SOURCE_DIR}/db/c.cc" + "${PROJECT_SOURCE_DIR}/db/db_impl.cc" + "${PROJECT_SOURCE_DIR}/db/db_impl.h" + "${PROJECT_SOURCE_DIR}/db/db_iter.cc" + "${PROJECT_SOURCE_DIR}/db/db_iter.h" + "${PROJECT_SOURCE_DIR}/db/dbformat.cc" + "${PROJECT_SOURCE_DIR}/db/dbformat.h" + "${PROJECT_SOURCE_DIR}/db/dumpfile.cc" + "${PROJECT_SOURCE_DIR}/db/filename.cc" + "${PROJECT_SOURCE_DIR}/db/filename.h" + "${PROJECT_SOURCE_DIR}/db/log_format.h" + "${PROJECT_SOURCE_DIR}/db/log_reader.cc" + "${PROJECT_SOURCE_DIR}/db/log_reader.h" + "${PROJECT_SOURCE_DIR}/db/log_writer.cc" + "${PROJECT_SOURCE_DIR}/db/log_writer.h" + "${PROJECT_SOURCE_DIR}/db/memtable.cc" + "${PROJECT_SOURCE_DIR}/db/memtable.h" + "${PROJECT_SOURCE_DIR}/db/repair.cc" + "${PROJECT_SOURCE_DIR}/db/skiplist.h" + "${PROJECT_SOURCE_DIR}/db/snapshot.h" + "${PROJECT_SOURCE_DIR}/db/table_cache.cc" + "${PROJECT_SOURCE_DIR}/db/table_cache.h" + "${PROJECT_SOURCE_DIR}/db/version_edit.cc" + "${PROJECT_SOURCE_DIR}/db/version_edit.h" + "${PROJECT_SOURCE_DIR}/db/version_set.cc" + "${PROJECT_SOURCE_DIR}/db/version_set.h" + "${PROJECT_SOURCE_DIR}/db/write_batch_internal.h" + "${PROJECT_SOURCE_DIR}/db/write_batch.cc" + "${PROJECT_SOURCE_DIR}/port/port.h" + "${PROJECT_SOURCE_DIR}/port/thread_annotations.h" + "${PROJECT_SOURCE_DIR}/table/block_builder.cc" + "${PROJECT_SOURCE_DIR}/table/block_builder.h" + "${PROJECT_SOURCE_DIR}/table/block.cc" + "${PROJECT_SOURCE_DIR}/table/block.h" + "${PROJECT_SOURCE_DIR}/table/filter_block.cc" + "${PROJECT_SOURCE_DIR}/table/filter_block.h" + "${PROJECT_SOURCE_DIR}/table/format.cc" + "${PROJECT_SOURCE_DIR}/table/format.h" + "${PROJECT_SOURCE_DIR}/table/iterator_wrapper.h" + "${PROJECT_SOURCE_DIR}/table/iterator.cc" + "${PROJECT_SOURCE_DIR}/table/merger.cc" + "${PROJECT_SOURCE_DIR}/table/merger.h" + "${PROJECT_SOURCE_DIR}/table/table_builder.cc" + "${PROJECT_SOURCE_DIR}/table/table.cc" + "${PROJECT_SOURCE_DIR}/table/two_level_iterator.cc" + "${PROJECT_SOURCE_DIR}/table/two_level_iterator.h" + "${PROJECT_SOURCE_DIR}/util/arena.cc" + "${PROJECT_SOURCE_DIR}/util/arena.h" + "${PROJECT_SOURCE_DIR}/util/bloom.cc" + "${PROJECT_SOURCE_DIR}/util/cache.cc" + "${PROJECT_SOURCE_DIR}/util/coding.cc" + "${PROJECT_SOURCE_DIR}/util/coding.h" + "${PROJECT_SOURCE_DIR}/util/comparator.cc" + "${PROJECT_SOURCE_DIR}/util/crc32c.cc" + "${PROJECT_SOURCE_DIR}/util/crc32c.h" + "${PROJECT_SOURCE_DIR}/util/env_posix.cc" + "${PROJECT_SOURCE_DIR}/util/env.cc" + "${PROJECT_SOURCE_DIR}/util/filter_policy.cc" + "${PROJECT_SOURCE_DIR}/util/hash.cc" + "${PROJECT_SOURCE_DIR}/util/hash.h" + "${PROJECT_SOURCE_DIR}/util/logging.cc" + "${PROJECT_SOURCE_DIR}/util/logging.h" + "${PROJECT_SOURCE_DIR}/util/mutexlock.h" + "${PROJECT_SOURCE_DIR}/util/options.cc" + "${PROJECT_SOURCE_DIR}/util/posix_logger.h" + "${PROJECT_SOURCE_DIR}/util/random.h" + "${PROJECT_SOURCE_DIR}/util/status.cc" + $ + + # Only CMake 3.3+ supports PUBLIC sources in targets exported by "install". + $<$:PUBLIC> + "${LEVELDB_PUBLIC_INCLUDE_DIR}/c.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/cache.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/comparator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/slice.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/status.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table_builder.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" +) +# MemEnv is not part of the interface and could be pulled to a separate library. +target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/helpers/memenv/memenv.cc" + "${PROJECT_SOURCE_DIR}/helpers/memenv/memenv.h" +) +target_include_directories(leveldb + PUBLIC + $ + $ +) +target_compile_definitions(leveldb + PRIVATE + # Used by include/export.h when building shared libraries. + LEVELDB_COMPILE_LIBRARY + # Used by port/port.h. + LEVELDB_PLATFORM_POSIX=1 +) +if(BUILD_SHARED_LIBS) + target_compile_definitions(leveldb + PUBLIC + # Used by include/export.h. + LEVELDB_SHARED_LIBRARY + ) +endif(BUILD_SHARED_LIBS) + +if(HAVE_CLANG_THREAD_SAFETY) + target_compile_options(leveldb + PUBLIC + -Werror -Wthread-safety) +endif(HAVE_CLANG_THREAD_SAFETY) + +# TODO(costan): This is only needed for port_posix. +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(leveldb Threads::Threads) + +if(HAVE_CRC32C) + target_link_libraries(leveldb crc32c) +endif(HAVE_CRC32C) +if(HAVE_SNAPPY) + target_link_libraries(leveldb snappy) +endif(HAVE_SNAPPY) +if(HAVE_TCMALLOC) + target_link_libraries(leveldb tcmalloc) +endif(HAVE_TCMALLOC) + +add_executable(leveldbutil + "${PROJECT_SOURCE_DIR}/db/leveldbutil.cc" +) +target_link_libraries(leveldbutil leveldb) + +if(LEVELDB_BUILD_TESTS) + enable_testing() + + function(leveldb_test test_file) + get_filename_component(test_target_name "${test_file}" NAME_WE) + + add_executable("${test_target_name}" "") + target_sources("${test_target_name}" + PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "${PROJECT_SOURCE_DIR}/util/testharness.cc" + "${PROJECT_SOURCE_DIR}/util/testharness.h" + "${PROJECT_SOURCE_DIR}/util/testutil.cc" + "${PROJECT_SOURCE_DIR}/util/testutil.h" + + "${test_file}" + ) + if(BUILD_SHARED_LIBS) + # Port functions aren't exposed in the shared library build. + target_sources("${test_target_name}" + PRIVATE + $ + ) + endif(BUILD_SHARED_LIBS) + + target_link_libraries("${test_target_name}" leveldb) + target_compile_definitions("${test_target_name}" + PRIVATE + LEVELDB_PLATFORM_POSIX=1 + ) + + add_test(NAME "${test_target_name}" COMMAND "${test_target_name}") + endfunction(leveldb_test) + + leveldb_test("${PROJECT_SOURCE_DIR}/db/c_test.c") + leveldb_test("${PROJECT_SOURCE_DIR}/db/fault_injection_test.cc") + + leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue178_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue200_test.cc") + + leveldb_test("${PROJECT_SOURCE_DIR}/util/env_test.cc") + + if(NOT BUILD_SHARED_LIBS) + leveldb_test("${PROJECT_SOURCE_DIR}/db/autocompact_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/corruption_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/db_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/dbformat_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/filename_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/log_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/recovery_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/skiplist_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/version_edit_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/version_set_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/db/write_batch_test.cc") + + leveldb_test("${PROJECT_SOURCE_DIR}/helpers/memenv/memenv_test.cc") + + leveldb_test("${PROJECT_SOURCE_DIR}/table/filter_block_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/table/table_test.cc") + + leveldb_test("${PROJECT_SOURCE_DIR}/util/arena_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/bloom_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/cache_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/coding_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/crc32c_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/hash_test.cc") + + # TODO(costan): This test also uses + # "${PROJECT_SOURCE_DIR}/util/env_posix_test_helper.h" + leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc") + endif(NOT BUILD_SHARED_LIBS) +endif(LEVELDB_BUILD_TESTS) + +if(LEVELDB_BUILD_BENCHMARKS) + function(leveldb_benchmark bench_file) + get_filename_component(bench_target_name "${bench_file}" NAME_WE) + + add_executable("${bench_target_name}" "") + target_sources("${bench_target_name}" + PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "${PROJECT_SOURCE_DIR}/util/histogram.cc" + "${PROJECT_SOURCE_DIR}/util/histogram.h" + "${PROJECT_SOURCE_DIR}/util/testharness.cc" + "${PROJECT_SOURCE_DIR}/util/testharness.h" + "${PROJECT_SOURCE_DIR}/util/testutil.cc" + "${PROJECT_SOURCE_DIR}/util/testutil.h" + + "${bench_file}" + ) + if(BUILD_SHARED_LIBS) + # Port functions aren't exposed in the shared library build. + target_sources("${bench_target_name}" + PRIVATE + $ + ) + endif(BUILD_SHARED_LIBS) + + target_link_libraries("${bench_target_name}" leveldb) + target_compile_definitions("${bench_target_name}" + PRIVATE + LEVELDB_PLATFORM_POSIX=1 + ) + endfunction(leveldb_benchmark) + + if(NOT BUILD_SHARED_LIBS) + leveldb_benchmark("${PROJECT_SOURCE_DIR}/db/db_bench.cc") + endif(NOT BUILD_SHARED_LIBS) + + check_library_exists(sqlite3 sqlite3_open "" HAVE_SQLITE3) + if(HAVE_SQLITE3) + leveldb_benchmark("${PROJECT_SOURCE_DIR}/doc/bench/db_bench_sqlite3.cc") + target_link_libraries(db_bench_sqlite3 sqlite3) + endif(HAVE_SQLITE3) + + # check_library_exists is insufficient here because the library names have + # different manglings when compiled with clang or gcc, at least when installed + # with Homebrew on Mac. + set(OLD_CMAKE_REQURED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES kyotocabinet) + check_cxx_source_compiles(" +#include + +int main() { + kyotocabinet::TreeDB* db = new kyotocabinet::TreeDB(); + delete db; + return 0; +} + " HAVE_KYOTOCABINET) + set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQURED_LIBRARIES}) + if(HAVE_KYOTOCABINET) + leveldb_benchmark("${PROJECT_SOURCE_DIR}/doc/bench/db_bench_tree_db.cc") + target_link_libraries(db_bench_tree_db kyotocabinet) + endif(HAVE_KYOTOCABINET) +endif(LEVELDB_BUILD_BENCHMARKS) + +if(LEVELDB_INSTALL) + include(GNUInstallDirs) + install(TARGETS leveldb + EXPORT LeveldbTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + install( + FILES + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/c.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/cache.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/comparator.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/slice.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/status.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/table_builder.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/leveldb + ) + + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/LeveldbConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ) + install( + EXPORT LeveldbTargets + NAMESPACE Leveldb:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Leveldb" + ) + install( + FILES + "${PROJECT_SOURCE_DIR}/cmake/LeveldbConfig.cmake" + "${PROJECT_BINARY_DIR}/LeveldbConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Leveldb" + ) +endif(LEVELDB_INSTALL) diff --git a/cmake/LeveldbConfig.cmake b/cmake/LeveldbConfig.cmake new file mode 100644 index 0000000000..8f3e78b47c --- /dev/null +++ b/cmake/LeveldbConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/LeveldbTargets.cmake") diff --git a/db/dumpfile.cc b/db/dumpfile.cc index 61c47c2ff9..2f1b5d91b3 100644 --- a/db/dumpfile.cc +++ b/db/dumpfile.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/dumpfile.h" + #include + #include "db/dbformat.h" #include "db/filename.h" #include "db/log_reader.h" diff --git a/helpers/memenv/memenv.h b/helpers/memenv/memenv.h index 03b88de761..3d929e4c4e 100644 --- a/helpers/memenv/memenv.h +++ b/helpers/memenv/memenv.h @@ -5,6 +5,8 @@ #ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ #define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#include "leveldb/export.h" + namespace leveldb { class Env; @@ -13,7 +15,7 @@ class Env; // all non-file-storage tasks to base_env. The caller must delete the result // when it is no longer needed. // *base_env must remain live while the result is in use. -Env* NewMemEnv(Env* base_env); +LEVELDB_EXPORT Env* NewMemEnv(Env* base_env); } // namespace leveldb diff --git a/include/leveldb/export.h b/include/leveldb/export.h index 39b20972a6..6ba9b183da 100644 --- a/include/leveldb/export.h +++ b/include/leveldb/export.h @@ -8,7 +8,7 @@ #if !defined(LEVELDB_EXPORT) #if defined(LEVELDB_SHARED_LIBRARY) -#if defined(OS_WIN) +#if defined(_WIN32) #if defined(LEVELDB_COMPILE_LIBRARY) #define LEVELDB_EXPORT __declspec(dllexport) @@ -16,13 +16,13 @@ #define LEVELDB_EXPORT __declspec(dllimport) #endif // defined(LEVELDB_COMPILE_LIBRARY) -#else // defined(OS_WIN) +#else // defined(_WIN32) #if defined(LEVELDB_COMPILE_LIBRARY) #define LEVELDB_EXPORT __attribute__((visibility("default"))) #else #define LEVELDB_EXPORT #endif -#endif // defined(OS_WIN) +#endif // defined(_WIN32) #else // defined(LEVELDB_SHARED_LIBRARY) #define LEVELDB_EXPORT diff --git a/port/port_config.h.in b/port/port_config.h.in new file mode 100644 index 0000000000..549f179077 --- /dev/null +++ b/port/port_config.h.in @@ -0,0 +1,36 @@ +// Copyright 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ +#define STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ + +// Define to 1 if you have a definition for fdatasync() in . +#if !defined(HAVE_FUNC_FDATASYNC) +#cmakedefine01 HAVE_FUNC_FDATASYNC +#endif // !defined(HAVE_FUNC_FDATASYNC) + +// Define to 1 if you have Google CRC32C. +#if !defined(HAVE_CRC32C) +#cmakedefine01 HAVE_CRC32C +#endif // !defined(HAVE_CRC32C) + +// Define to 1 if you have Google Snappy. +#if !defined(HAVE_SNAPPY) +#cmakedefine01 HAVE_SNAPPY +#endif // !defined(HAVE_SNAPPY) + +// Define to 1 if your processor stores words with the most significant byte +// first (like Motorola and SPARC, unlike Intel and VAX). +#if !defined(LEVELDB_IS_BIG_ENDIAN) +#cmakedefine01 LEVELDB_IS_BIG_ENDIAN +#endif // !defined(LEVELDB_IS_BIG_ENDIAN) + +// Define to 1 if you have the header. +// NOTE: was standardized in C++11, which will be required to build +// LevelDB soon. +#if !defined(LEVELDB_ATOMIC_PRESENT) +#cmakedefine01 LEVELDB_ATOMIC_PRESENT +#endif // !defined(LEVELDB_ATOMIC_PRESENT) + +#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ \ No newline at end of file diff --git a/port/port_posix.h b/port/port_posix.h index ce52042b59..2fb2269efa 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -7,36 +7,22 @@ #ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ #define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ -#undef PLATFORM_IS_LITTLE_ENDIAN -#if defined(__APPLE__) - #include - #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) - #define PLATFORM_IS_LITTLE_ENDIAN \ - (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) - #endif -#elif defined(OS_SOLARIS) - #include - #ifdef _LITTLE_ENDIAN - #define PLATFORM_IS_LITTLE_ENDIAN true - #else - #define PLATFORM_IS_LITTLE_ENDIAN false - #endif -#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) ||\ - defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) - #include - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) -#elif defined(OS_HPUX) - #define PLATFORM_IS_LITTLE_ENDIAN false -#elif defined(OS_ANDROID) - // Due to a bug in the NDK x86 definition, - // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. - // See http://code.google.com/p/android/issues/detail?id=39824 - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) -#else - #include -#endif +// port/port_config.h availability is automatically detected via __has_include +// in newer compilers. If LEVELDB_HAS_PORT_CONFIG_H is defined, it overrides the +// configuration detection. +#if defined(LEVELDB_HAS_PORT_CONFIG_H) + +#if LEVELDB_HAS_PORT_CONFIG_H +#include "port/port_config.h" +#endif // LEVELDB_HAS_PORT_CONFIG_H + +#elif defined(__has_include) + +#if __has_include("port/port_config.h") +#include "port/port_config.h" +#endif // __has_include("port/port_config.h") + +#endif // defined(LEVELDB_HAS_PORT_CONFIG_H) #include #if HAVE_CRC32C @@ -54,23 +40,14 @@ #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) #endif -#if defined(__APPLE__) || defined(OS_FREEBSD) ||\ - defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) -// Use fsync() on platforms without fdatasync() +#if !HAVE_FDATASYNC #define fdatasync fsync -#endif - -#if defined(OS_ANDROID) && __ANDROID_API__ < 9 -// fdatasync() was only introduced in API level 9 on Android. Use fsync() -// when targetting older platforms. -#define fdatasync fsync -#endif +#endif // !HAVE_FDATASYNC namespace leveldb { namespace port { -static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; -#undef PLATFORM_IS_LITTLE_ENDIAN +static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN; class CondVar; From 50fbc87e8c62a816d6afd4740e0652a13ac6dc3e Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 20 Mar 2018 15:22:23 -0700 Subject: [PATCH 080/174] Replace SIZE_MAX with std::numeric_limits. helpers/memenv/memenv.cc used SIZE_MAX without including . Since we're fixing this problem, replace SIZE_MAX with std::numeric_limits::max(), which is clearer. Fixes https://github.com/google/leveldb/issues/562 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189821707 --- helpers/memenv/memenv.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index ee7abd48e4..43b009d3db 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -65,7 +66,7 @@ class FileState { return Status::OK(); } - assert(offset / kBlockSize <= SIZE_MAX); + assert(offset / kBlockSize <= std::numeric_limits::max()); size_t block = static_cast(offset / kBlockSize); size_t block_offset = offset % kBlockSize; From df9a841a4fc9a04c7713542d75f50e749fb64b7b Mon Sep 17 00:00:00 2001 From: Hyzeta Date: Tue, 20 Mar 2018 15:28:09 -0700 Subject: [PATCH 081/174] Add export.h to CMakeLists.txt The header is missing from the file lists, resulting in incomplete installs. This was originally contributed in https://github.com/google/leveldb/pull/568. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189822907 --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index df486eaaf0..73b69a4c79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,7 @@ target_sources(leveldb "${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/export.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" @@ -382,6 +383,7 @@ if(LEVELDB_INSTALL) "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/export.h" "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" "${PROJECT_SOURCE_DIR}/${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" From 8e75db8623703cdc25ec3cd06f82129296672489 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 20 Mar 2018 15:35:14 -0700 Subject: [PATCH 082/174] Remove build configuration for make. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189824264 --- Makefile | 428 ------------------------------------------ build_detect_platform | 262 -------------------------- 2 files changed, 690 deletions(-) delete mode 100644 Makefile delete mode 100755 build_detect_platform diff --git a/Makefile b/Makefile deleted file mode 100644 index 82dd84a9a4..0000000000 --- a/Makefile +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) 2011 The LevelDB Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. See the AUTHORS file for names of contributors. - -#----------------------------------------------- -# Uncomment exactly one of the lines labelled (A), (B), and (C) below -# to switch between compilation modes. - -# (A) Production use (optimized mode) -OPT ?= -O2 -DNDEBUG -# (B) Debug mode, w/ full line-level debugging symbols -# OPT ?= -g2 -# (C) Profiling mode: opt, but w/debugging symbols -# OPT ?= -O2 -g2 -DNDEBUG -#----------------------------------------------- - -# detect what platform we're building on -$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \ - ./build_detect_platform build_config.mk ./) -# this file is generated by the previous line to set build flags and sources -include build_config.mk - -TESTS = \ - db/autocompact_test \ - db/c_test \ - db/corruption_test \ - db/db_test \ - db/dbformat_test \ - db/fault_injection_test \ - db/filename_test \ - db/log_test \ - db/recovery_test \ - db/skiplist_test \ - db/version_edit_test \ - db/version_set_test \ - db/write_batch_test \ - helpers/memenv/memenv_test \ - issues/issue178_test \ - issues/issue200_test \ - table/filter_block_test \ - table/table_test \ - util/arena_test \ - util/bloom_test \ - util/cache_test \ - util/coding_test \ - util/crc32c_test \ - util/env_posix_test \ - util/env_test \ - util/hash_test - -UTILS = \ - db/db_bench \ - db/leveldbutil - -# Put the object files in a subdirectory, but the application at the top of the object dir. -PROGNAMES := $(notdir $(TESTS) $(UTILS)) - -# On Linux may need libkyotocabinet-dev for dependency. -BENCHMARKS = \ - doc/bench/db_bench_sqlite3 \ - doc/bench/db_bench_tree_db - -CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) -CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) - -LDFLAGS += $(PLATFORM_LDFLAGS) -LIBS += $(PLATFORM_LIBS) - -SIMULATOR_OUTDIR=out-ios-x86 -DEVICE_OUTDIR=out-ios-arm - -ifeq ($(PLATFORM), IOS) -# Note: iOS should probably be using libtool, not ar. -AR=xcrun ar -SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path) -DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path) -DEVICE_CFLAGS = -isysroot "$(DEVICESDK)" -arch armv6 -arch armv7 -arch armv7s -arch arm64 -SIMULATOR_CFLAGS = -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 -STATIC_OUTDIR=out-ios-universal -else -STATIC_OUTDIR=out-static -SHARED_OUTDIR=out-shared -STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES)) -SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench c_test) -endif - -STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o)) -STATIC_MEMENVOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) - -DEVICE_LIBOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(SOURCES:.cc=.o)) -DEVICE_MEMENVOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) - -SIMULATOR_LIBOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(SOURCES:.cc=.o)) -SIMULATOR_MEMENVOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) - -SHARED_LIBOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(SOURCES:.cc=.o)) -SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) - -TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o -TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL) -TEST_STATIC_OBJS := $(STATIC_OUTDIR)/port/port_posix.o $(STATIC_OUTDIR)/util/crc32c.o $(STATIC_OUTDIR)/util/histogram.o - -STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS))) -STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS))) -STATIC_ALLOBJS := $(STATIC_LIBOBJECTS) $(STATIC_MEMENVOBJECTS) $(STATIC_TESTOBJS) $(STATIC_UTILOBJS) $(TESTHARNESS) -DEVICE_ALLOBJS := $(DEVICE_LIBOBJECTS) $(DEVICE_MEMENVOBJECTS) -SIMULATOR_ALLOBJS := $(SIMULATOR_LIBOBJECTS) $(SIMULATOR_MEMENVOBJECTS) - -default: all - -# Should we build shared libraries? -ifneq ($(PLATFORM_SHARED_EXT),) - -# Many leveldb test apps use non-exported API's. Only build a subset for testing. -SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS) -SHARED_CXXFLAGS += -DLEVELDB_SHARED_LIBRARY -SHARED_BUILD_CXXFLAGS += $(SHARED_CXXFLAGS) -DLEVELDB_COMPILE_LIBRARY - -ifneq ($(PLATFORM_SHARED_VERSIONED),true) -SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED_LIB2 = $(SHARED_LIB1) -SHARED_LIB3 = $(SHARED_LIB1) -SHARED_LIBS = $(SHARED_LIB1) -SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a -else -# Update db.h if you change these. -SHARED_VERSION_MAJOR = 1 -SHARED_VERSION_MINOR = 20 -SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR) -SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR) -SHARED_LIBS = $(SHARED_OUTDIR)/$(SHARED_LIB1) $(SHARED_OUTDIR)/$(SHARED_LIB2) $(SHARED_OUTDIR)/$(SHARED_LIB3) -$(SHARED_OUTDIR)/$(SHARED_LIB1): $(SHARED_OUTDIR)/$(SHARED_LIB3) - ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB1) -$(SHARED_OUTDIR)/$(SHARED_LIB2): $(SHARED_OUTDIR)/$(SHARED_LIB3) - ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB2) -SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a -endif - -$(SHARED_OUTDIR)/$(SHARED_LIB3): $(SHARED_LIBOBJECTS) - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED_LIB2) $(SHARED_LIBOBJECTS) -o $(SHARED_OUTDIR)/$(SHARED_LIB3) $(LIBS) - -endif # PLATFORM_SHARED_EXT - -all: $(SHARED_LIBS) $(SHARED_PROGRAMS) $(STATIC_OUTDIR)/libleveldb.a $(STATIC_OUTDIR)/libmemenv.a $(STATIC_PROGRAMS) - -check: $(STATIC_PROGRAMS) - for t in $(notdir $(TESTS)); do echo "***** Running $$t"; $(STATIC_OUTDIR)/$$t || exit 1; done - -clean: - -rm -rf out-static out-shared out-ios-x86 out-ios-arm out-ios-universal - -rm -f build_config.mk - -rm -rf ios-x86 ios-arm - -$(STATIC_OUTDIR): - mkdir $@ - -$(STATIC_OUTDIR)/db: | $(STATIC_OUTDIR) - mkdir $@ - -$(STATIC_OUTDIR)/helpers/memenv: | $(STATIC_OUTDIR) - mkdir -p $@ - -$(STATIC_OUTDIR)/port: | $(STATIC_OUTDIR) - mkdir $@ - -$(STATIC_OUTDIR)/table: | $(STATIC_OUTDIR) - mkdir $@ - -$(STATIC_OUTDIR)/util: | $(STATIC_OUTDIR) - mkdir $@ - -.PHONY: STATIC_OBJDIRS -STATIC_OBJDIRS: \ - $(STATIC_OUTDIR)/db \ - $(STATIC_OUTDIR)/port \ - $(STATIC_OUTDIR)/table \ - $(STATIC_OUTDIR)/util \ - $(STATIC_OUTDIR)/helpers/memenv - -$(SHARED_OUTDIR): - mkdir $@ - -$(SHARED_OUTDIR)/db: | $(SHARED_OUTDIR) - mkdir $@ - -$(SHARED_OUTDIR)/helpers/memenv: | $(SHARED_OUTDIR) - mkdir -p $@ - -$(SHARED_OUTDIR)/port: | $(SHARED_OUTDIR) - mkdir $@ - -$(SHARED_OUTDIR)/table: | $(SHARED_OUTDIR) - mkdir $@ - -$(SHARED_OUTDIR)/util: | $(SHARED_OUTDIR) - mkdir $@ - -.PHONY: SHARED_OBJDIRS -SHARED_OBJDIRS: \ - $(SHARED_OUTDIR)/db \ - $(SHARED_OUTDIR)/port \ - $(SHARED_OUTDIR)/table \ - $(SHARED_OUTDIR)/util \ - $(SHARED_OUTDIR)/helpers/memenv - -$(DEVICE_OUTDIR): - mkdir $@ - -$(DEVICE_OUTDIR)/db: | $(DEVICE_OUTDIR) - mkdir $@ - -$(DEVICE_OUTDIR)/helpers/memenv: | $(DEVICE_OUTDIR) - mkdir -p $@ - -$(DEVICE_OUTDIR)/port: | $(DEVICE_OUTDIR) - mkdir $@ - -$(DEVICE_OUTDIR)/table: | $(DEVICE_OUTDIR) - mkdir $@ - -$(DEVICE_OUTDIR)/util: | $(DEVICE_OUTDIR) - mkdir $@ - -.PHONY: DEVICE_OBJDIRS -DEVICE_OBJDIRS: \ - $(DEVICE_OUTDIR)/db \ - $(DEVICE_OUTDIR)/port \ - $(DEVICE_OUTDIR)/table \ - $(DEVICE_OUTDIR)/util \ - $(DEVICE_OUTDIR)/helpers/memenv - -$(SIMULATOR_OUTDIR): - mkdir $@ - -$(SIMULATOR_OUTDIR)/db: | $(SIMULATOR_OUTDIR) - mkdir $@ - -$(SIMULATOR_OUTDIR)/helpers/memenv: | $(SIMULATOR_OUTDIR) - mkdir -p $@ - -$(SIMULATOR_OUTDIR)/port: | $(SIMULATOR_OUTDIR) - mkdir $@ - -$(SIMULATOR_OUTDIR)/table: | $(SIMULATOR_OUTDIR) - mkdir $@ - -$(SIMULATOR_OUTDIR)/util: | $(SIMULATOR_OUTDIR) - mkdir $@ - -.PHONY: SIMULATOR_OBJDIRS -SIMULATOR_OBJDIRS: \ - $(SIMULATOR_OUTDIR)/db \ - $(SIMULATOR_OUTDIR)/port \ - $(SIMULATOR_OUTDIR)/table \ - $(SIMULATOR_OUTDIR)/util \ - $(SIMULATOR_OUTDIR)/helpers/memenv - -$(STATIC_ALLOBJS): | STATIC_OBJDIRS -$(DEVICE_ALLOBJS): | DEVICE_OBJDIRS -$(SIMULATOR_ALLOBJS): | SIMULATOR_OBJDIRS -$(SHARED_ALLOBJS): | SHARED_OBJDIRS - -ifeq ($(PLATFORM), IOS) -$(DEVICE_OUTDIR)/libleveldb.a: $(DEVICE_LIBOBJECTS) - rm -f $@ - $(AR) -rs $@ $(DEVICE_LIBOBJECTS) - -$(SIMULATOR_OUTDIR)/libleveldb.a: $(SIMULATOR_LIBOBJECTS) - rm -f $@ - $(AR) -rs $@ $(SIMULATOR_LIBOBJECTS) - -$(DEVICE_OUTDIR)/libmemenv.a: $(DEVICE_MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(DEVICE_MEMENVOBJECTS) - -$(SIMULATOR_OUTDIR)/libmemenv.a: $(SIMULATOR_MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(SIMULATOR_MEMENVOBJECTS) - -# For iOS, create universal object libraries to be used on both the simulator and -# a device. -$(STATIC_OUTDIR)/libleveldb.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a - lipo -create $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a -output $@ - -$(STATIC_OUTDIR)/libmemenv.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a - lipo -create $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a -output $@ -else -$(STATIC_OUTDIR)/libleveldb.a:$(STATIC_LIBOBJECTS) - rm -f $@ - $(AR) -rs $@ $(STATIC_LIBOBJECTS) - -$(STATIC_OUTDIR)/libmemenv.a:$(STATIC_MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(STATIC_MEMENVOBJECTS) -endif - -$(SHARED_MEMENVLIB):$(SHARED_MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(SHARED_MEMENVOBJECTS) - -$(STATIC_OUTDIR)/db_bench:db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/db_bench_sqlite3:doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) - -$(STATIC_OUTDIR)/db_bench_tree_db:doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) - -$(STATIC_OUTDIR)/leveldbutil:db/leveldbutil.cc $(STATIC_LIBOBJECTS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/leveldbutil.cc $(STATIC_LIBOBJECTS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/arena_test:util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/autocompact_test:db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/bloom_test:util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/c_test:$(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/cache_test:util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/coding_test:util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/corruption_test:db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/crc32c_test:util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/env_posix_test:util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/fault_injection_test:db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/filename_test:db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/filter_block_test:table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/hash_test:util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/issue178_test:issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/issue200_test:issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/log_test:db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/recovery_test:db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/table_test:table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/skiplist_test:db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/version_edit_test:db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/version_set_test:db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) $(CXXFLAGS) db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) - $(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS) - -$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL) $(TEST_STATIC_OBJS) - $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_CXXFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(TEST_STATIC_OBJS) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) - -$(SHARED_OUTDIR)/c_test:$(SHARED_OUTDIR)/db/c_test.o $(SHARED_LIBS) $(TESTUTIL) $(TEST_STATIC_OBJS) - $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_CXXFLAGS) $(SHARED_OUTDIR)/db/c_test.o $(TESTUTIL) $(TEST_STATIC_OBJS) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) - -.PHONY: run-shared-db_bench -run-shared-db_bench: $(SHARED_OUTDIR)/db_bench - LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench - -.PHONY: run-shared-c_test -run-shared-c_test: $(SHARED_OUTDIR)/c_test - LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/c_test - -$(SIMULATOR_OUTDIR)/%.o: %.cc - xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@ - -$(DEVICE_OUTDIR)/%.o: %.cc - xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) $(DEVICE_CFLAGS) -c $< -o $@ - -$(SIMULATOR_OUTDIR)/%.o: %.c - xcrun -sdk iphonesimulator $(CC) $(CFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@ - -$(DEVICE_OUTDIR)/%.o: %.c - xcrun -sdk iphoneos $(CC) $(CFLAGS) $(DEVICE_CFLAGS) -c $< -o $@ - -$(STATIC_OUTDIR)/%.o: %.cc - $(CXX) $(CXXFLAGS) -c $< -o $@ - -$(STATIC_OUTDIR)/%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -$(SHARED_OUTDIR)/%.o: %.cc - $(CXX) $(CXXFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ - -$(SHARED_OUTDIR)/%.o: %.c - $(CC) $(CFLAGS) $(SHARED_BUILD_CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/build_detect_platform b/build_detect_platform deleted file mode 100755 index 82d73a6359..0000000000 --- a/build_detect_platform +++ /dev/null @@ -1,262 +0,0 @@ -#!/bin/sh -# -# Detects OS we're compiling on and outputs a file specified by the first -# argument, which in turn gets read while processing Makefile. -# -# The output will set the following variables: -# CC C Compiler path -# CXX C++ Compiler path -# PLATFORM_LDFLAGS Linker flags -# PLATFORM_LIBS Libraries flags -# PLATFORM_SHARED_EXT Extension for shared libraries -# PLATFORM_SHARED_LDFLAGS Flags for building shared library -# This flag is embedded just before the name -# of the shared library without intervening spaces -# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library -# PLATFORM_CCFLAGS C compiler flags -# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: -# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned -# shared libraries, empty otherwise. -# -# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: -# -# -DLEVELDB_ATOMIC_PRESENT if is present -# -DLEVELDB_PLATFORM_POSIX=1 for Posix-based platforms -# -DHAVE_CRC32C=1 if the CRC32C library is present -# -DHAVE_SNAPPY=1 if the Snappy library is present -# - -OUTPUT=$1 -PREFIX=$2 -if test -z "$OUTPUT" || test -z "$PREFIX"; then - echo "usage: $0 " >&2 - exit 1 -fi - -# Delete existing output, if it exists -rm -f $OUTPUT -touch $OUTPUT - -if test -z "$CC"; then - CC=cc -fi - -if test -z "$CXX"; then - CXX=g++ -fi - -if test -z "$TMPDIR"; then - TMPDIR=/tmp -fi - -# Detect OS -if test -z "$TARGET_OS"; then - TARGET_OS=`uname -s` -fi - -COMMON_FLAGS= -CROSS_COMPILE= -PLATFORM_CCFLAGS= -PLATFORM_CXXFLAGS= -PLATFORM_LDFLAGS= -PLATFORM_LIBS= -PLATFORM_SHARED_EXT="so" -PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," -PLATFORM_SHARED_CFLAGS="-fPIC -fvisibility=hidden" -PLATFORM_SHARED_VERSIONED=true - -MEMCMP_FLAG= -if [ "$CXX" = "g++" ]; then - # Use libc's memcmp instead of GCC's memcmp. This results in ~40% - # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. - MEMCMP_FLAG="-fno-builtin-memcmp" -fi - -case "$TARGET_OS" in - CYGWIN_*) - PLATFORM=OS_LINUX - COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" - PLATFORM_LDFLAGS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - Darwin) - PLATFORM=OS_MACOSX - COMMON_FLAGS="$MEMCMP_FLAG" - PLATFORM_SHARED_EXT=dylib - [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` - PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" - PORT_FILE=port/port_posix.cc - ;; - Linux) - PLATFORM=OS_LINUX - COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - ;; - SunOS) - PLATFORM=OS_SOLARIS - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" - PLATFORM_LIBS="-lpthread -lrt" - PORT_FILE=port/port_posix.cc - ;; - FreeBSD) - PLATFORM=OS_FREEBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" - PLATFORM_LIBS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - NetBSD) - PLATFORM=OS_NETBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" - PLATFORM_LIBS="-lpthread -lgcc_s" - PORT_FILE=port/port_posix.cc - ;; - OpenBSD) - PLATFORM=OS_OPENBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - ;; - DragonFly) - PLATFORM=OS_DRAGONFLYBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" - PLATFORM_LIBS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - OS_ANDROID_CROSSCOMPILE) - PLATFORM=OS_ANDROID - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX=1" - PLATFORM_LDFLAGS="" # All pthread features are in the Android C library - PORT_FILE=port/port_posix.cc - CROSS_COMPILE=true - ;; - HP-UX) - PLATFORM=OS_HPUX - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - # man ld: +h internal_name - PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," - ;; - IOS) - PLATFORM=IOS - COMMON_FLAGS="$MEMCMP_FLAG" - [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` - PORT_FILE=port/port_posix.cc - PLATFORM_SHARED_EXT= - PLATFORM_SHARED_LDFLAGS= - PLATFORM_SHARED_CFLAGS= - PLATFORM_SHARED_VERSIONED= - ;; - *) - echo "Unknown platform!" >&2 - exit 1 -esac - -# We want to make a list of all cc files within util, db, table, and helpers -# except for the test and benchmark files. By default, find will output a list -# of all files matching either rule, so we need to append -print to make the -# prune take effect. -DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" - -set -f # temporarily disable globbing so that our patterns aren't expanded -PRUNE_TEST="-name *test*.cc -prune" -PRUNE_BENCH="-name *_bench.cc -prune" -PRUNE_TOOL="-name leveldbutil.cc -prune" -PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` - -set +f # re-enable globbing - -# The sources consist of the portable files, plus the platform-specific port -# file. -echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT -echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT - -if [ "$CROSS_COMPILE" = "true" ]; then - # Cross-compiling; do not try any compilation tests. - true -else - CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" - - # If -std=c++0x works, use as fallback for when memory barriers - # are not available. - $CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null < - int main() {} -EOF - if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX=1 -DLEVELDB_ATOMIC_PRESENT" - PLATFORM_CXXFLAGS="-std=c++0x" - else - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX=1" - fi - - # Test whether CRC32C library is installed - # https://github.com/google/crc32c - $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < - int main() {} -EOF - if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DHAVE_CRC32C=1" - PLATFORM_LIBS="$PLATFORM_LIBS -lcrc32c" - else - COMMON_FLAGS="$COMMON_FLAGS -DHAVE_CRC32C=0" - fi - - # Test whether Snappy library is installed - # https://github.com/google/snappy - $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT 2>/dev/null < - int main() {} -EOF - if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SNAPPY=1" - PLATFORM_LIBS="$PLATFORM_LIBS -lsnappy" - else - COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SNAPPY=0" - fi - - # Test whether tcmalloc is available - $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null </dev/null </dev/null -fi - -PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" -PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" - -echo "CC=$CC" >> $OUTPUT -echo "CXX=$CXX" >> $OUTPUT -echo "PLATFORM=$PLATFORM" >> $OUTPUT -echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT -echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT -echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT -echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT -echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT From 74f032ff6f2465160366d865b1bb89a45dc2046b Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 21 Mar 2018 00:47:18 -0700 Subject: [PATCH 083/174] leveldb: Require C++11. This CL switches the public headers to C++11 default and deleted constructors, and adds override to the relevant leveldb::EnvWrapper methods. This should be a good test for C++11 compiler support. Once this CL settles, the rest of the codebase can be safely modernized to C++11. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189873212 --- CMakeLists.txt | 11 ++- include/leveldb/cache.h | 9 ++- include/leveldb/db.h | 11 ++- include/leveldb/env.h | 121 +++++++++++++++++--------------- include/leveldb/filter_policy.h | 3 +- include/leveldb/iterator.h | 8 +-- include/leveldb/slice.h | 6 +- include/leveldb/table.h | 7 +- include/leveldb/table_builder.h | 7 +- include/leveldb/write_batch.h | 7 +- 10 files changed, 101 insertions(+), 89 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73b69a4c79..6f7c36ab16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,16 @@ # found in the LICENSE file. See the AUTHORS file for names of contributors. cmake_minimum_required(VERSION 3.9) -project(Leveldb VERSION 0.1.0 LANGUAGES C CXX) +project(Leveldb VERSION 1.21.0 LANGUAGES C CXX) -# This project can take advantage of C++11. +# This project can use C11, but will gracefully decay down to C89. +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED OFF) +set(CMAKE_C_EXTENSIONS OFF) + +# This project requires C++11. set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON) diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h index ed18baaa50..145ad69acb 100644 --- a/include/leveldb/cache.h +++ b/include/leveldb/cache.h @@ -32,7 +32,10 @@ LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity); class LEVELDB_EXPORT Cache { public: - Cache() { } + Cache() = default; + + Cache(const Cache&) = delete; + Cache& operator=(const Cache&) = delete; // Destroys all existing entries by calling the "deleter" // function that was passed to the constructor. @@ -100,10 +103,6 @@ class LEVELDB_EXPORT Cache { struct Rep; Rep* rep_; - - // No copying allowed - Cache(const Cache&); - void operator=(const Cache&); }; } // namespace leveldb diff --git a/include/leveldb/db.h b/include/leveldb/db.h index e99f45c1e7..092a154705 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -53,7 +53,11 @@ class LEVELDB_EXPORT DB { const std::string& name, DB** dbptr); - DB() { } + DB() = default; + + DB(const DB&) = delete; + DB& operator=(const DB&) = delete; + virtual ~DB(); // Set the database entry for "key" to "value". Returns OK on success, @@ -142,11 +146,6 @@ class LEVELDB_EXPORT DB { // Therefore the following call will compact the entire database: // db->CompactRange(NULL, NULL); virtual void CompactRange(const Slice* begin, const Slice* end) = 0; - - private: - // No copying allowed - DB(const DB&); - void operator=(const DB&); }; // Destroy the contents of the specified database. diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 9d8ebfe038..54c9e3b9e8 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -31,7 +31,11 @@ class WritableFile; class LEVELDB_EXPORT Env { public: - Env() { } + Env() = default; + + Env(const Env&) = delete; + Env& operator=(const Env&) = delete; + virtual ~Env(); // Return a default environment suitable for the current operating @@ -162,17 +166,16 @@ class LEVELDB_EXPORT Env { // Sleep/delay the thread for the prescribed number of micro-seconds. virtual void SleepForMicroseconds(int micros) = 0; - - private: - // No copying allowed - Env(const Env&); - void operator=(const Env&); }; // A file abstraction for reading sequentially through a file class LEVELDB_EXPORT SequentialFile { public: - SequentialFile() { } + SequentialFile() = default; + + SequentialFile(const SequentialFile&) = delete; + SequentialFile& operator=(const SequentialFile&) = delete; + virtual ~SequentialFile(); // Read up to "n" bytes from the file. "scratch[0..n-1]" may be @@ -193,17 +196,16 @@ class LEVELDB_EXPORT SequentialFile { // // REQUIRES: External synchronization virtual Status Skip(uint64_t n) = 0; - - private: - // No copying allowed - SequentialFile(const SequentialFile&); - void operator=(const SequentialFile&); }; // A file abstraction for randomly reading the contents of a file. class LEVELDB_EXPORT RandomAccessFile { public: - RandomAccessFile() { } + RandomAccessFile() = default; + + RandomAccessFile(const RandomAccessFile&) = delete; + RandomAccessFile& operator=(const RandomAccessFile&) = delete; + virtual ~RandomAccessFile(); // Read up to "n" bytes from the file starting at "offset". @@ -217,11 +219,6 @@ class LEVELDB_EXPORT RandomAccessFile { // Safe for concurrent use by multiple threads. virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const = 0; - - private: - // No copying allowed - RandomAccessFile(const RandomAccessFile&); - void operator=(const RandomAccessFile&); }; // A file abstraction for sequential writing. The implementation @@ -229,45 +226,42 @@ class LEVELDB_EXPORT RandomAccessFile { // at a time to the file. class LEVELDB_EXPORT WritableFile { public: - WritableFile() { } + WritableFile() = default; + + WritableFile(const WritableFile&) = delete; + WritableFile& operator=(const WritableFile&) = delete; + virtual ~WritableFile(); virtual Status Append(const Slice& data) = 0; virtual Status Close() = 0; virtual Status Flush() = 0; virtual Status Sync() = 0; - - private: - // No copying allowed - WritableFile(const WritableFile&); - void operator=(const WritableFile&); }; // An interface for writing log messages. class LEVELDB_EXPORT Logger { public: - Logger() { } + Logger() = default; + + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + virtual ~Logger(); // Write an entry to the log file with the specified format. virtual void Logv(const char* format, va_list ap) = 0; - - private: - // No copying allowed - Logger(const Logger&); - void operator=(const Logger&); }; - // Identifies a locked file. class LEVELDB_EXPORT FileLock { public: - FileLock() { } + FileLock() = default; + + FileLock(const FileLock&) = delete; + FileLock& operator=(const FileLock&) = delete; + virtual ~FileLock(); - private: - // No copying allowed - FileLock(const FileLock&); - void operator=(const FileLock&); }; // Log the specified data to *info_log if info_log is non-NULL. @@ -290,61 +284,72 @@ LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname, // functionality of another Env. class LEVELDB_EXPORT EnvWrapper : public Env { public: - // Initialize an EnvWrapper that delegates all calls to *t + // Initialize an EnvWrapper that delegates all calls to *t. explicit EnvWrapper(Env* t) : target_(t) { } virtual ~EnvWrapper(); - // Return the target to which this Env forwards all calls + // Return the target to which this Env forwards all calls. Env* target() const { return target_; } - // The following text is boilerplate that forwards all methods to target() - Status NewSequentialFile(const std::string& f, SequentialFile** r) { + // The following text is boilerplate that forwards all methods to target(). + Status NewSequentialFile(const std::string& f, SequentialFile** r) override { return target_->NewSequentialFile(f, r); } - Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + Status NewRandomAccessFile(const std::string& f, + RandomAccessFile** r) override { return target_->NewRandomAccessFile(f, r); } - Status NewWritableFile(const std::string& f, WritableFile** r) { + Status NewWritableFile(const std::string& f, WritableFile** r) override { return target_->NewWritableFile(f, r); } - Status NewAppendableFile(const std::string& f, WritableFile** r) { + Status NewAppendableFile(const std::string& f, WritableFile** r) override { return target_->NewAppendableFile(f, r); } - bool FileExists(const std::string& f) { return target_->FileExists(f); } - Status GetChildren(const std::string& dir, std::vector* r) { + bool FileExists(const std::string& f) override { + return target_->FileExists(f); + } + Status GetChildren(const std::string& dir, + std::vector* r) override { return target_->GetChildren(dir, r); } - Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } - Status CreateDir(const std::string& d) { return target_->CreateDir(d); } - Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } - Status GetFileSize(const std::string& f, uint64_t* s) { + Status DeleteFile(const std::string& f) override { + return target_->DeleteFile(f); + } + Status CreateDir(const std::string& d) override { + return target_->CreateDir(d); + } + Status DeleteDir(const std::string& d) override { + return target_->DeleteDir(d); + } + Status GetFileSize(const std::string& f, uint64_t* s) override { return target_->GetFileSize(f, s); } - Status RenameFile(const std::string& s, const std::string& t) { + Status RenameFile(const std::string& s, const std::string& t) override { return target_->RenameFile(s, t); } - Status LockFile(const std::string& f, FileLock** l) { + Status LockFile(const std::string& f, FileLock** l) override { return target_->LockFile(f, l); } - Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } - void Schedule(void (*f)(void*), void* a) { + Status UnlockFile(FileLock* l) override { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) override { return target_->Schedule(f, a); } - void StartThread(void (*f)(void*), void* a) { + void StartThread(void (*f)(void*), void* a) override { return target_->StartThread(f, a); } - virtual Status GetTestDirectory(std::string* path) { + Status GetTestDirectory(std::string* path) override { return target_->GetTestDirectory(path); } - virtual Status NewLogger(const std::string& fname, Logger** result) { + Status NewLogger(const std::string& fname, Logger** result) override { return target_->NewLogger(fname, result); } - uint64_t NowMicros() { + uint64_t NowMicros() override { return target_->NowMicros(); } - void SleepForMicroseconds(int micros) { + void SleepForMicroseconds(int micros) override { target_->SleepForMicroseconds(micros); } + private: Env* target_; }; diff --git a/include/leveldb/filter_policy.h b/include/leveldb/filter_policy.h index c3a5d208a6..ba027202a1 100644 --- a/include/leveldb/filter_policy.h +++ b/include/leveldb/filter_policy.h @@ -65,6 +65,7 @@ class LEVELDB_EXPORT FilterPolicy { // FilterPolicy (like NewBloomFilterPolicy) that does not ignore // trailing spaces in keys. LEVELDB_EXPORT const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); -} + +} // namespace leveldb #endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/include/leveldb/iterator.h b/include/leveldb/iterator.h index ff3b56629e..436508adcb 100644 --- a/include/leveldb/iterator.h +++ b/include/leveldb/iterator.h @@ -24,6 +24,10 @@ namespace leveldb { class LEVELDB_EXPORT Iterator { public: Iterator(); + + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + virtual ~Iterator(); // An iterator is either positioned at a key/value pair, or @@ -84,10 +88,6 @@ class LEVELDB_EXPORT Iterator { Cleanup* next; }; Cleanup cleanup_; - - // No copying allowed - Iterator(const Iterator&); - void operator=(const Iterator&); }; // Return an empty iterator (yields nothing). diff --git a/include/leveldb/slice.h b/include/leveldb/slice.h index b56a4e4f35..a86e8a6476 100644 --- a/include/leveldb/slice.h +++ b/include/leveldb/slice.h @@ -37,6 +37,10 @@ class LEVELDB_EXPORT Slice { // Create a slice that refers to s[0,strlen(s)-1] Slice(const char* s) : data_(s), size_(strlen(s)) { } + // Intentionally copyable. + Slice(const Slice&) = default; + Slice& operator=(const Slice&) = default; + // Return a pointer to the beginning of the referenced data const char* data() const { return data_; } @@ -81,8 +85,6 @@ class LEVELDB_EXPORT Slice { private: const char* data_; size_t size_; - - // Intentionally copyable }; inline bool operator==(const Slice& x, const Slice& y) { diff --git a/include/leveldb/table.h b/include/leveldb/table.h index 2f54c4ea3c..83078d2757 100644 --- a/include/leveldb/table.h +++ b/include/leveldb/table.h @@ -41,6 +41,9 @@ class LEVELDB_EXPORT Table { uint64_t file_size, Table** table); + Table(const Table&) = delete; + void operator=(const Table&) = delete; + ~Table(); // Returns a new iterator over the table contents. @@ -75,10 +78,6 @@ class LEVELDB_EXPORT Table { void ReadMeta(const Footer& footer); void ReadFilter(const Slice& filter_handle_value); - - // No copying allowed - Table(const Table&); - void operator=(const Table&); }; } // namespace leveldb diff --git a/include/leveldb/table_builder.h b/include/leveldb/table_builder.h index 3c3acc7327..8d05d3328e 100644 --- a/include/leveldb/table_builder.h +++ b/include/leveldb/table_builder.h @@ -31,6 +31,9 @@ class LEVELDB_EXPORT TableBuilder { // caller to close the file after calling Finish(). TableBuilder(const Options& options, WritableFile* file); + TableBuilder(const TableBuilder&) = delete; + void operator=(const TableBuilder&) = delete; + // REQUIRES: Either Finish() or Abandon() has been called. ~TableBuilder(); @@ -82,10 +85,6 @@ class LEVELDB_EXPORT TableBuilder { struct Rep; Rep* rep_; - - // No copying allowed - TableBuilder(const TableBuilder&); - void operator=(const TableBuilder&); }; } // namespace leveldb diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 40ceb03210..b6d72cb004 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -32,6 +32,11 @@ class Slice; class LEVELDB_EXPORT WriteBatch { public: WriteBatch(); + + // Intentionally copyable. + WriteBatch(const WriteBatch&) = default; + WriteBatch& operator =(const WriteBatch&) = default; + ~WriteBatch(); // Store the mapping "key->value" in the database. @@ -62,8 +67,6 @@ class LEVELDB_EXPORT WriteBatch { friend class WriteBatchInternal; std::string rep_; // See comment in write_batch.cc for the format of rep_ - - // Intentionally copyable }; } // namespace leveldb From 04f39105c5a418905da8b7657ca244d672c99d3b Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 21 Mar 2018 09:26:49 -0700 Subject: [PATCH 084/174] Take for granted in port/atomic_pointer.h. C++11 requires . This lets us remove the header detection (LEVELDB_ATOMIC_PRESENT) and simplify port/atomic_pointer.h. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189919098 --- CMakeLists.txt | 3 -- port/atomic_pointer.h | 80 +++---------------------------------------- port/port_config.h.in | 7 ---- 3 files changed, 5 insertions(+), 85 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f7c36ab16..ca43d7ee31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,6 @@ test_big_endian(LEVELDB_IS_BIG_ENDIAN) include(CheckIncludeFile) check_include_file("unistd.h" HAVE_UNISTD_H) -include(CheckIncludeFileCXX) -check_include_file_cxx("atomic" LEVELDB_ATOMIC_PRESENT) - include(CheckLibraryExists) check_library_exists(crc32c crc32c_value "" HAVE_CRC32C) check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index 43dab2ab3d..bb4e183c08 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -19,11 +19,9 @@ #define PORT_ATOMIC_POINTER_H_ #include -#ifdef LEVELDB_ATOMIC_PRESENT + #include -#elif defined(__APPLE__) -#include -#endif + #ifdef OS_WIN #include #endif @@ -55,11 +53,7 @@ namespace port { // Mac OS #elif defined(__APPLE__) inline void MemoryBarrier() { -#if defined(LEVELDB_ATOMIC_PRESENT) std::atomic_thread_fence(std::memory_order_seq_cst); -#else - OSMemoryBarrier(); -#endif // defined(LEVELDB_ATOMIC_PRESENT) } #define LEVELDB_HAVE_MEMORY_BARRIER @@ -124,7 +118,7 @@ inline void MemoryBarrier() { #endif -// AtomicPointer built using platform-specific MemoryBarrier() +// AtomicPointer built using platform-specific MemoryBarrier(). #if defined(LEVELDB_HAVE_MEMORY_BARRIER) class AtomicPointer { private: @@ -145,8 +139,8 @@ class AtomicPointer { } }; -// AtomicPointer based on -#elif defined(LEVELDB_ATOMIC_PRESENT) +// AtomicPointer based on C++11 . +#else class AtomicPointer { private: std::atomic rep_; @@ -167,70 +161,6 @@ class AtomicPointer { } }; -// Atomic pointer based on sparc memory barriers -#elif defined(__sparcv9) && defined(__GNUC__) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val; - __asm__ __volatile__ ( - "ldx [%[rep_]], %[val] \n\t" - "membar #LoadLoad|#LoadStore \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory"); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "membar #LoadStore|#StoreStore \n\t" - "stx %[v], [%[rep_]] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory"); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } -}; - -// Atomic pointer based on ia64 acq/rel -#elif defined(__ia64) && defined(__GNUC__) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val ; - __asm__ __volatile__ ( - "ld8.acq %[val] = [%[rep_]] \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory" - ); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "st8.rel [%[rep_]] = %[v] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory" - ); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } -}; - -// We have neither MemoryBarrier(), nor -#else -#error Please implement AtomicPointer for this platform. - #endif #undef LEVELDB_HAVE_MEMORY_BARRIER diff --git a/port/port_config.h.in b/port/port_config.h.in index 549f179077..19340554a2 100644 --- a/port/port_config.h.in +++ b/port/port_config.h.in @@ -26,11 +26,4 @@ #cmakedefine01 LEVELDB_IS_BIG_ENDIAN #endif // !defined(LEVELDB_IS_BIG_ENDIAN) -// Define to 1 if you have the header. -// NOTE: was standardized in C++11, which will be required to build -// LevelDB soon. -#if !defined(LEVELDB_ATOMIC_PRESENT) -#cmakedefine01 LEVELDB_ATOMIC_PRESENT -#endif // !defined(LEVELDB_ATOMIC_PRESENT) - #endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ \ No newline at end of file From 0db30413a4cfa8c980e675ba5cb96717d688af92 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 23 Mar 2018 12:50:14 -0700 Subject: [PATCH 085/174] leveldb: Add more thread safety annotations. After this CL, all classes with Mutex members should be covered by annotations. Exceptions are atomic members, which shouldn't need locking, and DBImpl members that cause errors when annotated, which will be tackled separately. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190260865 --- db/db_bench.cc | 19 ++++++++----------- db/db_impl.cc | 21 +++++++++++---------- db/db_test.cc | 10 ++++++---- db/fault_injection_test.cc | 26 +++++++++++++++++--------- db/skiplist_test.cc | 10 ++++++---- util/env_posix.cc | 14 +++++++------- util/env_test.cc | 18 +++++++++--------- 7 files changed, 64 insertions(+), 54 deletions(-) diff --git a/db/db_bench.cc b/db/db_bench.cc index 920f119757..701b128321 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -282,8 +282,8 @@ class Stats { // State shared by all concurrent executions of the same benchmark. struct SharedState { port::Mutex mu; - port::CondVar cv; - int total; + port::CondVar cv GUARDED_BY(mu); + int total GUARDED_BY(mu); // Each thread goes through the following states: // (1) initializing @@ -291,11 +291,12 @@ struct SharedState { // (3) running // (4) done - int num_initialized; - int num_done; - bool start; + int num_initialized GUARDED_BY(mu); + int num_done GUARDED_BY(mu); + bool start GUARDED_BY(mu); - SharedState() : cv(&mu) { } + SharedState(int total) + : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) { } }; // Per-thread state for concurrent executions of the same benchmark. @@ -584,11 +585,7 @@ class Benchmark { void RunBenchmark(int n, Slice name, void (Benchmark::*method)(ThreadState*)) { - SharedState shared; - shared.total = n; - shared.num_initialized = 0; - shared.num_done = 0; - shared.start = false; + SharedState shared(n); ThreadArg* arg = new ThreadArg[n]; for (int i = 0; i < n; i++) { diff --git a/db/db_impl.cc b/db/db_impl.cc index 8484e46ea7..cd8792ecf2 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -1053,11 +1053,15 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { } namespace { + struct IterState { - port::Mutex* mu; - Version* version; - MemTable* mem; - MemTable* imm; + port::Mutex* const mu; + Version* const version GUARDED_BY(mu); + MemTable* const mem GUARDED_BY(mu); + MemTable* const imm GUARDED_BY(mu); + + IterState(port::Mutex* mutex, MemTable* mem, MemTable* imm, Version* version) + : mu(mutex), version(version), mem(mem), imm(imm) { } }; static void CleanupIteratorState(void* arg1, void* arg2) { @@ -1069,12 +1073,12 @@ static void CleanupIteratorState(void* arg1, void* arg2) { state->mu->Unlock(); delete state; } -} // namespace + +} // anonymous namespace Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, SequenceNumber* latest_snapshot, uint32_t* seed) { - IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); @@ -1091,10 +1095,7 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, NewMergingIterator(&internal_comparator_, &list[0], list.size()); versions_->current()->Ref(); - cleanup->mu = &mutex_; - cleanup->mem = mem_; - cleanup->imm = imm_; - cleanup->version = versions_->current(); + IterState* cleanup = new IterState(&mutex_, mem_, imm_, versions_->current()); internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); *seed = ++seed_; diff --git a/db/db_test.cc b/db/db_test.cc index c818113313..b1d2cd84da 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -11,6 +11,8 @@ #include "leveldb/cache.h" #include "leveldb/env.h" #include "leveldb/table.h" +#include "port/port.h" +#include "port/thread_annotations.h" #include "util/hash.h" #include "util/logging.h" #include "util/mutexlock.h" @@ -36,21 +38,21 @@ namespace { class AtomicCounter { private: port::Mutex mu_; - int count_; + int count_ GUARDED_BY(mu_); public: AtomicCounter() : count_(0) { } void Increment() { IncrementBy(1); } - void IncrementBy(int count) { + void IncrementBy(int count) LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); count_ += count; } - int Read() { + int Read() LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); return count_; } - void Reset() { + void Reset() LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); count_ = 0; } diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index f8b24405a1..caead37e13 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -6,10 +6,10 @@ // the last "sync". It then checks for data loss errors by purposely dropping // file data (or entire files) not protected by a "sync". -#include "leveldb/db.h" - #include #include + +#include "leveldb/db.h" #include "db/db_impl.h" #include "db/filename.h" #include "db/log_format.h" @@ -18,6 +18,8 @@ #include "leveldb/env.h" #include "leveldb/table.h" #include "leveldb/write_batch.h" +#include "port/port.h" +#include "port/thread_annotations.h" #include "util/logging.h" #include "util/mutexlock.h" #include "util/testharness.h" @@ -126,7 +128,8 @@ class TestWritableFile : public WritableFile { class FaultInjectionTestEnv : public EnvWrapper { public: - FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {} + FaultInjectionTestEnv() + : EnvWrapper(Env::Default()), filesystem_active_(true) {} virtual ~FaultInjectionTestEnv() { } virtual Status NewWritableFile(const std::string& fname, WritableFile** result); @@ -146,14 +149,20 @@ class FaultInjectionTestEnv : public EnvWrapper { // system reset. Setting to inactive will freeze our saved filesystem state so // that it will stop being recorded. It can then be reset back to the state at // the time of the reset. - bool IsFilesystemActive() const { return filesystem_active_; } - void SetFilesystemActive(bool active) { filesystem_active_ = active; } + bool IsFilesystemActive() LOCKS_EXCLUDED(mutex_) { + MutexLock l(&mutex_); + return filesystem_active_; + } + void SetFilesystemActive(bool active) LOCKS_EXCLUDED(mutex_) { + MutexLock l(&mutex_); + filesystem_active_ = active; + } private: port::Mutex mutex_; - std::map db_file_state_; - std::set new_files_since_last_dir_sync_; - bool filesystem_active_; // Record flushes, syncs, writes + std::map db_file_state_ GUARDED_BY(mutex_); + std::set new_files_since_last_dir_sync_ GUARDED_BY(mutex_); + bool filesystem_active_ GUARDED_BY(mutex_); // Record flushes, syncs, writes }; TestWritableFile::TestWritableFile(const FileState& state, @@ -328,7 +337,6 @@ void FaultInjectionTestEnv::ResetState() { // Since we are not destroying the database, the existing files // should keep their recorded synced/flushed state. Therefore // we do not reset db_file_state_ and new_files_since_last_dir_sync_. - MutexLock l(&mutex_); SetFilesystemActive(true); } diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index aee1461e1b..90f9d0ea74 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -5,6 +5,8 @@ #include "db/skiplist.h" #include #include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" #include "util/arena.h" #include "util/hash.h" #include "util/random.h" @@ -312,7 +314,7 @@ class TestState { state_(STARTING), state_cv_(&mu_) {} - void Wait(ReaderState s) { + void Wait(ReaderState s) LOCKS_EXCLUDED(mu_) { mu_.Lock(); while (state_ != s) { state_cv_.Wait(); @@ -320,7 +322,7 @@ class TestState { mu_.Unlock(); } - void Change(ReaderState s) { + void Change(ReaderState s) LOCKS_EXCLUDED(mu_) { mu_.Lock(); state_ = s; state_cv_.Signal(); @@ -329,8 +331,8 @@ class TestState { private: port::Mutex mu_; - ReaderState state_; - port::CondVar state_cv_; + ReaderState state_ GUARDED_BY(mu_); + port::CondVar state_cv_ GUARDED_BY(mu_); }; static void ConcurrentReader(void* arg) { diff --git a/util/env_posix.cc b/util/env_posix.cc index 8327978402..4bfaf6ccde 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -22,6 +22,7 @@ #include "leveldb/env.h" #include "leveldb/slice.h" #include "port/port.h" +#include "port/thread_annotations.h" #include "util/logging.h" #include "util/mutexlock.h" #include "util/posix_logger.h" @@ -57,7 +58,7 @@ class Limiter { // If another resource is available, acquire it and return true. // Else return false. - bool Acquire() { + bool Acquire() LOCKS_EXCLUDED(mu_) { if (GetAllowed() <= 0) { return false; } @@ -73,7 +74,7 @@ class Limiter { // Release a resource acquired by a previous call to Acquire() that returned // true. - void Release() { + void Release() LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); SetAllowed(GetAllowed() + 1); } @@ -86,8 +87,7 @@ class Limiter { return reinterpret_cast(allowed_.Acquire_Load()); } - // REQUIRES: mu_ must be held - void SetAllowed(intptr_t v) { + void SetAllowed(intptr_t v) EXCLUSIVE_LOCKS_REQUIRED(mu_) { allowed_.Release_Store(reinterpret_cast(v)); } @@ -365,13 +365,13 @@ class PosixFileLock : public FileLock { class PosixLockTable { private: port::Mutex mu_; - std::set locked_files_; + std::set locked_files_ GUARDED_BY(mu_); public: - bool Insert(const std::string& fname) { + bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); return locked_files_.insert(fname).second; } - void Remove(const std::string& fname) { + void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); locked_files_.erase(fname); } diff --git a/util/env_test.cc b/util/env_test.cc index 0bf71211e6..fd89b4c8f2 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -7,6 +7,8 @@ #include #include "port/port.h" +#include "port/thread_annotations.h" +#include "util/mutexlock.h" #include "util/testharness.h" #include "util/testutil.h" @@ -17,10 +19,6 @@ static const int kReadOnlyFileLimit = 4; static const int kMMapLimit = 4; class EnvTest { - private: - port::Mutex mu_; - std::string events_; - public: Env* env_; EnvTest() : env_(Env::Default()) { } @@ -119,8 +117,10 @@ TEST(EnvTest, RunMany) { struct State { port::Mutex mu; - int val; - int num_running; + int val GUARDED_BY(mu); + int num_running GUARDED_BY(mu); + + State(int val, int num_running) : val(val), num_running(num_running) { } }; static void ThreadBody(void* arg) { @@ -132,9 +132,7 @@ static void ThreadBody(void* arg) { } TEST(EnvTest, StartThread) { - State state; - state.val = 0; - state.num_running = 3; + State state(0, 3); for (int i = 0; i < 3; i++) { env_->StartThread(&ThreadBody, &state); } @@ -147,6 +145,8 @@ TEST(EnvTest, StartThread) { } env_->SleepForMicroseconds(kDelayMicros); } + + MutexLock l(&state.mu); ASSERT_EQ(state.val, 3); } From 6fa45666703add49f77652b2eadd874d49aedaf6 Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 23 Mar 2018 13:02:10 -0700 Subject: [PATCH 086/174] Rename CMake project / targets from Leveldb to leveldb. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190262375 --- CMakeLists.txt | 18 +++++++++--------- cmake/LeveldbConfig.cmake | 1 - cmake/leveldbConfig.cmake | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 cmake/LeveldbConfig.cmake create mode 100644 cmake/leveldbConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ca43d7ee31..271caa7d58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # found in the LICENSE file. See the AUTHORS file for names of contributors. cmake_minimum_required(VERSION 3.9) -project(Leveldb VERSION 1.21.0 LANGUAGES C CXX) +project(leveldb VERSION 1.21.0 LANGUAGES C CXX) # This project can use C11, but will gracefully decay down to C89. set(CMAKE_C_STANDARD 11) @@ -372,7 +372,7 @@ endif(LEVELDB_BUILD_BENCHMARKS) if(LEVELDB_INSTALL) include(GNUInstallDirs) install(TARGETS leveldb - EXPORT LeveldbTargets + EXPORT leveldbTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -399,18 +399,18 @@ if(LEVELDB_INSTALL) include(CMakePackageConfigHelpers) write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/LeveldbConfigVersion.cmake" + "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake" COMPATIBILITY SameMajorVersion ) install( - EXPORT LeveldbTargets - NAMESPACE Leveldb:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Leveldb" + EXPORT leveldbTargets + NAMESPACE leveldb:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb" ) install( FILES - "${PROJECT_SOURCE_DIR}/cmake/LeveldbConfig.cmake" - "${PROJECT_BINARY_DIR}/LeveldbConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Leveldb" + "${PROJECT_SOURCE_DIR}/cmake/leveldbConfig.cmake" + "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb" ) endif(LEVELDB_INSTALL) diff --git a/cmake/LeveldbConfig.cmake b/cmake/LeveldbConfig.cmake deleted file mode 100644 index 8f3e78b47c..0000000000 --- a/cmake/LeveldbConfig.cmake +++ /dev/null @@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/LeveldbTargets.cmake") diff --git a/cmake/leveldbConfig.cmake b/cmake/leveldbConfig.cmake new file mode 100644 index 0000000000..eea6e5c477 --- /dev/null +++ b/cmake/leveldbConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake") From 260655b4c294991fe03bf6ab8b6d722ccfc41d32 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 9 Apr 2018 16:41:02 -0700 Subject: [PATCH 087/174] Define LEVELDB_HAS_PORT_CONFIG_H for old compilers. The CMake-based build relies on __has_include, which is standardized in C++17. Unfortunately, __has_include is available without requiring --std=c++17 on all the compilers on CI, so this problem was not caught. Fixes https://github.com/google/leveldb/issues/572 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192208842 --- CMakeLists.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 271caa7d58..cc1022f6ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,14 @@ int main() { return 0; } " HAVE_CLANG_THREAD_SAFETY) set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +# Test whether C++17 __has_include is available. +check_cxx_source_compiles(" +#if defined(__has_include) && __has_include() +#include +#endif +int main() { std::string str; return 0; } +" HAVE_CXX17_HAS_INCLUDE) + set(LEVELDB_PUBLIC_INCLUDE_DIR "include/leveldb") set(LEVELDB_PORT_CONFIG_DIR "include/port") @@ -85,6 +93,12 @@ target_sources(leveldb_port_posix $ $ ) +if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions(leveldb_port_posix + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) +endif(NOT HAVE_CXX17_HAS_INCLUDE) if(BUILD_SHARED_LIBS) set_property(TARGET leveldb_port_posix PROPERTY POSITION_INDEPENDENT_CODE ON) endif(BUILD_SHARED_LIBS) @@ -199,6 +213,13 @@ target_compile_definitions(leveldb # Used by port/port.h. LEVELDB_PLATFORM_POSIX=1 ) +if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions(leveldb + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) +endif(NOT HAVE_CXX17_HAS_INCLUDE) + if(BUILD_SHARED_LIBS) target_compile_definitions(leveldb PUBLIC @@ -263,6 +284,12 @@ if(LEVELDB_BUILD_TESTS) PRIVATE LEVELDB_PLATFORM_POSIX=1 ) + if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions("${test_target_name}" + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) + endif(NOT HAVE_CXX17_HAS_INCLUDE) add_test(NAME "${test_target_name}" COMMAND "${test_target_name}") endfunction(leveldb_test) @@ -336,6 +363,12 @@ if(LEVELDB_BUILD_BENCHMARKS) PRIVATE LEVELDB_PLATFORM_POSIX=1 ) + if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions("${bench_target_name}" + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) + endif(NOT HAVE_CXX17_HAS_INCLUDE) endfunction(leveldb_benchmark) if(NOT BUILD_SHARED_LIBS) From 6a3b915166fce75aaf9ac209114a3ad9caa34171 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 10 Apr 2018 16:12:14 -0700 Subject: [PATCH 088/174] Remove PLATFORM_IS_LITTLE_ENDIAN from port/posix.h. This is an accidental leftover from the CMake migration. The macro has been replaced with LEVELDB_IS_BIG_ENDIAN. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192364918 --- port/port_posix.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/port/port_posix.h b/port/port_posix.h index 2fb2269efa..54b07b6ece 100644 --- a/port/port_posix.h +++ b/port/port_posix.h @@ -36,10 +36,6 @@ #include "port/atomic_pointer.h" #include "port/thread_annotations.h" -#ifndef PLATFORM_IS_LITTLE_ENDIAN -#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) -#endif - #if !HAVE_FDATASYNC #define fdatasync fsync #endif // !HAVE_FDATASYNC From 09217fd0677a4fd9713c7a4d774c494a7d3c1f15 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 10 Apr 2018 16:18:06 -0700 Subject: [PATCH 089/174] Replace NULL with nullptr in C++ files. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192365747 --- db/builder.cc | 2 +- db/c.cc | 26 ++--- db/corruption_test.cc | 18 ++-- db/db_bench.cc | 42 ++++---- db/db_impl.cc | 184 +++++++++++++++++----------------- db/db_impl.h | 8 +- db/db_test.cc | 164 +++++++++++++++--------------- db/dumpfile.cc | 4 +- db/fault_injection_test.cc | 10 +- db/log_reader.cc | 2 +- db/log_reader.h | 2 +- db/recovery_test.cc | 14 +-- db/repair.cc | 12 +-- db/skiplist.h | 36 +++---- db/skiplist_test.cc | 4 +- db/table_cache.cc | 18 ++-- db/table_cache.h | 10 +- db/version_edit.cc | 8 +- db/version_set.cc | 77 +++++++------- db/version_set.h | 20 ++-- db/version_set_test.cc | 52 +++++----- doc/bench/db_bench_sqlite3.cc | 59 +++++------ doc/bench/db_bench_tree_db.cc | 24 ++--- helpers/memenv/memenv.cc | 6 +- include/leveldb/cache.h | 2 +- include/leveldb/db.h | 8 +- include/leveldb/env.h | 12 +-- include/leveldb/options.h | 24 ++--- include/leveldb/status.h | 12 +-- include/leveldb/table.h | 2 +- port/port_posix.cc | 4 +- table/block.cc | 16 +-- table/filter_block.cc | 4 +- table/iterator.cc | 12 +-- table/iterator_wrapper.h | 8 +- table/merger.cc | 12 +-- table/table.cc | 24 ++--- table/table_builder.cc | 12 +-- table/table_test.cc | 22 ++-- table/two_level_iterator.cc | 32 +++--- util/arena.cc | 2 +- util/cache.cc | 24 ++--- util/cache_test.cc | 4 +- util/coding.cc | 12 +-- util/coding.h | 2 +- util/coding_test.cc | 19 ++-- util/env.cc | 2 +- util/env_posix.cc | 40 ++++---- util/env_posix_test.cc | 2 +- util/env_test.cc | 6 +- util/options.cc | 6 +- util/posix_logger.h | 2 +- util/status.cc | 2 +- util/testharness.cc | 10 +- util/testutil.h | 4 +- 55 files changed, 575 insertions(+), 570 deletions(-) diff --git a/db/builder.cc b/db/builder.cc index 729f9fd4ac..5fa405d31d 100644 --- a/db/builder.cc +++ b/db/builder.cc @@ -56,7 +56,7 @@ Status BuildTable(const std::string& dbname, s = file->Close(); } delete file; - file = NULL; + file = nullptr; if (s.ok()) { // Verify that the table is usable diff --git a/db/c.cc b/db/c.cc index 0ccf08c4f3..77b33d5ad7 100644 --- a/db/c.cc +++ b/db/c.cc @@ -129,10 +129,10 @@ struct leveldb_env_t { }; static bool SaveError(char** errptr, const Status& s) { - assert(errptr != NULL); + assert(errptr != nullptr); if (s.ok()) { return false; - } else if (*errptr == NULL) { + } else if (*errptr == nullptr) { *errptr = strdup(s.ToString().c_str()); } else { // TODO(sanjay): Merge with existing error? @@ -154,7 +154,7 @@ leveldb_t* leveldb_open( char** errptr) { DB* db; if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { - return NULL; + return nullptr; } leveldb_t* result = new leveldb_t; result->rep = db; @@ -199,7 +199,7 @@ char* leveldb_get( const char* key, size_t keylen, size_t* vallen, char** errptr) { - char* result = NULL; + char* result = nullptr; std::string tmp; Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); if (s.ok()) { @@ -244,7 +244,7 @@ char* leveldb_property_value( // We use strdup() since we expect human readable output. return strdup(tmp.c_str()); } else { - return NULL; + return nullptr; } } @@ -269,9 +269,9 @@ void leveldb_compact_range( const char* limit_key, size_t limit_key_len) { Slice a, b; db->rep->CompactRange( - // Pass NULL Slice if corresponding "const char*" is NULL - (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), - (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); + // Pass null Slice if corresponding "const char*" is null + (start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr)); } void leveldb_destroy_db( @@ -418,11 +418,11 @@ void leveldb_options_set_paranoid_checks( } void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { - opt->rep.env = (env ? env->rep : NULL); + opt->rep.env = (env ? env->rep : nullptr); } void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { - opt->rep.info_log = (l ? l->rep : NULL); + opt->rep.info_log = (l ? l->rep : nullptr); } void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { @@ -517,7 +517,7 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { }; Wrapper* wrapper = new Wrapper; wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); - wrapper->state_ = NULL; + wrapper->state_ = nullptr; wrapper->destructor_ = &Wrapper::DoNothing; return wrapper; } @@ -544,7 +544,7 @@ void leveldb_readoptions_set_fill_cache( void leveldb_readoptions_set_snapshot( leveldb_readoptions_t* opt, const leveldb_snapshot_t* snap) { - opt->rep.snapshot = (snap ? snap->rep : NULL); + opt->rep.snapshot = (snap ? snap->rep : nullptr); } leveldb_writeoptions_t* leveldb_writeoptions_create() { @@ -586,7 +586,7 @@ void leveldb_env_destroy(leveldb_env_t* env) { char* leveldb_env_get_test_directory(leveldb_env_t* env) { std::string result; if (!env->rep->GetTestDirectory(&result).ok()) { - return NULL; + return nullptr; } char* buffer = static_cast(malloc(result.size() + 1)); diff --git a/db/corruption_test.cc b/db/corruption_test.cc index 37a484d25f..0b93c241eb 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -39,7 +39,7 @@ class CorruptionTest { dbname_ = test::TmpDir() + "/corruption_test"; DestroyDB(dbname_, options_); - db_ = NULL; + db_ = nullptr; options_.create_if_missing = true; Reopen(); options_.create_if_missing = false; @@ -53,7 +53,7 @@ class CorruptionTest { Status TryReopen() { delete db_; - db_ = NULL; + db_ = nullptr; return DB::Open(options_, dbname_, &db_); } @@ -63,7 +63,7 @@ class CorruptionTest { void RepairDB() { delete db_; - db_ = NULL; + db_ = nullptr; ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); } @@ -237,8 +237,8 @@ TEST(CorruptionTest, TableFile) { Build(100); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); - dbi->TEST_CompactRange(1, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); + dbi->TEST_CompactRange(1, nullptr, nullptr); Corrupt(kTableFile, 100, 1); Check(90, 99); @@ -251,8 +251,8 @@ TEST(CorruptionTest, TableFileRepair) { Build(100); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); - dbi->TEST_CompactRange(1, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); + dbi->TEST_CompactRange(1, nullptr, nullptr); Corrupt(kTableFile, 100, 1); RepairDB(); @@ -302,7 +302,7 @@ TEST(CorruptionTest, CorruptedDescriptor) { ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); Corrupt(kDescriptorFile, 0, 1000); Status s = TryReopen(); @@ -343,7 +343,7 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) { Corrupt(kTableFile, 100, 1); env_.SleepForMicroseconds(100000); } - dbi->CompactRange(NULL, NULL); + dbi->CompactRange(nullptr, nullptr); // Write must fail because of corrupted table std::string tmp1, tmp2; diff --git a/db/db_bench.cc b/db/db_bench.cc index 701b128321..115cf4506b 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -111,12 +111,12 @@ static bool FLAGS_use_existing_db = false; static bool FLAGS_reuse_logs = false; // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; namespace leveldb { namespace { -leveldb::Env* g_env = NULL; +leveldb::Env* g_env = nullptr; // Helper for quickly generating random data. class RandomGenerator { @@ -370,18 +370,18 @@ class Benchmark { kMajorVersion, kMinorVersion); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -402,11 +402,11 @@ class Benchmark { public: Benchmark() - : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr), filter_policy_(FLAGS_bloom_bits >= 0 ? NewBloomFilterPolicy(FLAGS_bloom_bits) - : NULL), - db_(NULL), + : nullptr), + db_(nullptr), num_(FLAGS_num), value_size_(FLAGS_value_size), entries_per_batch_(1), @@ -435,12 +435,12 @@ class Benchmark { Open(); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -453,7 +453,7 @@ class Benchmark { entries_per_batch_ = 1; write_options_ = WriteOptions(); - void (Benchmark::*method)(ThreadState*) = NULL; + void (Benchmark::*method)(ThreadState*) = nullptr; bool fresh_db = false; int num_threads = FLAGS_threads; @@ -532,16 +532,16 @@ class Benchmark { if (FLAGS_use_existing_db) { fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", name.ToString().c_str()); - method = NULL; + method = nullptr; } else { delete db_; - db_ = NULL; + db_ = nullptr; DestroyDB(FLAGS_db, Options()); Open(); } } - if (method != NULL) { + if (method != nullptr) { RunBenchmark(num_threads, name, method); } } @@ -643,7 +643,7 @@ class Benchmark { int dummy; port::AtomicPointer ap(&dummy); int count = 0; - void *ptr = NULL; + void *ptr = nullptr; thread->stats.AddMessage("(each op is 1000 loads)"); while (count < 100000) { for (int i = 0; i < 1000; i++) { @@ -652,7 +652,7 @@ class Benchmark { count++; thread->stats.FinishedSingleOp(); } - if (ptr == NULL) exit(1); // Disable unused variable warning. + if (ptr == nullptr) exit(1); // Disable unused variable warning. } void SnappyCompress(ThreadState* thread) { @@ -703,7 +703,7 @@ class Benchmark { } void Open() { - assert(db_ == NULL); + assert(db_ == nullptr); Options options; options.env = g_env; options.create_if_missing = !FLAGS_use_existing_db; @@ -914,7 +914,7 @@ class Benchmark { } void Compact(ThreadState* thread) { - db_->CompactRange(NULL, NULL); + db_->CompactRange(nullptr, nullptr); } void PrintStats(const char* key) { @@ -1004,7 +1004,7 @@ int main(int argc, char** argv) { leveldb::g_env = leveldb::Env::Default(); // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { + if (FLAGS_db == nullptr) { leveldb::g_env->GetTestDirectory(&default_db_path); default_db_path += "/dbbench"; FLAGS_db = default_db_path.c_str(); diff --git a/db/db_impl.cc b/db/db_impl.cc index cd8792ecf2..02a6872820 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -77,8 +77,8 @@ struct DBImpl::CompactionState { explicit CompactionState(Compaction* c) : compaction(c), - outfile(NULL), - builder(NULL), + outfile(nullptr), + builder(nullptr), total_bytes(0) { } }; @@ -95,22 +95,22 @@ Options SanitizeOptions(const std::string& dbname, const Options& src) { Options result = src; result.comparator = icmp; - result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; + result.filter_policy = (src.filter_policy != nullptr) ? ipolicy : nullptr; ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); ClipToRange(&result.max_file_size, 1<<20, 1<<30); ClipToRange(&result.block_size, 1<<10, 4<<20); - if (result.info_log == NULL) { + if (result.info_log == nullptr) { // Open a log file in the same directory as the db src.env->CreateDir(dbname); // In case it does not exist src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); if (!s.ok()) { // No place suitable for logging - result.info_log = NULL; + result.info_log = nullptr; } } - if (result.block_cache == NULL) { + if (result.block_cache == nullptr) { result.block_cache = NewLRUCache(8 << 20); } return result; @@ -131,39 +131,39 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), table_cache_(new TableCache(dbname_, options_, TableCacheSize(options_))), - db_lock_(NULL), - shutting_down_(NULL), + db_lock_(nullptr), + shutting_down_(nullptr), background_work_finished_signal_(&mutex_), - mem_(NULL), - imm_(NULL), - logfile_(NULL), + mem_(nullptr), + imm_(nullptr), + logfile_(nullptr), logfile_number_(0), - log_(NULL), + log_(nullptr), seed_(0), tmp_batch_(new WriteBatch), background_compaction_scheduled_(false), - manual_compaction_(NULL), + manual_compaction_(nullptr), versions_(new VersionSet(dbname_, &options_, table_cache_, &internal_comparator_)) { - has_imm_.Release_Store(NULL); + has_imm_.Release_Store(nullptr); } DBImpl::~DBImpl() { // Wait for background work to finish mutex_.Lock(); - shutting_down_.Release_Store(this); // Any non-NULL value is ok + shutting_down_.Release_Store(this); // Any non-null value is ok while (background_compaction_scheduled_) { background_work_finished_signal_.Wait(); } mutex_.Unlock(); - if (db_lock_ != NULL) { + if (db_lock_ != nullptr) { env_->UnlockFile(db_lock_); } delete versions_; - if (mem_ != NULL) mem_->Unref(); - if (imm_ != NULL) imm_->Unref(); + if (mem_ != nullptr) mem_->Unref(); + if (imm_ != nullptr) imm_->Unref(); delete tmp_batch_; delete log_; delete logfile_; @@ -283,7 +283,7 @@ Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { // committed only when the descriptor is created, and this directory // may already exist from a previous failed creation attempt. env_->CreateDir(dbname_); - assert(db_lock_ == NULL); + assert(db_lock_ == nullptr); Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); if (!s.ok()) { return s; @@ -374,12 +374,12 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, Env* env; Logger* info_log; const char* fname; - Status* status; // NULL if options_.paranoid_checks==false + Status* status; // null if options_.paranoid_checks==false virtual void Corruption(size_t bytes, const Status& s) { Log(info_log, "%s%s: dropping %d bytes; %s", - (this->status == NULL ? "(ignoring error) " : ""), + (this->status == nullptr ? "(ignoring error) " : ""), fname, static_cast(bytes), s.ToString().c_str()); - if (this->status != NULL && this->status->ok()) *this->status = s; + if (this->status != nullptr && this->status->ok()) *this->status = s; } }; @@ -399,7 +399,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, reporter.env = env_; reporter.info_log = options_.info_log; reporter.fname = fname.c_str(); - reporter.status = (options_.paranoid_checks ? &status : NULL); + reporter.status = (options_.paranoid_checks ? &status : nullptr); // We intentionally make log::Reader do checksumming even if // paranoid_checks==false so that corruptions cause entire commits // to be skipped instead of propagating bad information (like overly @@ -414,7 +414,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, Slice record; WriteBatch batch; int compactions = 0; - MemTable* mem = NULL; + MemTable* mem = nullptr; while (reader.ReadRecord(&record, &scratch) && status.ok()) { if (record.size() < 12) { @@ -424,7 +424,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, } WriteBatchInternal::SetContents(&batch, record); - if (mem == NULL) { + if (mem == nullptr) { mem = new MemTable(internal_comparator_); mem->Ref(); } @@ -443,9 +443,9 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { compactions++; *save_manifest = true; - status = WriteLevel0Table(mem, edit, NULL); + status = WriteLevel0Table(mem, edit, nullptr); mem->Unref(); - mem = NULL; + mem = nullptr; if (!status.ok()) { // Reflect errors immediately so that conditions like full // file-systems cause the DB::Open() to fail. @@ -458,31 +458,31 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, // See if we should keep reusing the last log file. if (status.ok() && options_.reuse_logs && last_log && compactions == 0) { - assert(logfile_ == NULL); - assert(log_ == NULL); - assert(mem_ == NULL); + assert(logfile_ == nullptr); + assert(log_ == nullptr); + assert(mem_ == nullptr); uint64_t lfile_size; if (env_->GetFileSize(fname, &lfile_size).ok() && env_->NewAppendableFile(fname, &logfile_).ok()) { Log(options_.info_log, "Reusing old log %s \n", fname.c_str()); log_ = new log::Writer(logfile_, lfile_size); logfile_number_ = log_number; - if (mem != NULL) { + if (mem != nullptr) { mem_ = mem; - mem = NULL; + mem = nullptr; } else { - // mem can be NULL if lognum exists but was empty. + // mem can be nullptr if lognum exists but was empty. mem_ = new MemTable(internal_comparator_); mem_->Ref(); } } } - if (mem != NULL) { + if (mem != nullptr) { // mem did not get reused; compact it. if (status.ok()) { *save_manifest = true; - status = WriteLevel0Table(mem, edit, NULL); + status = WriteLevel0Table(mem, edit, nullptr); } mem->Unref(); } @@ -522,7 +522,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, if (s.ok() && meta.file_size > 0) { const Slice min_user_key = meta.smallest.user_key(); const Slice max_user_key = meta.largest.user_key(); - if (base != NULL) { + if (base != nullptr) { level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); } edit->AddFile(level, meta.number, meta.file_size, @@ -538,7 +538,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, void DBImpl::CompactMemTable() { mutex_.AssertHeld(); - assert(imm_ != NULL); + assert(imm_ != nullptr); // Save the contents of the memtable as a new Table VersionEdit edit; @@ -561,8 +561,8 @@ void DBImpl::CompactMemTable() { if (s.ok()) { // Commit to the new state imm_->Unref(); - imm_ = NULL; - has_imm_.Release_Store(NULL); + imm_ = nullptr; + has_imm_.Release_Store(nullptr); DeleteObsoleteFiles(); } else { RecordBackgroundError(s); @@ -596,14 +596,14 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin, ManualCompaction manual; manual.level = level; manual.done = false; - if (begin == NULL) { - manual.begin = NULL; + if (begin == nullptr) { + manual.begin = nullptr; } else { begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); manual.begin = &begin_storage; } - if (end == NULL) { - manual.end = NULL; + if (end == nullptr) { + manual.end = nullptr; } else { end_storage = InternalKey(*end, 0, static_cast(0)); manual.end = &end_storage; @@ -611,7 +611,7 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin, MutexLock l(&mutex_); while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { - if (manual_compaction_ == NULL) { // Idle + if (manual_compaction_ == nullptr) { // Idle manual_compaction_ = &manual; MaybeScheduleCompaction(); } else { // Running either my compaction or another compaction. @@ -620,20 +620,20 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin, } if (manual_compaction_ == &manual) { // Cancel my manual compaction since we aborted early for some reason. - manual_compaction_ = NULL; + manual_compaction_ = nullptr; } } Status DBImpl::TEST_CompactMemTable() { - // NULL batch means just wait for earlier writes to be done - Status s = Write(WriteOptions(), NULL); + // nullptr batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), nullptr); if (s.ok()) { // Wait until the compaction completes MutexLock l(&mutex_); - while (imm_ != NULL && bg_error_.ok()) { + while (imm_ != nullptr && bg_error_.ok()) { background_work_finished_signal_.Wait(); } - if (imm_ != NULL) { + if (imm_ != nullptr) { s = bg_error_; } } @@ -656,8 +656,8 @@ void DBImpl::MaybeScheduleCompaction() { // DB is being deleted; no more background compactions } else if (!bg_error_.ok()) { // Already got an error; no more changes - } else if (imm_ == NULL && - manual_compaction_ == NULL && + } else if (imm_ == nullptr && + manual_compaction_ == nullptr && !versions_->NeedsCompaction()) { // No work to be done } else { @@ -692,19 +692,19 @@ void DBImpl::BackgroundCall() { void DBImpl::BackgroundCompaction() { mutex_.AssertHeld(); - if (imm_ != NULL) { + if (imm_ != nullptr) { CompactMemTable(); return; } Compaction* c; - bool is_manual = (manual_compaction_ != NULL); + bool is_manual = (manual_compaction_ != nullptr); InternalKey manual_end; if (is_manual) { ManualCompaction* m = manual_compaction_; c = versions_->CompactRange(m->level, m->begin, m->end); - m->done = (c == NULL); - if (c != NULL) { + m->done = (c == nullptr); + if (c != nullptr) { manual_end = c->input(0, c->num_input_files(0) - 1)->largest; } Log(options_.info_log, @@ -718,7 +718,7 @@ void DBImpl::BackgroundCompaction() { } Status status; - if (c == NULL) { + if (c == nullptr) { // Nothing to do } else if (!is_manual && c->IsTrivialMove()) { // Move file to next level @@ -770,18 +770,18 @@ void DBImpl::BackgroundCompaction() { m->tmp_storage = manual_end; m->begin = &m->tmp_storage; } - manual_compaction_ = NULL; + manual_compaction_ = nullptr; } } void DBImpl::CleanupCompaction(CompactionState* compact) { mutex_.AssertHeld(); - if (compact->builder != NULL) { + if (compact->builder != nullptr) { // May happen if we get a shutdown call in the middle of compaction compact->builder->Abandon(); delete compact->builder; } else { - assert(compact->outfile == NULL); + assert(compact->outfile == nullptr); } delete compact->outfile; for (size_t i = 0; i < compact->outputs.size(); i++) { @@ -792,8 +792,8 @@ void DBImpl::CleanupCompaction(CompactionState* compact) { } Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { - assert(compact != NULL); - assert(compact->builder == NULL); + assert(compact != nullptr); + assert(compact->builder == nullptr); uint64_t file_number; { mutex_.Lock(); @@ -818,9 +818,9 @@ Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, Iterator* input) { - assert(compact != NULL); - assert(compact->outfile != NULL); - assert(compact->builder != NULL); + assert(compact != nullptr); + assert(compact->outfile != nullptr); + assert(compact->builder != nullptr); const uint64_t output_number = compact->current_output()->number; assert(output_number != 0); @@ -837,7 +837,7 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, compact->current_output()->file_size = current_bytes; compact->total_bytes += current_bytes; delete compact->builder; - compact->builder = NULL; + compact->builder = nullptr; // Finish and check for file errors if (s.ok()) { @@ -847,7 +847,7 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, s = compact->outfile->Close(); } delete compact->outfile; - compact->outfile = NULL; + compact->outfile = nullptr; if (s.ok() && current_entries > 0) { // Verify that the table is usable @@ -901,8 +901,8 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { compact->compaction->level() + 1); assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); - assert(compact->builder == NULL); - assert(compact->outfile == NULL); + assert(compact->builder == nullptr); + assert(compact->outfile == nullptr); if (snapshots_.empty()) { compact->smallest_snapshot = versions_->LastSequence(); } else { @@ -921,10 +921,10 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { SequenceNumber last_sequence_for_key = kMaxSequenceNumber; for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { // Prioritize immutable compaction work - if (has_imm_.NoBarrier_Load() != NULL) { + if (has_imm_.NoBarrier_Load() != nullptr) { const uint64_t imm_start = env_->NowMicros(); mutex_.Lock(); - if (imm_ != NULL) { + if (imm_ != nullptr) { CompactMemTable(); // Wake up MakeRoomForWrite() if necessary. background_work_finished_signal_.SignalAll(); @@ -935,7 +935,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { Slice key = input->key(); if (compact->compaction->ShouldStopBefore(key) && - compact->builder != NULL) { + compact->builder != nullptr) { status = FinishCompactionOutputFile(compact, input); if (!status.ok()) { break; @@ -989,7 +989,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (!drop) { // Open output file if necessary - if (compact->builder == NULL) { + if (compact->builder == nullptr) { status = OpenCompactionOutputFile(compact); if (!status.ok()) { break; @@ -1017,14 +1017,14 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (status.ok() && shutting_down_.Acquire_Load()) { status = Status::IOError("Deleting DB during compaction"); } - if (status.ok() && compact->builder != NULL) { + if (status.ok() && compact->builder != nullptr) { status = FinishCompactionOutputFile(compact, input); } if (status.ok()) { status = input->status(); } delete input; - input = NULL; + input = nullptr; CompactionStats stats; stats.micros = env_->NowMicros() - start_micros - imm_micros; @@ -1068,7 +1068,7 @@ static void CleanupIteratorState(void* arg1, void* arg2) { IterState* state = reinterpret_cast(arg1); state->mu->Lock(); state->mem->Unref(); - if (state->imm != NULL) state->imm->Unref(); + if (state->imm != nullptr) state->imm->Unref(); state->version->Unref(); state->mu->Unlock(); delete state; @@ -1086,7 +1086,7 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, std::vector list; list.push_back(mem_->NewIterator()); mem_->Ref(); - if (imm_ != NULL) { + if (imm_ != nullptr) { list.push_back(imm_->NewIterator()); imm_->Ref(); } @@ -1096,7 +1096,7 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, versions_->current()->Ref(); IterState* cleanup = new IterState(&mutex_, mem_, imm_, versions_->current()); - internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, nullptr); *seed = ++seed_; mutex_.Unlock(); @@ -1120,7 +1120,7 @@ Status DBImpl::Get(const ReadOptions& options, Status s; MutexLock l(&mutex_); SequenceNumber snapshot; - if (options.snapshot != NULL) { + if (options.snapshot != nullptr) { snapshot = reinterpret_cast(options.snapshot)->number_; } else { snapshot = versions_->LastSequence(); @@ -1130,7 +1130,7 @@ Status DBImpl::Get(const ReadOptions& options, MemTable* imm = imm_; Version* current = versions_->current(); mem->Ref(); - if (imm != NULL) imm->Ref(); + if (imm != nullptr) imm->Ref(); current->Ref(); bool have_stat_update = false; @@ -1143,7 +1143,7 @@ Status DBImpl::Get(const ReadOptions& options, LookupKey lkey(key, snapshot); if (mem->Get(lkey, value, &s)) { // Done - } else if (imm != NULL && imm->Get(lkey, value, &s)) { + } else if (imm != nullptr && imm->Get(lkey, value, &s)) { // Done } else { s = current->Get(options, lkey, value, &stats); @@ -1156,7 +1156,7 @@ Status DBImpl::Get(const ReadOptions& options, MaybeScheduleCompaction(); } mem->Unref(); - if (imm != NULL) imm->Unref(); + if (imm != nullptr) imm->Unref(); current->Unref(); return s; } @@ -1167,7 +1167,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& options) { Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); return NewDBIterator( this, user_comparator(), iter, - (options.snapshot != NULL + (options.snapshot != nullptr ? reinterpret_cast(options.snapshot)->number_ : latest_snapshot), seed); @@ -1215,10 +1215,10 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } // May temporarily unlock and wait. - Status status = MakeRoomForWrite(my_batch == NULL); + Status status = MakeRoomForWrite(my_batch == nullptr); uint64_t last_sequence = versions_->LastSequence(); Writer* last_writer = &w; - if (status.ok() && my_batch != NULL) { // NULL batch is for compactions + if (status.ok() && my_batch != nullptr) { // nullptr batch is for compactions WriteBatch* updates = BuildBatchGroup(&last_writer); WriteBatchInternal::SetSequence(updates, last_sequence + 1); last_sequence += WriteBatchInternal::Count(updates); @@ -1273,13 +1273,13 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } // REQUIRES: Writer list must be non-empty -// REQUIRES: First writer must have a non-NULL batch +// REQUIRES: First writer must have a non-null batch WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { mutex_.AssertHeld(); assert(!writers_.empty()); Writer* first = writers_.front(); WriteBatch* result = first->batch; - assert(result != NULL); + assert(result != nullptr); size_t size = WriteBatchInternal::ByteSize(first->batch); @@ -1301,7 +1301,7 @@ WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { break; } - if (w->batch != NULL) { + if (w->batch != nullptr) { size += WriteBatchInternal::ByteSize(w->batch); if (size > max_size) { // Do not make batch too big @@ -1351,7 +1351,7 @@ Status DBImpl::MakeRoomForWrite(bool force) { (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { // There is room in current memtable break; - } else if (imm_ != NULL) { + } else if (imm_ != nullptr) { // We have filled up the current memtable, but the previous // one is still being compacted, so we wait. Log(options_.info_log, "Current memtable full; waiting...\n"); @@ -1364,7 +1364,7 @@ Status DBImpl::MakeRoomForWrite(bool force) { // Attempt to switch to a new memtable and trigger compaction of old assert(versions_->PrevLogNumber() == 0); uint64_t new_log_number = versions_->NewFileNumber(); - WritableFile* lfile = NULL; + WritableFile* lfile = nullptr; s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); if (!s.ok()) { // Avoid chewing through file number space in a tight loop. @@ -1498,7 +1498,7 @@ DB::~DB() { } Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) { - *dbptr = NULL; + *dbptr = nullptr; DBImpl* impl = new DBImpl(options, dbname); impl->mutex_.Lock(); @@ -1506,7 +1506,7 @@ Status DB::Open(const Options& options, const std::string& dbname, // Recover handles create_if_missing, error_if_exists bool save_manifest = false; Status s = impl->Recover(&edit, &save_manifest); - if (s.ok() && impl->mem_ == NULL) { + if (s.ok() && impl->mem_ == nullptr) { // Create new log and a corresponding memtable. uint64_t new_log_number = impl->versions_->NewFileNumber(); WritableFile* lfile; @@ -1532,7 +1532,7 @@ Status DB::Open(const Options& options, const std::string& dbname, } impl->mutex_.Unlock(); if (s.ok()) { - assert(impl->mem_ != NULL); + assert(impl->mem_ != nullptr); *dbptr = impl; } else { delete impl; diff --git a/db/db_impl.h b/db/db_impl.h index 634411283a..00e800a114 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -131,7 +131,7 @@ class DBImpl : public DB { // table_cache_ provides its own synchronization TableCache* const table_cache_; - // Lock over the persistent DB state. Non-NULL iff successfully acquired. + // Lock over the persistent DB state. Non-null iff successfully acquired. FileLock* db_lock_; // State below is protected by mutex_ @@ -140,7 +140,7 @@ class DBImpl : public DB { port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_); MemTable* mem_; MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted - port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + port::AtomicPointer has_imm_; // So bg thread can detect non-null imm_ WritableFile* logfile_; uint64_t logfile_number_ GUARDED_BY(mutex_); log::Writer* log_; @@ -163,8 +163,8 @@ class DBImpl : public DB { struct ManualCompaction { int level; bool done; - const InternalKey* begin; // NULL means beginning of key range - const InternalKey* end; // NULL means end of key range + const InternalKey* begin; // null means beginning of key range + const InternalKey* end; // null means end of key range InternalKey tmp_storage; // Used to keep track of compaction progress }; ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); diff --git a/db/db_test.cc b/db/db_test.cc index b1d2cd84da..47e3287e0b 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -96,35 +96,35 @@ class TestEnv : public EnvWrapper { // Special Env used to delay background operations class SpecialEnv : public EnvWrapper { public: - // sstable/log Sync() calls are blocked while this pointer is non-NULL. + // sstable/log Sync() calls are blocked while this pointer is non-null. port::AtomicPointer delay_data_sync_; // sstable/log Sync() calls return an error. port::AtomicPointer data_sync_error_; - // Simulate no-space errors while this pointer is non-NULL. + // Simulate no-space errors while this pointer is non-null. port::AtomicPointer no_space_; - // Simulate non-writable file system while this pointer is non-NULL + // Simulate non-writable file system while this pointer is non-null. port::AtomicPointer non_writable_; - // Force sync of manifest files to fail while this pointer is non-NULL + // Force sync of manifest files to fail while this pointer is non-null. port::AtomicPointer manifest_sync_error_; - // Force write to manifest files to fail while this pointer is non-NULL + // Force write to manifest files to fail while this pointer is non-null. port::AtomicPointer manifest_write_error_; bool count_random_reads_; AtomicCounter random_read_counter_; explicit SpecialEnv(Env* base) : EnvWrapper(base) { - delay_data_sync_.Release_Store(NULL); - data_sync_error_.Release_Store(NULL); - no_space_.Release_Store(NULL); - non_writable_.Release_Store(NULL); + delay_data_sync_.Release_Store(nullptr); + data_sync_error_.Release_Store(nullptr); + no_space_.Release_Store(nullptr); + non_writable_.Release_Store(nullptr); count_random_reads_ = false; - manifest_sync_error_.Release_Store(NULL); - manifest_write_error_.Release_Store(NULL); + manifest_sync_error_.Release_Store(nullptr); + manifest_write_error_.Release_Store(nullptr); } Status NewWritableFile(const std::string& f, WritableFile** r) { @@ -140,7 +140,7 @@ class SpecialEnv : public EnvWrapper { } ~DataFile() { delete base_; } Status Append(const Slice& data) { - if (env_->no_space_.Acquire_Load() != NULL) { + if (env_->no_space_.Acquire_Load() != nullptr) { // Drop writes on the floor return Status::OK(); } else { @@ -150,10 +150,10 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->data_sync_error_.Acquire_Load() != NULL) { + if (env_->data_sync_error_.Acquire_Load() != nullptr) { return Status::IOError("simulated data sync error"); } - while (env_->delay_data_sync_.Acquire_Load() != NULL) { + while (env_->delay_data_sync_.Acquire_Load() != nullptr) { DelayMilliseconds(100); } return base_->Sync(); @@ -167,7 +167,7 @@ class SpecialEnv : public EnvWrapper { ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } ~ManifestFile() { delete base_; } Status Append(const Slice& data) { - if (env_->manifest_write_error_.Acquire_Load() != NULL) { + if (env_->manifest_write_error_.Acquire_Load() != nullptr) { return Status::IOError("simulated writer error"); } else { return base_->Append(data); @@ -176,7 +176,7 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + if (env_->manifest_sync_error_.Acquire_Load() != nullptr) { return Status::IOError("simulated sync error"); } else { return base_->Sync(); @@ -184,16 +184,16 @@ class SpecialEnv : public EnvWrapper { } }; - if (non_writable_.Acquire_Load() != NULL) { + if (non_writable_.Acquire_Load() != nullptr) { return Status::IOError("simulated write error"); } Status s = target()->NewWritableFile(f, r); if (s.ok()) { - if (strstr(f.c_str(), ".ldb") != NULL || - strstr(f.c_str(), ".log") != NULL) { + if (strstr(f.c_str(), ".ldb") != nullptr || + strstr(f.c_str(), ".log") != nullptr) { *r = new DataFile(this, *r); - } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + } else if (strstr(f.c_str(), "MANIFEST") != nullptr) { *r = new ManifestFile(this, *r); } } @@ -251,7 +251,7 @@ class DBTest { filter_policy_ = NewBloomFilterPolicy(10); dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, Options()); - db_ = NULL; + db_ = nullptr; Reopen(); } @@ -298,27 +298,27 @@ class DBTest { return reinterpret_cast(db_); } - void Reopen(Options* options = NULL) { + void Reopen(Options* options = nullptr) { ASSERT_OK(TryReopen(options)); } void Close() { delete db_; - db_ = NULL; + db_ = nullptr; } - void DestroyAndReopen(Options* options = NULL) { + void DestroyAndReopen(Options* options = nullptr) { delete db_; - db_ = NULL; + db_ = nullptr; DestroyDB(dbname_, Options()); ASSERT_OK(TryReopen(options)); } Status TryReopen(Options* options) { delete db_; - db_ = NULL; + db_ = nullptr; Options opts; - if (options != NULL) { + if (options != nullptr) { opts = *options; } else { opts = CurrentOptions(); @@ -337,7 +337,7 @@ class DBTest { return db_->Delete(WriteOptions(), k); } - std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { ReadOptions options; options.snapshot = snapshot; std::string result; @@ -549,7 +549,7 @@ class DBTest { TEST(DBTest, Empty) { do { - ASSERT_TRUE(db_ != NULL); + ASSERT_TRUE(db_ != nullptr); ASSERT_EQ("NOT_FOUND", Get("foo")); } while (ChangeOptions()); } @@ -590,7 +590,7 @@ TEST(DBTest, GetFromImmutableLayer) { Put("k1", std::string(100000, 'x')); // Fill memtable Put("k2", std::string(100000, 'y')); // Trigger compaction ASSERT_EQ("v1", Get("foo")); - env_->delay_data_sync_.Release_Store(NULL); // Release sync calls + env_->delay_data_sync_.Release_Store(nullptr); // Release sync calls } while (ChangeOptions()); } @@ -695,7 +695,7 @@ TEST(DBTest, GetEncountersEmptyLevel) { } // Step 2: clear level 1 if necessary. - dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); ASSERT_EQ(NumTableFilesAtLevel(0), 1); ASSERT_EQ(NumTableFilesAtLevel(1), 0); ASSERT_EQ(NumTableFilesAtLevel(2), 1); @@ -1032,7 +1032,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) { // Reopening moves updates to level-0 Reopen(&options); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); ASSERT_EQ(NumTableFilesAtLevel(0), 0); ASSERT_GT(NumTableFilesAtLevel(1), 1); @@ -1083,7 +1083,7 @@ TEST(DBTest, SparseMerge) { } Put("C", "vc"); dbfull()->TEST_CompactMemTable(); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); // Make sparse update Put("A", "va2"); @@ -1094,9 +1094,9 @@ TEST(DBTest, SparseMerge) { // Compactions should not cause us to create a situation where // a file overlaps too much data at the next level. ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); - dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); } @@ -1207,7 +1207,7 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); } } while (ChangeOptions()); } @@ -1283,11 +1283,11 @@ TEST(DBTest, HiddenValuesAreRemoved) { db_->ReleaseSnapshot(snapshot); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); Slice x("x"); - dbfull()->TEST_CompactRange(0, NULL, &x); + dbfull()->TEST_CompactRange(0, nullptr, &x); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); ASSERT_EQ(NumTableFilesAtLevel(0), 0); ASSERT_GE(NumTableFilesAtLevel(1), 1); - dbfull()->TEST_CompactRange(1, NULL, &x); + dbfull()->TEST_CompactRange(1, nullptr, &x); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); @@ -1313,11 +1313,11 @@ TEST(DBTest, DeletionMarkers1) { ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); Slice z("z"); - dbfull()->TEST_CompactRange(last-2, NULL, &z); + dbfull()->TEST_CompactRange(last-2, nullptr, &z); // DEL eliminated, but v1 remains because we aren't compacting that level // (DEL can be eliminated because v2 hides v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); - dbfull()->TEST_CompactRange(last-1, NULL, NULL); + dbfull()->TEST_CompactRange(last-1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); @@ -1340,10 +1340,10 @@ TEST(DBTest, DeletionMarkers2) { ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-2, NULL, NULL); + dbfull()->TEST_CompactRange(last-2, nullptr, nullptr); // DEL kept: "last" file overlaps ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-1, NULL, NULL); + dbfull()->TEST_CompactRange(last-1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); @@ -1376,8 +1376,8 @@ TEST(DBTest, OverlapInLevel0) { ASSERT_EQ("2,1,1", FilesPerLevel()); // Compact away the placeholder files we created initially - dbfull()->TEST_CompactRange(1, NULL, NULL); - dbfull()->TEST_CompactRange(2, NULL, NULL); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); + dbfull()->TEST_CompactRange(2, nullptr, nullptr); ASSERT_EQ("2", FilesPerLevel()); // Do a memtable compaction. Before bug-fix, the compaction would @@ -1437,7 +1437,7 @@ TEST(DBTest, Fflush_Issue474) { static const int kNum = 100000; Random rnd(test::RandomSeed()); for (int i = 0; i < kNum; i++) { - fflush(NULL); + fflush(nullptr); ASSERT_OK(Put(RandomKey(&rnd), RandomString(&rnd, 100))); } } @@ -1495,7 +1495,7 @@ TEST(DBTest, CustomComparator) { Options new_options = CurrentOptions(); new_options.create_if_missing = true; new_options.comparator = &cmp; - new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.filter_policy = nullptr; // Cannot use bloom filters new_options.write_buffer_size = 1000; // Compact more often DestroyAndReopen(&new_options); ASSERT_OK(Put("[10]", "ten")); @@ -1550,7 +1550,7 @@ TEST(DBTest, ManualCompaction) { // Compact all MakeTables(1, "a", "z"); ASSERT_EQ("0,1,2", FilesPerLevel()); - db_->CompactRange(NULL, NULL); + db_->CompactRange(nullptr, nullptr); ASSERT_EQ("0,0,1", FilesPerLevel()); } @@ -1559,38 +1559,38 @@ TEST(DBTest, DBOpen_Options) { DestroyDB(dbname, Options()); // Does not exist, and create_if_missing == false: error - DB* db = NULL; + DB* db = nullptr; Options opts; opts.create_if_missing = false; Status s = DB::Open(opts, dbname, &db); - ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); - ASSERT_TRUE(db == NULL); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr); + ASSERT_TRUE(db == nullptr); // Does not exist, and create_if_missing == true: OK opts.create_if_missing = true; s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; // Does exist, and error_if_exists == true: error opts.create_if_missing = false; opts.error_if_exists = true; s = DB::Open(opts, dbname, &db); - ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); - ASSERT_TRUE(db == NULL); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr); + ASSERT_TRUE(db == nullptr); // Does exist, and error_if_exists == false: OK opts.create_if_missing = true; opts.error_if_exists = false; s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; } TEST(DBTest, DestroyEmptyDir) { @@ -1628,9 +1628,9 @@ TEST(DBTest, DestroyOpenDB) { Options opts; opts.create_if_missing = true; - DB* db = NULL; + DB* db = nullptr; ASSERT_OK(DB::Open(opts, dbname, &db)); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); // Must fail to destroy an open db. ASSERT_TRUE(env_->FileExists(dbname)); @@ -1638,7 +1638,7 @@ TEST(DBTest, DestroyOpenDB) { ASSERT_TRUE(env_->FileExists(dbname)); delete db; - db = NULL; + db = nullptr; // Should succeed destroying a closed db. ASSERT_OK(DestroyDB(dbname, Options())); @@ -1646,7 +1646,7 @@ TEST(DBTest, DestroyOpenDB) { } TEST(DBTest, Locking) { - DB* db2 = NULL; + DB* db2 = nullptr; Status s = DB::Open(CurrentOptions(), dbname_, &db2); ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; } @@ -1664,10 +1664,10 @@ TEST(DBTest, NoSpace) { env_->no_space_.Release_Store(env_); // Force out-of-space errors for (int i = 0; i < 10; i++) { for (int level = 0; level < config::kNumLevels-1; level++) { - dbfull()->TEST_CompactRange(level, NULL, NULL); + dbfull()->TEST_CompactRange(level, nullptr, nullptr); } } - env_->no_space_.Release_Store(NULL); + env_->no_space_.Release_Store(nullptr); ASSERT_LT(CountFiles(), num_files + 3); } @@ -1688,7 +1688,7 @@ TEST(DBTest, NonWritableFileSystem) { } } ASSERT_GT(errors, 0); - env_->non_writable_.Release_Store(NULL); + env_->non_writable_.Release_Store(nullptr); } TEST(DBTest, WriteSyncError) { @@ -1712,7 +1712,7 @@ TEST(DBTest, WriteSyncError) { ASSERT_EQ("NOT_FOUND", Get("k2")); // (d) make sync behave normally - env_->data_sync_error_.Release_Store(NULL); + env_->data_sync_error_.Release_Store(nullptr); // (e) Do a non-sync write; should fail w.sync = false; @@ -1753,11 +1753,11 @@ TEST(DBTest, ManifestWriteError) { // Merging compaction (will fail) error_type->Release_Store(env_); - dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail ASSERT_EQ("bar", Get("foo")); // Recovery: should not lose data - error_type->Release_Store(NULL); + error_type->Release_Store(nullptr); Reopen(&options); ASSERT_EQ("bar", Get("foo")); } @@ -1849,7 +1849,7 @@ TEST(DBTest, BloomFilter) { fprintf(stderr, "%d missing => %d reads\n", N, reads); ASSERT_LE(reads, 3*N/100); - env_->delay_data_sync_.Release_Store(NULL); + env_->delay_data_sync_.Release_Store(nullptr); Close(); delete options.block_cache; delete options.filter_policy; @@ -1883,7 +1883,7 @@ static void MTThreadBody(void* arg) { Random rnd(1000 + id); std::string value; char valbuf[1500]; - while (t->state->stop.Acquire_Load() == NULL) { + while (t->state->stop.Acquire_Load() == nullptr) { t->state->counter[id].Release_Store(reinterpret_cast(counter)); int key = rnd.Uniform(kNumKeys); @@ -1946,7 +1946,7 @@ TEST(DBTest, MultiThreaded) { // Stop the threads and wait for them to finish mt.stop.Release_Store(&mt); for (int id = 0; id < kNumThreads; id++) { - while (mt.thread_done[id].Acquire_Load() == NULL) { + while (mt.thread_done[id].Acquire_Load() == nullptr) { DelayMilliseconds(100); } } @@ -1978,7 +1978,7 @@ class ModelDB: public DB { return Status::NotFound(key); } virtual Iterator* NewIterator(const ReadOptions& options) { - if (options.snapshot == NULL) { + if (options.snapshot == nullptr) { KVMap* saved = new KVMap; *saved = map_; return new ModelIter(saved, true); @@ -2112,8 +2112,8 @@ TEST(DBTest, Randomized) { do { ModelDB model(CurrentOptions()); const int N = 10000; - const Snapshot* model_snap = NULL; - const Snapshot* db_snap = NULL; + const Snapshot* model_snap = nullptr; + const Snapshot* db_snap = nullptr; std::string k, v; for (int step = 0; step < N; step++) { if (step % 100 == 0) { @@ -2158,23 +2158,23 @@ TEST(DBTest, Randomized) { } if ((step % 100) == 0) { - ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, nullptr, nullptr)); ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); // Save a snapshot from each DB this time that we'll use next // time we compare things, to make sure the current state is // preserved with the snapshot - if (model_snap != NULL) model.ReleaseSnapshot(model_snap); - if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + if (model_snap != nullptr) model.ReleaseSnapshot(model_snap); + if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap); Reopen(); - ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, nullptr, nullptr)); model_snap = model.GetSnapshot(); db_snap = db_->GetSnapshot(); } } - if (model_snap != NULL) model.ReleaseSnapshot(model_snap); - if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + if (model_snap != nullptr) model.ReleaseSnapshot(model_snap); + if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap); } while (ChangeOptions()); } @@ -2188,15 +2188,15 @@ void BM_LogAndApply(int iters, int num_base_files) { std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; DestroyDB(dbname, Options()); - DB* db = NULL; + DB* db = nullptr; Options opts; opts.create_if_missing = true; Status s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; Env* env = Env::Default(); @@ -2205,7 +2205,7 @@ void BM_LogAndApply(int iters, int num_base_files) { InternalKeyComparator cmp(BytewiseComparator()); Options options; - VersionSet vset(dbname, &options, NULL, &cmp); + VersionSet vset(dbname, &options, nullptr, &cmp); bool save_manifest; ASSERT_OK(vset.Recover(&save_manifest)); VersionEdit vbase; diff --git a/db/dumpfile.cc b/db/dumpfile.cc index 2f1b5d91b3..941988b872 100644 --- a/db/dumpfile.cc +++ b/db/dumpfile.cc @@ -145,8 +145,8 @@ Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) { Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) { uint64_t file_size; - RandomAccessFile* file = NULL; - Table* table = NULL; + RandomAccessFile* file = nullptr; + Table* table = nullptr; Status s = env->GetFileSize(fname, &file_size); if (s.ok()) { s = env->NewRandomAccessFile(fname, &file); diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index caead37e13..7894999ccf 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -172,7 +172,7 @@ TestWritableFile::TestWritableFile(const FileState& state, target_(f), writable_file_opened_(true), env_(env) { - assert(f != NULL); + assert(f != nullptr); } TestWritableFile::~TestWritableFile() { @@ -378,7 +378,7 @@ class FaultInjectionTest { FaultInjectionTest() : env_(new FaultInjectionTestEnv), tiny_cache_(NewLRUCache(100)), - db_(NULL) { + db_(nullptr) { dbname_ = test::TmpDir() + "/fault_test"; DestroyDB(dbname_, Options()); // Destroy any db from earlier run options_.reuse_logs = true; @@ -457,14 +457,14 @@ class FaultInjectionTest { Status OpenDB() { delete db_; - db_ = NULL; + db_ = nullptr; env_->ResetState(); return DB::Open(options_, dbname_, &db_); } void CloseDB() { delete db_; - db_ = NULL; + db_ = nullptr; } void DeleteAllData() { @@ -493,7 +493,7 @@ class FaultInjectionTest { void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) { DeleteAllData(); Build(0, num_pre_sync); - db_->CompactRange(NULL, NULL); + db_->CompactRange(nullptr, nullptr); Build(num_pre_sync, num_post_sync); } diff --git a/db/log_reader.cc b/db/log_reader.cc index 48ae863e5d..19c4df6fdb 100644 --- a/db/log_reader.cc +++ b/db/log_reader.cc @@ -185,7 +185,7 @@ void Reader::ReportCorruption(uint64_t bytes, const char* reason) { } void Reader::ReportDrop(uint64_t bytes, const Status& reason) { - if (reporter_ != NULL && + if (reporter_ != nullptr && end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { reporter_->Corruption(static_cast(bytes), reason); } diff --git a/db/log_reader.h b/db/log_reader.h index 8389d61f8f..7dcce8e844 100644 --- a/db/log_reader.h +++ b/db/log_reader.h @@ -32,7 +32,7 @@ class Reader { // Create a reader that will return log records from "*file". // "*file" must remain live while this Reader is in use. // - // If "reporter" is non-NULL, it is notified whenever some data is + // If "reporter" is non-null, it is notified whenever some data is // dropped due to a detected corruption. "*reporter" must remain // live while this Reader is in use. // diff --git a/db/recovery_test.cc b/db/recovery_test.cc index e5a7c804a0..c852803a45 100644 --- a/db/recovery_test.cc +++ b/db/recovery_test.cc @@ -17,7 +17,7 @@ namespace leveldb { class RecoveryTest { public: - RecoveryTest() : env_(Env::Default()), db_(NULL) { + RecoveryTest() : env_(Env::Default()), db_(nullptr) { dbname_ = test::TmpDir() + "/recovery_test"; DestroyDB(dbname_, Options()); Open(); @@ -44,25 +44,25 @@ class RecoveryTest { void Close() { delete db_; - db_ = NULL; + db_ = nullptr; } - Status OpenWithStatus(Options* options = NULL) { + Status OpenWithStatus(Options* options = nullptr) { Close(); Options opts; - if (options != NULL) { + if (options != nullptr) { opts = *options; } else { opts.reuse_logs = true; // TODO(sanjay): test both ways opts.create_if_missing = true; } - if (opts.env == NULL) { + if (opts.env == nullptr) { opts.env = env_; } return DB::Open(opts, dbname_, &db_); } - void Open(Options* options = NULL) { + void Open(Options* options = nullptr) { ASSERT_OK(OpenWithStatus(options)); ASSERT_EQ(1, NumLogs()); } @@ -71,7 +71,7 @@ class RecoveryTest { return db_->Put(WriteOptions(), k, v); } - std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { std::string result; Status s = db_->Get(ReadOptions(), k, &result); if (s.IsNotFound()) { diff --git a/db/repair.cc b/db/repair.cc index c10da82a4c..df8dcd2c75 100644 --- a/db/repair.cc +++ b/db/repair.cc @@ -227,7 +227,7 @@ class Repairer { status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); delete iter; mem->Unref(); - mem = NULL; + mem = nullptr; if (status.ok()) { if (meta.file_size > 0) { table_numbers_.push_back(meta.number); @@ -350,13 +350,13 @@ class Repairer { } } delete builder; - builder = NULL; + builder = nullptr; if (s.ok()) { s = file->Close(); } delete file; - file = NULL; + file = nullptr; if (counter > 0 && s.ok()) { std::string orig = TableFileName(dbname_, t.meta.number); @@ -410,7 +410,7 @@ class Repairer { status = file->Close(); } delete file; - file = NULL; + file = nullptr; if (!status.ok()) { env_->DeleteFile(tmp); @@ -438,14 +438,14 @@ class Repairer { // dir/lost/foo const char* slash = strrchr(fname.c_str(), '/'); std::string new_dir; - if (slash != NULL) { + if (slash != nullptr) { new_dir.assign(fname.data(), slash - fname.data()); } new_dir.append("/lost"); env_->CreateDir(new_dir); // Ignore error std::string new_file = new_dir; new_file.append("/"); - new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + new_file.append((slash == nullptr) ? fname.c_str() : slash + 1); Status s = env_->RenameFile(fname, new_file); Log(options_.info_log, "Archiving %s: %s\n", fname.c_str(), s.ToString().c_str()); diff --git a/db/skiplist.h b/db/skiplist.h index 8bd77764d8..b806ce0fb4 100644 --- a/db/skiplist.h +++ b/db/skiplist.h @@ -123,9 +123,9 @@ class SkipList { bool KeyIsAfterNode(const Key& key, Node* n) const; // Return the earliest node that comes at or after key. - // Return NULL if there is no such node. + // Return nullptr if there is no such node. // - // If prev is non-NULL, fills prev[level] with pointer to previous + // If prev is non-null, fills prev[level] with pointer to previous // node at "level" for every level in [0..max_height_-1]. Node* FindGreaterOrEqual(const Key& key, Node** prev) const; @@ -190,12 +190,12 @@ SkipList::NewNode(const Key& key, int height) { template inline SkipList::Iterator::Iterator(const SkipList* list) { list_ = list; - node_ = NULL; + node_ = nullptr; } template inline bool SkipList::Iterator::Valid() const { - return node_ != NULL; + return node_ != nullptr; } template @@ -217,13 +217,13 @@ inline void SkipList::Iterator::Prev() { assert(Valid()); node_ = list_->FindLessThan(node_->key); if (node_ == list_->head_) { - node_ = NULL; + node_ = nullptr; } } template inline void SkipList::Iterator::Seek(const Key& target) { - node_ = list_->FindGreaterOrEqual(target, NULL); + node_ = list_->FindGreaterOrEqual(target, nullptr); } template @@ -235,7 +235,7 @@ template inline void SkipList::Iterator::SeekToLast() { node_ = list_->FindLast(); if (node_ == list_->head_) { - node_ = NULL; + node_ = nullptr; } } @@ -254,8 +254,8 @@ int SkipList::RandomHeight() { template bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { - // NULL n is considered infinite - return (n != NULL) && (compare_(n->key, key) < 0); + // null n is considered infinite + return (n != nullptr) && (compare_(n->key, key) < 0); } template @@ -269,7 +269,7 @@ typename SkipList::Node* SkipList::FindGreaterOr // Keep searching in this list x = next; } else { - if (prev != NULL) prev[level] = x; + if (prev != nullptr) prev[level] = x; if (level == 0) { return next; } else { @@ -288,7 +288,7 @@ SkipList::FindLessThan(const Key& key) const { while (true) { assert(x == head_ || compare_(x->key, key) < 0); Node* next = x->Next(level); - if (next == NULL || compare_(next->key, key) >= 0) { + if (next == nullptr || compare_(next->key, key) >= 0) { if (level == 0) { return x; } else { @@ -308,7 +308,7 @@ typename SkipList::Node* SkipList::FindLast() int level = GetMaxHeight() - 1; while (true) { Node* next = x->Next(level); - if (next == NULL) { + if (next == nullptr) { if (level == 0) { return x; } else { @@ -329,7 +329,7 @@ SkipList::SkipList(Comparator cmp, Arena* arena) max_height_(reinterpret_cast(1)), rnd_(0xdeadbeef) { for (int i = 0; i < kMaxHeight; i++) { - head_->SetNext(i, NULL); + head_->SetNext(i, nullptr); } } @@ -341,7 +341,7 @@ void SkipList::Insert(const Key& key) { Node* x = FindGreaterOrEqual(key, prev); // Our data structure does not allow duplicate insertion - assert(x == NULL || !Equal(key, x->key)); + assert(x == nullptr || !Equal(key, x->key)); int height = RandomHeight(); if (height > GetMaxHeight()) { @@ -353,9 +353,9 @@ void SkipList::Insert(const Key& key) { // It is ok to mutate max_height_ without any synchronization // with concurrent readers. A concurrent reader that observes // the new value of max_height_ will see either the old value of - // new level pointers from head_ (NULL), or a new value set in + // new level pointers from head_ (nullptr), or a new value set in // the loop below. In the former case the reader will - // immediately drop to the next level since NULL sorts after all + // immediately drop to the next level since nullptr sorts after all // keys. In the latter case the reader will use the new node. max_height_.NoBarrier_Store(reinterpret_cast(height)); } @@ -371,8 +371,8 @@ void SkipList::Insert(const Key& key) { template bool SkipList::Contains(const Key& key) const { - Node* x = FindGreaterOrEqual(key, NULL); - if (x != NULL && Equal(key, x->key)) { + Node* x = FindGreaterOrEqual(key, nullptr); + if (x != nullptr && Equal(key, x->key)) { return true; } else { return false; diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index 90f9d0ea74..24e0887ad4 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -310,7 +310,7 @@ class TestState { explicit TestState(int s) : seed_(s), - quit_flag_(NULL), + quit_flag_(nullptr), state_(STARTING), state_cv_(&mu_) {} @@ -362,7 +362,7 @@ static void RunConcurrent(int run) { for (int i = 0; i < kSize; i++) { state.t_.WriteStep(&rnd); } - state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.quit_flag_.Release_Store(&state); // Any non-null arg will do state.Wait(TestState::DONE); } } diff --git a/db/table_cache.cc b/db/table_cache.cc index 6cf005b91f..7226d3b7a7 100644 --- a/db/table_cache.cc +++ b/db/table_cache.cc @@ -49,10 +49,10 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, EncodeFixed64(buf, file_number); Slice key(buf, sizeof(buf)); *handle = cache_->Lookup(key); - if (*handle == NULL) { + if (*handle == nullptr) { std::string fname = TableFileName(dbname_, file_number); - RandomAccessFile* file = NULL; - Table* table = NULL; + RandomAccessFile* file = nullptr; + Table* table = nullptr; s = env_->NewRandomAccessFile(fname, &file); if (!s.ok()) { std::string old_fname = SSTTableFileName(dbname_, file_number); @@ -65,7 +65,7 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, } if (!s.ok()) { - assert(table == NULL); + assert(table == nullptr); delete file; // We do not cache error results so that if the error is transient, // or somebody repairs the file, we recover automatically. @@ -83,11 +83,11 @@ Iterator* TableCache::NewIterator(const ReadOptions& options, uint64_t file_number, uint64_t file_size, Table** tableptr) { - if (tableptr != NULL) { - *tableptr = NULL; + if (tableptr != nullptr) { + *tableptr = nullptr; } - Cache::Handle* handle = NULL; + Cache::Handle* handle = nullptr; Status s = FindTable(file_number, file_size, &handle); if (!s.ok()) { return NewErrorIterator(s); @@ -96,7 +96,7 @@ Iterator* TableCache::NewIterator(const ReadOptions& options, Table* table = reinterpret_cast(cache_->Value(handle))->table; Iterator* result = table->NewIterator(options); result->RegisterCleanup(&UnrefEntry, cache_, handle); - if (tableptr != NULL) { + if (tableptr != nullptr) { *tableptr = table; } return result; @@ -108,7 +108,7 @@ Status TableCache::Get(const ReadOptions& options, const Slice& k, void* arg, void (*saver)(void*, const Slice&, const Slice&)) { - Cache::Handle* handle = NULL; + Cache::Handle* handle = nullptr; Status s = FindTable(file_number, file_size, &handle); if (s.ok()) { Table* t = reinterpret_cast(cache_->Value(handle))->table; diff --git a/db/table_cache.h b/db/table_cache.h index e9191dc9a7..ae8bee5e6d 100644 --- a/db/table_cache.h +++ b/db/table_cache.h @@ -25,15 +25,15 @@ class TableCache { // Return an iterator for the specified file number (the corresponding // file length must be exactly "file_size" bytes). If "tableptr" is - // non-NULL, also sets "*tableptr" to point to the Table object - // underlying the returned iterator, or NULL if no Table object underlies - // the returned iterator. The returned "*tableptr" object is owned by - // the cache and should not be deleted, and is valid for as long as the + // non-null, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or to nullptr if no Table object + // underlies the returned iterator. The returned "*tableptr" object is owned + // by the cache and should not be deleted, and is valid for as long as the // returned iterator is live. Iterator* NewIterator(const ReadOptions& options, uint64_t file_number, uint64_t file_size, - Table** tableptr = NULL); + Table** tableptr = nullptr); // If a seek to internal key "k" in specified file finds an entry, // call (*handle_result)(arg, found_key, found_value). diff --git a/db/version_edit.cc b/db/version_edit.cc index f10a2d58b2..b7a366d60e 100644 --- a/db/version_edit.cc +++ b/db/version_edit.cc @@ -109,7 +109,7 @@ static bool GetLevel(Slice* input, int* level) { Status VersionEdit::DecodeFrom(const Slice& src) { Clear(); Slice input = src; - const char* msg = NULL; + const char* msg = nullptr; uint32_t tag; // Temporary storage for parsing @@ -119,7 +119,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { Slice str; InternalKey key; - while (msg == NULL && GetVarint32(&input, &tag)) { + while (msg == nullptr && GetVarint32(&input, &tag)) { switch (tag) { case kComparator: if (GetLengthPrefixedSlice(&input, &str)) { @@ -198,12 +198,12 @@ Status VersionEdit::DecodeFrom(const Slice& src) { } } - if (msg == NULL && !input.empty()) { + if (msg == nullptr && !input.empty()) { msg = "invalid tag"; } Status result; - if (msg != NULL) { + if (msg != nullptr) { result = Status::Corruption("VersionEdit", msg); } return result; diff --git a/db/version_set.cc b/db/version_set.cc index 7022b274e1..02cc66f06f 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -106,15 +106,15 @@ int FindFile(const InternalKeyComparator& icmp, static bool AfterFile(const Comparator* ucmp, const Slice* user_key, const FileMetaData* f) { - // NULL user_key occurs before all keys and is therefore never after *f - return (user_key != NULL && + // null user_key occurs before all keys and is therefore never after *f + return (user_key != nullptr && ucmp->Compare(*user_key, f->largest.user_key()) > 0); } static bool BeforeFile(const Comparator* ucmp, const Slice* user_key, const FileMetaData* f) { - // NULL user_key occurs after all keys and is therefore never before *f - return (user_key != NULL && + // null user_key occurs after all keys and is therefore never before *f + return (user_key != nullptr && ucmp->Compare(*user_key, f->smallest.user_key()) < 0); } @@ -141,7 +141,7 @@ bool SomeFileOverlapsRange( // Binary search over file list uint32_t index = 0; - if (smallest_user_key != NULL) { + if (smallest_user_key != nullptr) { // Find the earliest possible internal key for smallest_user_key InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); index = FindFile(icmp, files, small.Encode()); @@ -338,9 +338,9 @@ Status Version::Get(const ReadOptions& options, const Comparator* ucmp = vset_->icmp_.user_comparator(); Status s; - stats->seek_file = NULL; + stats->seek_file = nullptr; stats->seek_file_level = -1; - FileMetaData* last_file_read = NULL; + FileMetaData* last_file_read = nullptr; int last_file_read_level = -1; // We can search level-by-level since entries never hop across @@ -374,13 +374,13 @@ Status Version::Get(const ReadOptions& options, // Binary search to find earliest index whose largest key >= ikey. uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); if (index >= num_files) { - files = NULL; + files = nullptr; num_files = 0; } else { tmp2 = files[index]; if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { // All of "tmp2" is past any data for user_key - files = NULL; + files = nullptr; num_files = 0; } else { files = &tmp2; @@ -390,7 +390,7 @@ Status Version::Get(const ReadOptions& options, } for (uint32_t i = 0; i < num_files; ++i) { - if (last_file_read != NULL && stats->seek_file == NULL) { + if (last_file_read != nullptr && stats->seek_file == nullptr) { // We have had more than one seek for this read. Charge the 1st file. stats->seek_file = last_file_read; stats->seek_file_level = last_file_read_level; @@ -430,9 +430,9 @@ Status Version::Get(const ReadOptions& options, bool Version::UpdateStats(const GetStats& stats) { FileMetaData* f = stats.seek_file; - if (f != NULL) { + if (f != nullptr) { f->allowed_seeks--; - if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + if (f->allowed_seeks <= 0 && file_to_compact_ == nullptr) { file_to_compact_ = f; file_to_compact_level_ = stats.seek_file_level; return true; @@ -537,10 +537,10 @@ void Version::GetOverlappingInputs( assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; - if (begin != NULL) { + if (begin != nullptr) { user_begin = begin->user_key(); } - if (end != NULL) { + if (end != nullptr) { user_end = end->user_key(); } const Comparator* user_cmp = vset_->icmp_.user_comparator(); @@ -548,20 +548,21 @@ void Version::GetOverlappingInputs( FileMetaData* f = files_[level][i++]; const Slice file_start = f->smallest.user_key(); const Slice file_limit = f->largest.user_key(); - if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + if (begin != nullptr && user_cmp->Compare(file_limit, user_begin) < 0) { // "f" is completely before specified range; skip it - } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + } else if (end != nullptr && user_cmp->Compare(file_start, user_end) > 0) { // "f" is completely after specified range; skip it } else { inputs->push_back(f); if (level == 0) { // Level-0 files may overlap each other. So check if the newly // added file has expanded the range. If so, restart search. - if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + if (begin != nullptr && user_cmp->Compare(file_start, user_begin) < 0) { user_begin = file_start; inputs->clear(); i = 0; - } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + } else if (end != nullptr && user_cmp->Compare(file_limit, + user_end) > 0) { user_end = file_limit; inputs->clear(); i = 0; @@ -786,10 +787,10 @@ VersionSet::VersionSet(const std::string& dbname, last_sequence_(0), log_number_(0), prev_log_number_(0), - descriptor_file_(NULL), - descriptor_log_(NULL), + descriptor_file_(nullptr), + descriptor_log_(nullptr), dummy_versions_(this), - current_(NULL) { + current_(nullptr) { AppendVersion(new Version(this)); } @@ -804,7 +805,7 @@ void VersionSet::AppendVersion(Version* v) { // Make "v" current assert(v->refs_ == 0); assert(v != current_); - if (current_ != NULL) { + if (current_ != nullptr) { current_->Unref(); } current_ = v; @@ -844,10 +845,10 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { // a temporary file that contains a snapshot of the current version. std::string new_manifest_file; Status s; - if (descriptor_log_ == NULL) { + if (descriptor_log_ == nullptr) { // No reason to unlock *mu here since we only hit this path in the // first call to LogAndApply (when opening the database). - assert(descriptor_file_ == NULL); + assert(descriptor_file_ == nullptr); new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); edit->SetNextFile(next_file_number_); s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); @@ -893,8 +894,8 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { if (!new_manifest_file.empty()) { delete descriptor_log_; delete descriptor_file_; - descriptor_log_ = NULL; - descriptor_file_ = NULL; + descriptor_log_ = nullptr; + descriptor_file_ = nullptr; env_->DeleteFile(new_manifest_file); } } @@ -986,7 +987,7 @@ Status VersionSet::Recover(bool *save_manifest) { } } delete file; - file = NULL; + file = nullptr; if (s.ok()) { if (!have_next_file) { @@ -1044,12 +1045,12 @@ bool VersionSet::ReuseManifest(const std::string& dscname, return false; } - assert(descriptor_file_ == NULL); - assert(descriptor_log_ == NULL); + assert(descriptor_file_ == nullptr); + assert(descriptor_log_ == nullptr); Status r = env_->NewAppendableFile(dscname, &descriptor_file_); if (!r.ok()) { Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str()); - assert(descriptor_file_ == NULL); + assert(descriptor_file_ == nullptr); return false; } @@ -1176,7 +1177,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { Table* tableptr; Iterator* iter = table_cache_->NewIterator( ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); - if (tableptr != NULL) { + if (tableptr != nullptr) { result += tableptr->ApproximateOffsetOf(ikey.Encode()); } delete iter; @@ -1299,7 +1300,7 @@ Compaction* VersionSet::PickCompaction() { // We prefer compactions triggered by too much data in a level over // the compactions triggered by seeks. const bool size_compaction = (current_->compaction_score_ >= 1); - const bool seek_compaction = (current_->file_to_compact_ != NULL); + const bool seek_compaction = (current_->file_to_compact_ != nullptr); if (size_compaction) { level = current_->compaction_level_; assert(level >= 0); @@ -1324,7 +1325,7 @@ Compaction* VersionSet::PickCompaction() { c = new Compaction(options_, level); c->inputs_[0].push_back(current_->file_to_compact_); } else { - return NULL; + return nullptr; } c->input_version_ = current_; @@ -1414,7 +1415,7 @@ Compaction* VersionSet::CompactRange( std::vector inputs; current_->GetOverlappingInputs(level, begin, end, &inputs); if (inputs.empty()) { - return NULL; + return nullptr; } // Avoid compacting too much in one shot in case the range is large. @@ -1445,7 +1446,7 @@ Compaction* VersionSet::CompactRange( Compaction::Compaction(const Options* options, int level) : level_(level), max_output_file_size_(MaxFileSizeForLevel(options, level)), - input_version_(NULL), + input_version_(nullptr), grandparent_index_(0), seen_key_(false), overlapped_bytes_(0) { @@ -1455,7 +1456,7 @@ Compaction::Compaction(const Options* options, int level) } Compaction::~Compaction() { - if (input_version_ != NULL) { + if (input_version_ != nullptr) { input_version_->Unref(); } } @@ -1523,9 +1524,9 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) { } void Compaction::ReleaseInputs() { - if (input_version_ != NULL) { + if (input_version_ != nullptr) { input_version_->Unref(); - input_version_ = NULL; + input_version_ = nullptr; } } diff --git a/db/version_set.h b/db/version_set.h index 80d448a7ed..77b98950ab 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -45,8 +45,8 @@ int FindFile(const InternalKeyComparator& icmp, // Returns true iff some file in "files" overlaps the user key range // [*smallest,*largest]. -// smallest==NULL represents a key smaller than all keys in the DB. -// largest==NULL represents a key largest than all keys in the DB. +// smallest==nullptr represents a key smaller than all keys in the DB. +// largest==nullptr represents a key largest than all keys in the DB. // REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges // in sorted order. bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, @@ -90,14 +90,14 @@ class Version { void GetOverlappingInputs( int level, - const InternalKey* begin, // NULL means before all keys - const InternalKey* end, // NULL means after all keys + const InternalKey* begin, // nullptr means before all keys + const InternalKey* end, // nullptr means after all keys std::vector* inputs); // Returns true iff some file in the specified level overlaps // some part of [*smallest_user_key,*largest_user_key]. - // smallest_user_key==NULL represents a key smaller than all keys in the DB. - // largest_user_key==NULL represents a key largest than all keys in the DB. + // smallest_user_key==nullptr represents a key smaller than all the DB's keys. + // largest_user_key==nullptr represents a key largest than all the DB's keys. bool OverlapInLevel(int level, const Slice* smallest_user_key, const Slice* largest_user_key); @@ -148,7 +148,7 @@ class Version { explicit Version(VersionSet* vset) : vset_(vset), next_(this), prev_(this), refs_(0), - file_to_compact_(NULL), + file_to_compact_(nullptr), file_to_compact_level_(-1), compaction_score_(-1), compaction_level_(-1) { @@ -224,13 +224,13 @@ class VersionSet { uint64_t PrevLogNumber() const { return prev_log_number_; } // Pick level and inputs for a new compaction. - // Returns NULL if there is no compaction to be done. + // Returns nullptr if there is no compaction to be done. // Otherwise returns a pointer to a heap-allocated object that // describes the compaction. Caller should delete the result. Compaction* PickCompaction(); // Return a compaction object for compacting the range [begin,end] in - // the specified level. Returns NULL if there is nothing in that + // the specified level. Returns nullptr if there is nothing in that // level that overlaps the specified range. Caller should delete // the result. Compaction* CompactRange( @@ -249,7 +249,7 @@ class VersionSet { // Returns true iff some level needs a compaction. bool NeedsCompaction() const { Version* v = current_; - return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != nullptr); } // Add all files listed in any live version to *live. diff --git a/db/version_set_test.cc b/db/version_set_test.cc index 501e34d133..d21433e9a2 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -40,20 +40,20 @@ class FindFileTest { bool Overlaps(const char* smallest, const char* largest) { InternalKeyComparator cmp(BytewiseComparator()); - Slice s(smallest != NULL ? smallest : ""); - Slice l(largest != NULL ? largest : ""); + Slice s(smallest != nullptr ? smallest : ""); + Slice l(largest != nullptr ? largest : ""); return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, - (smallest != NULL ? &s : NULL), - (largest != NULL ? &l : NULL)); + (smallest != nullptr ? &s : nullptr), + (largest != nullptr ? &l : nullptr)); } }; TEST(FindFileTest, Empty) { ASSERT_EQ(0, Find("foo")); ASSERT_TRUE(! Overlaps("a", "z")); - ASSERT_TRUE(! Overlaps(NULL, "z")); - ASSERT_TRUE(! Overlaps("a", NULL)); - ASSERT_TRUE(! Overlaps(NULL, NULL)); + ASSERT_TRUE(! Overlaps(nullptr, "z")); + ASSERT_TRUE(! Overlaps("a", nullptr)); + ASSERT_TRUE(! Overlaps(nullptr, nullptr)); } TEST(FindFileTest, Single) { @@ -78,12 +78,12 @@ TEST(FindFileTest, Single) { ASSERT_TRUE(Overlaps("q", "q")); ASSERT_TRUE(Overlaps("q", "q1")); - ASSERT_TRUE(! Overlaps(NULL, "j")); - ASSERT_TRUE(! Overlaps("r", NULL)); - ASSERT_TRUE(Overlaps(NULL, "p")); - ASSERT_TRUE(Overlaps(NULL, "p1")); - ASSERT_TRUE(Overlaps("q", NULL)); - ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(! Overlaps(nullptr, "j")); + ASSERT_TRUE(! Overlaps("r", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, "p")); + ASSERT_TRUE(Overlaps(nullptr, "p1")); + ASSERT_TRUE(Overlaps("q", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, nullptr)); } @@ -130,19 +130,19 @@ TEST(FindFileTest, MultipleNullBoundaries) { Add("200", "250"); Add("300", "350"); Add("400", "450"); - ASSERT_TRUE(! Overlaps(NULL, "149")); - ASSERT_TRUE(! Overlaps("451", NULL)); - ASSERT_TRUE(Overlaps(NULL, NULL)); - ASSERT_TRUE(Overlaps(NULL, "150")); - ASSERT_TRUE(Overlaps(NULL, "199")); - ASSERT_TRUE(Overlaps(NULL, "200")); - ASSERT_TRUE(Overlaps(NULL, "201")); - ASSERT_TRUE(Overlaps(NULL, "400")); - ASSERT_TRUE(Overlaps(NULL, "800")); - ASSERT_TRUE(Overlaps("100", NULL)); - ASSERT_TRUE(Overlaps("200", NULL)); - ASSERT_TRUE(Overlaps("449", NULL)); - ASSERT_TRUE(Overlaps("450", NULL)); + ASSERT_TRUE(! Overlaps(nullptr, "149")); + ASSERT_TRUE(! Overlaps("451", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, nullptr)); + ASSERT_TRUE(Overlaps(nullptr, "150")); + ASSERT_TRUE(Overlaps(nullptr, "199")); + ASSERT_TRUE(Overlaps(nullptr, "200")); + ASSERT_TRUE(Overlaps(nullptr, "201")); + ASSERT_TRUE(Overlaps(nullptr, "400")); + ASSERT_TRUE(Overlaps(nullptr, "800")); + ASSERT_TRUE(Overlaps("100", nullptr)); + ASSERT_TRUE(Overlaps("200", nullptr)); + ASSERT_TRUE(Overlaps("449", nullptr)); + ASSERT_TRUE(Overlaps("450", nullptr)); } TEST(FindFileTest, OverlapSequenceChecks) { diff --git a/doc/bench/db_bench_sqlite3.cc b/doc/bench/db_bench_sqlite3.cc index e63aaa8dcc..7e05de2be4 100644 --- a/doc/bench/db_bench_sqlite3.cc +++ b/doc/bench/db_bench_sqlite3.cc @@ -76,7 +76,7 @@ static bool FLAGS_transaction = true; static bool FLAGS_WAL_enabled = true; // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; inline static void ExecErrorCheck(int status, char *err_msg) { @@ -107,7 +107,8 @@ inline static void WalCheckpoint(sqlite3* db_) { // Flush all writes to disk if (FLAGS_WAL_enabled) { - sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr, + nullptr); } } @@ -207,18 +208,18 @@ class Benchmark { fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -313,7 +314,7 @@ class Benchmark { }; Benchmark() - : db_(NULL), + : db_(nullptr), db_num_(0), num_(FLAGS_num), reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), @@ -345,12 +346,12 @@ class Benchmark { Open(); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -415,11 +416,11 @@ class Benchmark { } void Open() { - assert(db_ == NULL); + assert(db_ == nullptr); int status; char file_name[100]; - char* err_msg = NULL; + char* err_msg = nullptr; db_num_++; // Open database @@ -439,7 +440,7 @@ class Benchmark { char cache_size[100]; snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", FLAGS_num_pages); - status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); // FLAGS_page_size is defaulted to 1024 @@ -447,7 +448,7 @@ class Benchmark { char page_size[100]; snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", FLAGS_page_size); - status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } @@ -457,9 +458,10 @@ class Benchmark { // LevelDB's default cache size is a combined 4 MB std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; - status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); - status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + status = sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, + &err_msg); ExecErrorCheck(status, err_msg); } @@ -470,7 +472,8 @@ class Benchmark { std::string stmt_array[] = { locking_stmt, create_stmt }; int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); for (int i = 0; i < stmt_array_length; i++) { - status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + status = sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, + &err_msg); ExecErrorCheck(status, err_msg); } } @@ -484,7 +487,7 @@ class Benchmark { return; } sqlite3_close(db_); - db_ = NULL; + db_ = nullptr; Open(); Start(); } @@ -495,7 +498,7 @@ class Benchmark { message_ = msg; } - char* err_msg = NULL; + char* err_msg = nullptr; int status; sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; @@ -506,18 +509,18 @@ class Benchmark { // Check for synchronous flag in options std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF"; - status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); // Preparing sqlite3 statements status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, - &replace_stmt, NULL); + &replace_stmt, nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, - &begin_trans_stmt, NULL); + &begin_trans_stmt, nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, NULL); + &end_trans_stmt, nullptr); ErrorCheck(status); bool transaction = (entries_per_batch > 1); @@ -588,12 +591,12 @@ class Benchmark { // Preparing sqlite3 statements status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, - &begin_trans_stmt, NULL); + &begin_trans_stmt, nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, NULL); + &end_trans_stmt, nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr); ErrorCheck(status); bool transaction = (entries_per_batch > 1); @@ -651,7 +654,7 @@ class Benchmark { sqlite3_stmt *pStmt; std::string read_str = "SELECT * FROM test ORDER BY key"; - status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr); ErrorCheck(status); for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); @@ -706,7 +709,7 @@ int main(int argc, char** argv) { } // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { + if (FLAGS_db == nullptr) { leveldb::Env::Default()->GetTestDirectory(&default_db_path); default_db_path += "/dbbench"; FLAGS_db = default_db_path.c_str(); diff --git a/doc/bench/db_bench_tree_db.cc b/doc/bench/db_bench_tree_db.cc index 4ca381f11f..9f8fb9011b 100644 --- a/doc/bench/db_bench_tree_db.cc +++ b/doc/bench/db_bench_tree_db.cc @@ -69,7 +69,7 @@ static bool FLAGS_use_existing_db = false; static bool FLAGS_compression = true; // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; inline static void DBSynchronize(kyotocabinet::TreeDB* db_) @@ -183,18 +183,18 @@ class Benchmark { kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -289,7 +289,7 @@ class Benchmark { }; Benchmark() - : db_(NULL), + : db_(nullptr), num_(FLAGS_num), reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), bytes_(0), @@ -321,12 +321,12 @@ class Benchmark { Open(false); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -387,7 +387,7 @@ class Benchmark { private: void Open(bool sync) { - assert(db_ == NULL); + assert(db_ == nullptr); // Initialize db_ db_ = new kyotocabinet::TreeDB(); @@ -430,7 +430,7 @@ class Benchmark { return; } delete db_; - db_ = NULL; + db_ = nullptr; Open(sync); Start(); // Do not count time taken to destroy/open } @@ -516,7 +516,7 @@ int main(int argc, char** argv) { } // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { + if (FLAGS_db == nullptr) { leveldb::Env::Default()->GetTestDirectory(&default_db_path); default_db_path += "/dbbench"; FLAGS_db = default_db_path.c_str(); diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index 43b009d3db..d44627b8c2 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -246,7 +246,7 @@ class InMemoryEnv : public EnvWrapper { SequentialFile** result) { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { - *result = NULL; + *result = nullptr; return Status::IOError(fname, "File not found"); } @@ -258,7 +258,7 @@ class InMemoryEnv : public EnvWrapper { RandomAccessFile** result) { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { - *result = NULL; + *result = nullptr; return Status::IOError(fname, "File not found"); } @@ -286,7 +286,7 @@ class InMemoryEnv : public EnvWrapper { MutexLock lock(&mutex_); FileState** sptr = &file_map_[fname]; FileState* file = *sptr; - if (file == NULL) { + if (file == nullptr) { file = new FileState(); file->Ref(); } diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h index 145ad69acb..e416ea5e84 100644 --- a/include/leveldb/cache.h +++ b/include/leveldb/cache.h @@ -56,7 +56,7 @@ class LEVELDB_EXPORT Cache { virtual Handle* Insert(const Slice& key, void* value, size_t charge, void (*deleter)(const Slice& key, void* value)) = 0; - // If the cache has no mapping for "key", returns NULL. + // If the cache has no mapping for "key", returns nullptr. // // Else return a handle that corresponds to the mapping. The caller // must call this->Release(handle) when the returned mapping is no diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 092a154705..84c32bc1bb 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -47,7 +47,7 @@ class LEVELDB_EXPORT DB { // Open the database with the specified "name". // Stores a pointer to a heap-allocated database in *dbptr and returns // OK on success. - // Stores NULL in *dbptr and returns a non-OK status on error. + // Stores nullptr in *dbptr and returns a non-OK status on error. // Caller should delete *dbptr when it is no longer needed. static Status Open(const Options& options, const std::string& name, @@ -141,10 +141,10 @@ class LEVELDB_EXPORT DB { // needed to access the data. This operation should typically only // be invoked by users who understand the underlying implementation. // - // begin==NULL is treated as a key before all keys in the database. - // end==NULL is treated as a key after all keys in the database. + // begin==nullptr is treated as a key before all keys in the database. + // end==nullptr is treated as a key after all keys in the database. // Therefore the following call will compact the entire database: - // db->CompactRange(NULL, NULL); + // db->CompactRange(nullptr, nullptr); virtual void CompactRange(const Slice* begin, const Slice* end) = 0; }; diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 54c9e3b9e8..87dc06e63d 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -47,7 +47,7 @@ class LEVELDB_EXPORT Env { // Create a brand new sequentially-readable file with the specified name. // On success, stores a pointer to the new file in *result and returns OK. - // On failure stores NULL in *result and returns non-OK. If the file does + // On failure stores nullptr in *result and returns non-OK. If the file does // not exist, returns a non-OK status. Implementations should return a // NotFound status when the file does not exist. // @@ -57,7 +57,7 @@ class LEVELDB_EXPORT Env { // Create a brand new random access read-only file with the // specified name. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and + // *result and returns OK. On failure stores nullptr in *result and // returns non-OK. If the file does not exist, returns a non-OK // status. Implementations should return a NotFound status when the file does // not exist. @@ -69,7 +69,7 @@ class LEVELDB_EXPORT Env { // Create an object that writes to a new file with the specified // name. Deletes any existing file with the same name and creates a // new file. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and + // *result and returns OK. On failure stores nullptr in *result and // returns non-OK. // // The returned file will only be accessed by one thread at a time. @@ -79,7 +79,7 @@ class LEVELDB_EXPORT Env { // Create an object that either appends to an existing file, or // writes to a new file (if the file does not exist to begin with). // On success, stores a pointer to the new file in *result and - // returns OK. On failure stores NULL in *result and returns + // returns OK. On failure stores nullptr in *result and returns // non-OK. // // The returned file will only be accessed by one thread at a time. @@ -117,7 +117,7 @@ class LEVELDB_EXPORT Env { const std::string& target) = 0; // Lock the specified file. Used to prevent concurrent access to - // the same db by multiple processes. On failure, stores NULL in + // the same db by multiple processes. On failure, stores nullptr in // *lock and returns non-OK. // // On success, stores a pointer to the object that represents the @@ -264,7 +264,7 @@ class LEVELDB_EXPORT FileLock { virtual ~FileLock(); }; -// Log the specified data to *info_log if info_log is non-NULL. +// Log the specified data to *info_log if info_log is non-null. void Log(Logger* info_log, const char* format, ...) # if defined(__GNUC__) || defined(__clang__) __attribute__((__format__ (__printf__, 2, 3))) diff --git a/include/leveldb/options.h b/include/leveldb/options.h index 1c4292157c..b6ddbd85e3 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -63,9 +63,9 @@ struct LEVELDB_EXPORT Options { Env* env; // Any internal progress/error information generated by the db will - // be written to info_log if it is non-NULL, or to a file stored - // in the same directory as the DB contents if info_log is NULL. - // Default: NULL + // be written to info_log if it is non-null, or to a file stored + // in the same directory as the DB contents if info_log is null. + // Default: nullptr Logger* info_log; // ------------------- @@ -93,9 +93,9 @@ struct LEVELDB_EXPORT Options { // Control over blocks (user data is stored in a set of blocks, and // a block is the unit of reading from disk). - // If non-NULL, use the specified cache for blocks. - // If NULL, leveldb will automatically create and use an 8MB internal cache. - // Default: NULL + // If non-null, use the specified cache for blocks. + // If null, leveldb will automatically create and use an 8MB internal cache. + // Default: nullptr Cache* block_cache; // Approximate size of user data packed per block. Note that the @@ -147,11 +147,11 @@ struct LEVELDB_EXPORT Options { // Default: currently false, but may become true later. bool reuse_logs; - // If non-NULL, use the specified filter policy to reduce disk reads. + // If non-null, use the specified filter policy to reduce disk reads. // Many applications will benefit from passing the result of // NewBloomFilterPolicy() here. // - // Default: NULL + // Default: nullptr const FilterPolicy* filter_policy; // Create an Options object with default values for all fields. @@ -170,17 +170,17 @@ struct LEVELDB_EXPORT ReadOptions { // Default: true bool fill_cache; - // If "snapshot" is non-NULL, read as of the supplied snapshot + // If "snapshot" is non-null, read as of the supplied snapshot // (which must belong to the DB that is being read and which must - // not have been released). If "snapshot" is NULL, use an implicit + // not have been released). If "snapshot" is null, use an implicit // snapshot of the state at the beginning of this read operation. - // Default: NULL + // Default: nullptr const Snapshot* snapshot; ReadOptions() : verify_checksums(false), fill_cache(true), - snapshot(NULL) { + snapshot(nullptr) { } }; diff --git a/include/leveldb/status.h b/include/leveldb/status.h index 42ad4bbbcc..39d692df36 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -22,7 +22,7 @@ namespace leveldb { class LEVELDB_EXPORT Status { public: // Create a success status. - Status() : state_(NULL) { } + Status() : state_(nullptr) { } ~Status() { delete[] state_; } // Copy the specified status. @@ -50,7 +50,7 @@ class LEVELDB_EXPORT Status { } // Returns true iff the status indicates success. - bool ok() const { return (state_ == NULL); } + bool ok() const { return (state_ == nullptr); } // Returns true iff the status indicates a NotFound error. bool IsNotFound() const { return code() == kNotFound; } @@ -72,7 +72,7 @@ class LEVELDB_EXPORT Status { std::string ToString() const; private: - // OK status has a NULL state_. Otherwise, state_ is a new[] array + // OK status has a null state_. Otherwise, state_ is a new[] array // of the following form: // state_[0..3] == length of message // state_[4] == code @@ -89,7 +89,7 @@ class LEVELDB_EXPORT Status { }; Code code() const { - return (state_ == NULL) ? kOk : static_cast(state_[4]); + return (state_ == nullptr) ? kOk : static_cast(state_[4]); } Status(Code code, const Slice& msg, const Slice& msg2); @@ -97,14 +97,14 @@ class LEVELDB_EXPORT Status { }; inline Status::Status(const Status& s) { - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); } inline void Status::operator=(const Status& s) { // The following condition catches both aliasing (when this == &s), // and the common case where both s and *this are ok. if (state_ != s.state_) { delete[] state_; - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); } } diff --git a/include/leveldb/table.h b/include/leveldb/table.h index 83078d2757..e9f6641562 100644 --- a/include/leveldb/table.h +++ b/include/leveldb/table.h @@ -31,7 +31,7 @@ class LEVELDB_EXPORT Table { // If successful, returns ok and sets "*table" to the newly opened // table. The client should delete "*table" when no longer needed. // If there was an error while initializing the table, sets "*table" - // to NULL and returns a non-ok status. Does not take ownership of + // to nullptr and returns a non-ok status. Does not take ownership of // "*source", but the client must ensure that "source" remains live // for the duration of the returned table's lifetime. // diff --git a/port/port_posix.cc b/port/port_posix.cc index 30e8007ae3..04095bbf85 100644 --- a/port/port_posix.cc +++ b/port/port_posix.cc @@ -18,7 +18,7 @@ static void PthreadCall(const char* label, int result) { } } -Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } +Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr)); } Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } @@ -28,7 +28,7 @@ void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } CondVar::CondVar(Mutex* mu) : mu_(mu) { - PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); + PthreadCall("init cv", pthread_cond_init(&cv_, nullptr)); } CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } diff --git a/table/block.cc b/table/block.cc index 43e402c9c0..6fdfdeae59 100644 --- a/table/block.cc +++ b/table/block.cc @@ -48,13 +48,13 @@ Block::~Block() { // and the length of the value in "*shared", "*non_shared", and // "*value_length", respectively. Will not dereference past "limit". // -// If any errors are detected, returns NULL. Otherwise, returns a +// If any errors are detected, returns nullptr. Otherwise, returns a // pointer to the key delta (just past the three decoded values). static inline const char* DecodeEntry(const char* p, const char* limit, uint32_t* shared, uint32_t* non_shared, uint32_t* value_length) { - if (limit - p < 3) return NULL; + if (limit - p < 3) return nullptr; *shared = reinterpret_cast(p)[0]; *non_shared = reinterpret_cast(p)[1]; *value_length = reinterpret_cast(p)[2]; @@ -62,13 +62,13 @@ static inline const char* DecodeEntry(const char* p, const char* limit, // Fast path: all three values are encoded in one byte each p += 3; } else { - if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; - if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; - if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, shared)) == nullptr) return nullptr; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == nullptr) return nullptr; + if ((p = GetVarint32Ptr(p, limit, value_length)) == nullptr) return nullptr; } if (static_cast(limit - p) < (*non_shared + *value_length)) { - return NULL; + return nullptr; } return p; } @@ -174,7 +174,7 @@ class Block::Iter : public Iterator { const char* key_ptr = DecodeEntry(data_ + region_offset, data_ + restarts_, &shared, &non_shared, &value_length); - if (key_ptr == NULL || (shared != 0)) { + if (key_ptr == nullptr || (shared != 0)) { CorruptionError(); return; } @@ -237,7 +237,7 @@ class Block::Iter : public Iterator { // Decode next entry uint32_t shared, non_shared, value_length; p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); - if (p == NULL || key_.size() < shared) { + if (p == nullptr || key_.size() < shared) { CorruptionError(); return false; } else { diff --git a/table/filter_block.cc b/table/filter_block.cc index 1ed5134170..ce0aa048e9 100644 --- a/table/filter_block.cc +++ b/table/filter_block.cc @@ -78,8 +78,8 @@ void FilterBlockBuilder::GenerateFilter() { FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, const Slice& contents) : policy_(policy), - data_(NULL), - offset_(NULL), + data_(nullptr), + offset_(nullptr), num_(0), base_lg_(0) { size_t n = contents.size(); diff --git a/table/iterator.cc b/table/iterator.cc index 3d1c87fdec..aff0e59fc3 100644 --- a/table/iterator.cc +++ b/table/iterator.cc @@ -7,14 +7,14 @@ namespace leveldb { Iterator::Iterator() { - cleanup_.function = NULL; - cleanup_.next = NULL; + cleanup_.function = nullptr; + cleanup_.next = nullptr; } Iterator::~Iterator() { - if (cleanup_.function != NULL) { + if (cleanup_.function != nullptr) { (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); - for (Cleanup* c = cleanup_.next; c != NULL; ) { + for (Cleanup* c = cleanup_.next; c != nullptr; ) { (*c->function)(c->arg1, c->arg2); Cleanup* next = c->next; delete c; @@ -24,9 +24,9 @@ Iterator::~Iterator() { } void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { - assert(func != NULL); + assert(func != nullptr); Cleanup* c; - if (cleanup_.function == NULL) { + if (cleanup_.function == nullptr) { c = &cleanup_; } else { c = new Cleanup; diff --git a/table/iterator_wrapper.h b/table/iterator_wrapper.h index f410c3fabe..f1814cab43 100644 --- a/table/iterator_wrapper.h +++ b/table/iterator_wrapper.h @@ -16,8 +16,8 @@ namespace leveldb { // cache locality. class IteratorWrapper { public: - IteratorWrapper(): iter_(NULL), valid_(false) { } - explicit IteratorWrapper(Iterator* iter): iter_(NULL) { + IteratorWrapper(): iter_(nullptr), valid_(false) { } + explicit IteratorWrapper(Iterator* iter): iter_(nullptr) { Set(iter); } ~IteratorWrapper() { delete iter_; } @@ -28,7 +28,7 @@ class IteratorWrapper { void Set(Iterator* iter) { delete iter_; iter_ = iter; - if (iter_ == NULL) { + if (iter_ == nullptr) { valid_ = false; } else { Update(); @@ -40,7 +40,7 @@ class IteratorWrapper { bool Valid() const { return valid_; } Slice key() const { assert(Valid()); return key_; } Slice value() const { assert(Valid()); return iter_->value(); } - // Methods below require iter() != NULL + // Methods below require iter() != nullptr Status status() const { assert(iter_); return iter_->status(); } void Next() { assert(iter_); iter_->Next(); Update(); } void Prev() { assert(iter_); iter_->Prev(); Update(); } diff --git a/table/merger.cc b/table/merger.cc index 2dde4dc21f..e079680807 100644 --- a/table/merger.cc +++ b/table/merger.cc @@ -17,7 +17,7 @@ class MergingIterator : public Iterator { : comparator_(comparator), children_(new IteratorWrapper[n]), n_(n), - current_(NULL), + current_(nullptr), direction_(kForward) { for (int i = 0; i < n; i++) { children_[i].Set(children[i]); @@ -29,7 +29,7 @@ class MergingIterator : public Iterator { } virtual bool Valid() const { - return (current_ != NULL); + return (current_ != nullptr); } virtual void SeekToFirst() { @@ -153,11 +153,11 @@ class MergingIterator : public Iterator { }; void MergingIterator::FindSmallest() { - IteratorWrapper* smallest = NULL; + IteratorWrapper* smallest = nullptr; for (int i = 0; i < n_; i++) { IteratorWrapper* child = &children_[i]; if (child->Valid()) { - if (smallest == NULL) { + if (smallest == nullptr) { smallest = child; } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { smallest = child; @@ -168,11 +168,11 @@ void MergingIterator::FindSmallest() { } void MergingIterator::FindLargest() { - IteratorWrapper* largest = NULL; + IteratorWrapper* largest = nullptr; for (int i = n_-1; i >= 0; i--) { IteratorWrapper* child = &children_[i]; if (child->Valid()) { - if (largest == NULL) { + if (largest == nullptr) { largest = child; } else if (comparator_->Compare(child->key(), largest->key()) > 0) { largest = child; diff --git a/table/table.cc b/table/table.cc index ff73ceedd9..8e737e1b50 100644 --- a/table/table.cc +++ b/table/table.cc @@ -39,7 +39,7 @@ Status Table::Open(const Options& options, RandomAccessFile* file, uint64_t size, Table** table) { - *table = NULL; + *table = nullptr; if (size < Footer::kEncodedLength) { return Status::Corruption("file is too short to be an sstable"); } @@ -74,8 +74,8 @@ Status Table::Open(const Options& options, rep->metaindex_handle = footer.metaindex_handle(); rep->index_block = index_block; rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); - rep->filter_data = NULL; - rep->filter = NULL; + rep->filter_data = nullptr; + rep->filter = nullptr; *table = new Table(rep); (*table)->ReadMeta(footer); } @@ -84,7 +84,7 @@ Status Table::Open(const Options& options, } void Table::ReadMeta(const Footer& footer) { - if (rep_->options.filter_policy == NULL) { + if (rep_->options.filter_policy == nullptr) { return; // Do not need any metadata } @@ -161,8 +161,8 @@ Iterator* Table::BlockReader(void* arg, const Slice& index_value) { Table* table = reinterpret_cast(arg); Cache* block_cache = table->rep_->options.block_cache; - Block* block = NULL; - Cache::Handle* cache_handle = NULL; + Block* block = nullptr; + Cache::Handle* cache_handle = nullptr; BlockHandle handle; Slice input = index_value; @@ -172,13 +172,13 @@ Iterator* Table::BlockReader(void* arg, if (s.ok()) { BlockContents contents; - if (block_cache != NULL) { + if (block_cache != nullptr) { char cache_key_buffer[16]; EncodeFixed64(cache_key_buffer, table->rep_->cache_id); EncodeFixed64(cache_key_buffer+8, handle.offset()); Slice key(cache_key_buffer, sizeof(cache_key_buffer)); cache_handle = block_cache->Lookup(key); - if (cache_handle != NULL) { + if (cache_handle != nullptr) { block = reinterpret_cast(block_cache->Value(cache_handle)); } else { s = ReadBlock(table->rep_->file, options, handle, &contents); @@ -199,10 +199,10 @@ Iterator* Table::BlockReader(void* arg, } Iterator* iter; - if (block != NULL) { + if (block != nullptr) { iter = block->NewIterator(table->rep_->options.comparator); - if (cache_handle == NULL) { - iter->RegisterCleanup(&DeleteBlock, block, NULL); + if (cache_handle == nullptr) { + iter->RegisterCleanup(&DeleteBlock, block, nullptr); } else { iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); } @@ -228,7 +228,7 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, Slice handle_value = iiter->value(); FilterBlockReader* filter = rep_->filter; BlockHandle handle; - if (filter != NULL && + if (filter != nullptr && handle.DecodeFrom(&handle_value).ok() && !filter->KeyMayMatch(handle.offset(), k)) { // Not found diff --git a/table/table_builder.cc b/table/table_builder.cc index 62002c84f2..444d4f96fb 100644 --- a/table/table_builder.cc +++ b/table/table_builder.cc @@ -53,7 +53,7 @@ struct TableBuilder::Rep { index_block(&index_block_options), num_entries(0), closed(false), - filter_block(opt.filter_policy == NULL ? NULL + filter_block(opt.filter_policy == nullptr ? nullptr : new FilterBlockBuilder(opt.filter_policy)), pending_index_entry(false) { index_block_options.block_restart_interval = 1; @@ -62,7 +62,7 @@ struct TableBuilder::Rep { TableBuilder::TableBuilder(const Options& options, WritableFile* file) : rep_(new Rep(options, file)) { - if (rep_->filter_block != NULL) { + if (rep_->filter_block != nullptr) { rep_->filter_block->StartBlock(0); } } @@ -106,7 +106,7 @@ void TableBuilder::Add(const Slice& key, const Slice& value) { r->pending_index_entry = false; } - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { r->filter_block->AddKey(key); } @@ -131,7 +131,7 @@ void TableBuilder::Flush() { r->pending_index_entry = true; r->status = r->file->Flush(); } - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { r->filter_block->StartBlock(r->offset); } } @@ -205,7 +205,7 @@ Status TableBuilder::Finish() { BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; // Write filter block - if (ok() && r->filter_block != NULL) { + if (ok() && r->filter_block != nullptr) { WriteRawBlock(r->filter_block->Finish(), kNoCompression, &filter_block_handle); } @@ -213,7 +213,7 @@ Status TableBuilder::Finish() { // Write metaindex block if (ok()) { BlockBuilder meta_index_block(&r->options); - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { // Add mapping from "filter.Name" to location of filter data std::string key = "filter."; key.append(r->options.filter_policy->Name()); diff --git a/table/table_test.cc b/table/table_test.cc index abf6e246ff..e47db3d0e7 100644 --- a/table/table_test.cc +++ b/table/table_test.cc @@ -172,7 +172,7 @@ class Constructor { virtual const KVMap& data() { return data_; } - virtual DB* db() const { return NULL; } // Overridden in DBConstructor + virtual DB* db() const { return nullptr; } // Overridden in DBConstructor private: KVMap data_; @@ -183,13 +183,13 @@ class BlockConstructor: public Constructor { explicit BlockConstructor(const Comparator* cmp) : Constructor(cmp), comparator_(cmp), - block_(NULL) { } + block_(nullptr) { } ~BlockConstructor() { delete block_; } virtual Status FinishImpl(const Options& options, const KVMap& data) { delete block_; - block_ = NULL; + block_ = nullptr; BlockBuilder builder(&options); for (KVMap::const_iterator it = data.begin(); @@ -222,7 +222,7 @@ class TableConstructor: public Constructor { public: TableConstructor(const Comparator* cmp) : Constructor(cmp), - source_(NULL), table_(NULL) { + source_(nullptr), table_(nullptr) { } ~TableConstructor() { Reset(); @@ -262,8 +262,8 @@ class TableConstructor: public Constructor { void Reset() { delete table_; delete source_; - table_ = NULL; - source_ = NULL; + table_ = nullptr; + source_ = nullptr; } StringSource* source_; @@ -351,7 +351,7 @@ class DBConstructor: public Constructor { explicit DBConstructor(const Comparator* cmp) : Constructor(cmp), comparator_(cmp) { - db_ = NULL; + db_ = nullptr; NewDB(); } ~DBConstructor() { @@ -359,7 +359,7 @@ class DBConstructor: public Constructor { } virtual Status FinishImpl(const Options& options, const KVMap& data) { delete db_; - db_ = NULL; + db_ = nullptr; NewDB(); for (KVMap::const_iterator it = data.begin(); it != data.end(); @@ -436,11 +436,11 @@ static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); class Harness { public: - Harness() : constructor_(NULL) { } + Harness() : constructor_(nullptr) { } void Init(const TestArgs& args) { delete constructor_; - constructor_ = NULL; + constructor_ = nullptr; options_ = Options(); options_.block_restart_interval = args.restart_interval; @@ -636,7 +636,7 @@ class Harness { } } - // Returns NULL if not running against a DB + // Returns nullptr if not running against a DB DB* db() const { return constructor_->db(); } private: diff --git a/table/two_level_iterator.cc b/table/two_level_iterator.cc index 7822ebab9c..4e6f420e80 100644 --- a/table/two_level_iterator.cc +++ b/table/two_level_iterator.cc @@ -46,7 +46,7 @@ class TwoLevelIterator: public Iterator { // It'd be nice if status() returned a const Status& instead of a Status if (!index_iter_.status().ok()) { return index_iter_.status(); - } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + } else if (data_iter_.iter() != nullptr && !data_iter_.status().ok()) { return data_iter_.status(); } else { return status_; @@ -67,8 +67,8 @@ class TwoLevelIterator: public Iterator { const ReadOptions options_; Status status_; IteratorWrapper index_iter_; - IteratorWrapper data_iter_; // May be NULL - // If data_iter_ is non-NULL, then "data_block_handle_" holds the + IteratorWrapper data_iter_; // May be nullptr + // If data_iter_ is non-null, then "data_block_handle_" holds the // "index_value" passed to block_function_ to create the data_iter_. std::string data_block_handle_; }; @@ -82,7 +82,7 @@ TwoLevelIterator::TwoLevelIterator( arg_(arg), options_(options), index_iter_(index_iter), - data_iter_(NULL) { + data_iter_(nullptr) { } TwoLevelIterator::~TwoLevelIterator() { @@ -91,21 +91,21 @@ TwoLevelIterator::~TwoLevelIterator() { void TwoLevelIterator::Seek(const Slice& target) { index_iter_.Seek(target); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.Seek(target); + if (data_iter_.iter() != nullptr) data_iter_.Seek(target); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToFirst() { index_iter_.SeekToFirst(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToLast() { index_iter_.SeekToLast(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToLast(); SkipEmptyDataBlocksBackward(); } @@ -123,42 +123,42 @@ void TwoLevelIterator::Prev() { void TwoLevelIterator::SkipEmptyDataBlocksForward() { - while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); return; } index_iter_.Next(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); } } void TwoLevelIterator::SkipEmptyDataBlocksBackward() { - while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); return; } index_iter_.Prev(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToLast(); } } void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { - if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + if (data_iter_.iter() != nullptr) SaveError(data_iter_.status()); data_iter_.Set(data_iter); } void TwoLevelIterator::InitDataBlock() { if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); } else { Slice handle = index_iter_.value(); - if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + if (data_iter_.iter() != nullptr && handle.compare(data_block_handle_) == 0) { // data_iter_ is already constructed with this iterator, so // no need to change anything } else { diff --git a/util/arena.cc b/util/arena.cc index 74078213ee..a0338bf192 100644 --- a/util/arena.cc +++ b/util/arena.cc @@ -10,7 +10,7 @@ namespace leveldb { static const int kBlockSize = 4096; Arena::Arena() : memory_usage_(0) { - alloc_ptr_ = NULL; // First allocation will allocate a block + alloc_ptr_ = nullptr; // First allocation will allocate a block alloc_bytes_remaining_ = 0; } diff --git a/util/cache.cc b/util/cache.cc index 10b7103d50..7cc2cea9ce 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -69,7 +69,7 @@ struct LRUHandle { // 4.4.3's builtin hashtable. class HandleTable { public: - HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); } ~HandleTable() { delete[] list_; } LRUHandle* Lookup(const Slice& key, uint32_t hash) { @@ -79,9 +79,9 @@ class HandleTable { LRUHandle* Insert(LRUHandle* h) { LRUHandle** ptr = FindPointer(h->key(), h->hash); LRUHandle* old = *ptr; - h->next_hash = (old == NULL ? NULL : old->next_hash); + h->next_hash = (old == nullptr ? nullptr : old->next_hash); *ptr = h; - if (old == NULL) { + if (old == nullptr) { ++elems_; if (elems_ > length_) { // Since each cache entry is fairly large, we aim for a small @@ -95,7 +95,7 @@ class HandleTable { LRUHandle* Remove(const Slice& key, uint32_t hash) { LRUHandle** ptr = FindPointer(key, hash); LRUHandle* result = *ptr; - if (result != NULL) { + if (result != nullptr) { *ptr = result->next_hash; --elems_; } @@ -114,7 +114,7 @@ class HandleTable { // pointer to the trailing slot in the corresponding linked list. LRUHandle** FindPointer(const Slice& key, uint32_t hash) { LRUHandle** ptr = &list_[hash & (length_ - 1)]; - while (*ptr != NULL && + while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) { ptr = &(*ptr)->next_hash; } @@ -131,7 +131,7 @@ class HandleTable { uint32_t count = 0; for (uint32_t i = 0; i < length_; i++) { LRUHandle* h = list_[i]; - while (h != NULL) { + while (h != nullptr) { LRUHandle* next = h->next_hash; uint32_t hash = h->hash; LRUHandle** ptr = &new_list[hash & (new_length - 1)]; @@ -255,7 +255,7 @@ void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) { Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { MutexLock l(&mutex_); LRUHandle* e = table_.Lookup(key, hash); - if (e != NULL) { + if (e != nullptr) { Ref(e); } return reinterpret_cast(e); @@ -290,7 +290,7 @@ Cache::Handle* LRUCache::Insert( FinishErase(table_.Insert(e)); } else { // don't cache. (capacity_==0 is supported and turns off caching.) // next is read by key() in an assert, so it must be initialized - e->next = NULL; + e->next = nullptr; } while (usage_ > capacity_ && lru_.next != &lru_) { LRUHandle* old = lru_.next; @@ -304,17 +304,17 @@ Cache::Handle* LRUCache::Insert( return reinterpret_cast(e); } -// If e != NULL, finish removing *e from the cache; it has already been removed -// from the hash table. Return whether e != NULL. Requires mutex_ held. +// If e != nullptr, finish removing *e from the cache; it has already been +// removed from the hash table. Return whether e != nullptr. bool LRUCache::FinishErase(LRUHandle* e) { - if (e != NULL) { + if (e != nullptr) { assert(e->in_cache); LRU_Remove(e); e->in_cache = false; usage_ -= e->charge; Unref(e); } - return e != NULL; + return e != nullptr; } void LRUCache::Erase(const Slice& key, uint32_t hash) { diff --git a/util/cache_test.cc b/util/cache_test.cc index 246ab8efdd..8647febb9d 100644 --- a/util/cache_test.cc +++ b/util/cache_test.cc @@ -47,8 +47,8 @@ class CacheTest { int Lookup(int key) { Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); - const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); - if (handle != NULL) { + const int r = (handle == nullptr) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != nullptr) { cache_->Release(handle); } return r; diff --git a/util/coding.cc b/util/coding.cc index 21e3186d5d..9e72613440 100644 --- a/util/coding.cc +++ b/util/coding.cc @@ -125,14 +125,14 @@ const char* GetVarint32PtrFallback(const char* p, return reinterpret_cast(p); } } - return NULL; + return nullptr; } bool GetVarint32(Slice* input, uint32_t* value) { const char* p = input->data(); const char* limit = p + input->size(); const char* q = GetVarint32Ptr(p, limit, value); - if (q == NULL) { + if (q == nullptr) { return false; } else { *input = Slice(q, limit - q); @@ -154,14 +154,14 @@ const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { return reinterpret_cast(p); } } - return NULL; + return nullptr; } bool GetVarint64(Slice* input, uint64_t* value) { const char* p = input->data(); const char* limit = p + input->size(); const char* q = GetVarint64Ptr(p, limit, value); - if (q == NULL) { + if (q == nullptr) { return false; } else { *input = Slice(q, limit - q); @@ -173,8 +173,8 @@ const char* GetLengthPrefixedSlice(const char* p, const char* limit, Slice* result) { uint32_t len; p = GetVarint32Ptr(p, limit, &len); - if (p == NULL) return NULL; - if (p + len > limit) return NULL; + if (p == nullptr) return nullptr; + if (p + len > limit) return nullptr; *result = Slice(p, len); return p + len; } diff --git a/util/coding.h b/util/coding.h index 1fb3d66154..f0fa2cb4db 100644 --- a/util/coding.h +++ b/util/coding.h @@ -35,7 +35,7 @@ bool GetLengthPrefixedSlice(Slice* input, Slice* result); // Pointer-based variants of GetVarint... These either store a value // in *v and return a pointer just past the parsed value, or return -// NULL on error. These routines only look at bytes in the range +// nullptr on error. These routines only look at bytes in the range // [p..limit-1] const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* v); const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v); diff --git a/util/coding_test.cc b/util/coding_test.cc index 22f6adce27..d315e19507 100644 --- a/util/coding_test.cc +++ b/util/coding_test.cc @@ -89,7 +89,7 @@ TEST(Coding, Varint32) { uint32_t actual; const char* start = p; p = GetVarint32Ptr(p, limit, &actual); - ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p != nullptr); ASSERT_EQ(expected, actual); ASSERT_EQ(VarintLength(actual), p - start); } @@ -124,19 +124,18 @@ TEST(Coding, Varint64) { uint64_t actual; const char* start = p; p = GetVarint64Ptr(p, limit, &actual); - ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p != nullptr); ASSERT_EQ(values[i], actual); ASSERT_EQ(VarintLength(actual), p - start); } ASSERT_EQ(p, limit); - } TEST(Coding, Varint32Overflow) { uint32_t result; std::string input("\x81\x82\x83\x84\x85\x11"); ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) - == NULL); + == nullptr); } TEST(Coding, Varint32Truncation) { @@ -145,9 +144,10 @@ TEST(Coding, Varint32Truncation) { PutVarint32(&s, large_value); uint32_t result; for (size_t len = 0; len < s.size() - 1; len++) { - ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_TRUE( + GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != nullptr); ASSERT_EQ(large_value, result); } @@ -155,7 +155,7 @@ TEST(Coding, Varint64Overflow) { uint64_t result; std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) - == NULL); + == nullptr); } TEST(Coding, Varint64Truncation) { @@ -164,9 +164,10 @@ TEST(Coding, Varint64Truncation) { PutVarint64(&s, large_value); uint64_t result; for (size_t len = 0; len < s.size() - 1; len++) { - ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_TRUE( + GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != nullptr); ASSERT_EQ(large_value, result); } diff --git a/util/env.cc b/util/env.cc index c58a0821ef..40a1363913 100644 --- a/util/env.cc +++ b/util/env.cc @@ -29,7 +29,7 @@ FileLock::~FileLock() { } void Log(Logger* info_log, const char* format, ...) { - if (info_log != NULL) { + if (info_log != nullptr) { va_list ap; va_start(ap, format); info_log->Logv(format, ap); diff --git a/util/env_posix.cc b/util/env_posix.cc index 4bfaf6ccde..e758d5f2ee 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -282,7 +282,7 @@ class PosixWritableFile : public WritableFile { const char* sep = strrchr(f, '/'); Slice basename; std::string dir; - if (sep == NULL) { + if (sep == nullptr) { dir = "."; basename = f; } else { @@ -390,7 +390,7 @@ class PosixEnv : public Env { SequentialFile** result) { int fd = open(fname.c_str(), O_RDONLY); if (fd < 0) { - *result = NULL; + *result = nullptr; return PosixError(fname, errno); } else { *result = new PosixSequentialFile(fname, fd); @@ -400,7 +400,7 @@ class PosixEnv : public Env { virtual Status NewRandomAccessFile(const std::string& fname, RandomAccessFile** result) { - *result = NULL; + *result = nullptr; Status s; int fd = open(fname.c_str(), O_RDONLY); if (fd < 0) { @@ -409,7 +409,7 @@ class PosixEnv : public Env { uint64_t size; s = GetFileSize(fname, &size); if (s.ok()) { - void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + void* base = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); if (base != MAP_FAILED) { *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); } else { @@ -431,7 +431,7 @@ class PosixEnv : public Env { Status s; int fd = open(fname.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); if (fd < 0) { - *result = NULL; + *result = nullptr; s = PosixError(fname, errno); } else { *result = new PosixWritableFile(fname, fd); @@ -444,7 +444,7 @@ class PosixEnv : public Env { Status s; int fd = open(fname.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); if (fd < 0) { - *result = NULL; + *result = nullptr; s = PosixError(fname, errno); } else { *result = new PosixWritableFile(fname, fd); @@ -460,11 +460,11 @@ class PosixEnv : public Env { std::vector* result) { result->clear(); DIR* d = opendir(dir.c_str()); - if (d == NULL) { + if (d == nullptr) { return PosixError(dir, errno); } struct dirent* entry; - while ((entry = readdir(d)) != NULL) { + while ((entry = readdir(d)) != nullptr) { result->push_back(entry->d_name); } closedir(d); @@ -516,7 +516,7 @@ class PosixEnv : public Env { } virtual Status LockFile(const std::string& fname, FileLock** lock) { - *lock = NULL; + *lock = nullptr; Status result; int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); if (fd < 0) { @@ -576,8 +576,8 @@ class PosixEnv : public Env { virtual Status NewLogger(const std::string& fname, Logger** result) { FILE* f = fopen(fname.c_str(), "w"); - if (f == NULL) { - *result = NULL; + if (f == nullptr) { + *result = nullptr; return PosixError(fname, errno); } else { *result = new PosixLogger(f, &PosixEnv::gettid); @@ -587,7 +587,7 @@ class PosixEnv : public Env { virtual uint64_t NowMicros() { struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; } @@ -607,7 +607,7 @@ class PosixEnv : public Env { void BGThread(); static void* BGThreadWrapper(void* arg) { reinterpret_cast(arg)->BGThread(); - return NULL; + return nullptr; } pthread_mutex_t mu_; @@ -657,8 +657,8 @@ PosixEnv::PosixEnv() : started_bgthread_(false), mmap_limit_(MaxMmaps()), fd_limit_(MaxOpenFiles()) { - PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); - PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); + PthreadCall("mutex_init", pthread_mutex_init(&mu_, nullptr)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, nullptr)); } void PosixEnv::Schedule(void (*function)(void*), void* arg) { @@ -669,7 +669,7 @@ void PosixEnv::Schedule(void (*function)(void*), void* arg) { started_bgthread_ = true; PthreadCall( "create thread", - pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + pthread_create(&bgthread_, nullptr, &PosixEnv::BGThreadWrapper, this)); } // If the queue is currently empty, the background thread may currently be @@ -713,7 +713,7 @@ static void* StartThreadWrapper(void* arg) { StartThreadState* state = reinterpret_cast(arg); state->user_function(state->arg); delete state; - return NULL; + return nullptr; } void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { @@ -722,7 +722,7 @@ void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { state->user_function = function; state->arg = arg; PthreadCall("start thread", - pthread_create(&t, NULL, &StartThreadWrapper, state)); + pthread_create(&t, nullptr, &StartThreadWrapper, state)); } } // namespace @@ -732,12 +732,12 @@ static Env* default_env; static void InitDefaultEnv() { default_env = new PosixEnv; } void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { - assert(default_env == NULL); + assert(default_env == nullptr); open_read_only_file_limit = limit; } void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { - assert(default_env == NULL); + assert(default_env == nullptr); mmap_limit = limit; } diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index 295f8ae440..e28df9a5ea 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -32,7 +32,7 @@ TEST(EnvPosixTest, TestOpenOnRead) { std::string test_file = test_dir + "/open_on_read.txt"; FILE* f = fopen(test_file.c_str(), "w"); - ASSERT_TRUE(f != NULL); + ASSERT_TRUE(f != nullptr); const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; fputs(kFileData, f); fclose(f); diff --git a/util/env_test.cc b/util/env_test.cc index fd89b4c8f2..070109b61c 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -77,14 +77,14 @@ TEST(EnvTest, ReadWrite) { } TEST(EnvTest, RunImmediately) { - port::AtomicPointer called (NULL); + port::AtomicPointer called(nullptr); env_->Schedule(&SetBool, &called); env_->SleepForMicroseconds(kDelayMicros); - ASSERT_TRUE(called.NoBarrier_Load() != NULL); + ASSERT_TRUE(called.NoBarrier_Load() != nullptr); } TEST(EnvTest, RunMany) { - port::AtomicPointer last_id (NULL); + port::AtomicPointer last_id(nullptr); struct CB { port::AtomicPointer* last_id_ptr; // Pointer to shared slot diff --git a/util/options.cc b/util/options.cc index b5e6227613..351fa394e2 100644 --- a/util/options.cc +++ b/util/options.cc @@ -15,16 +15,16 @@ Options::Options() error_if_exists(false), paranoid_checks(false), env(Env::Default()), - info_log(NULL), + info_log(nullptr), write_buffer_size(4<<20), max_open_files(1000), - block_cache(NULL), + block_cache(nullptr), block_size(4096), block_restart_interval(16), max_file_size(2<<20), compression(kSnappyCompression), reuse_logs(false), - filter_policy(NULL) { + filter_policy(nullptr) { } } // namespace leveldb diff --git a/util/posix_logger.h b/util/posix_logger.h index 9741b1afad..1909e61282 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -45,7 +45,7 @@ class PosixLogger : public Logger { char* limit = base + bufsize; struct timeval now_tv; - gettimeofday(&now_tv, NULL); + gettimeofday(&now_tv, nullptr); const time_t seconds = now_tv.tv_sec; struct tm t; localtime_r(&seconds, &t); diff --git a/util/status.cc b/util/status.cc index a44f35b314..5591381a10 100644 --- a/util/status.cc +++ b/util/status.cc @@ -34,7 +34,7 @@ Status::Status(Code code, const Slice& msg, const Slice& msg2) { } std::string Status::ToString() const { - if (state_ == NULL) { + if (state_ == nullptr) { return "OK"; } else { char tmp[30]; diff --git a/util/testharness.cc b/util/testharness.cc index 95f025f8fd..37ba410b62 100644 --- a/util/testharness.cc +++ b/util/testharness.cc @@ -26,7 +26,7 @@ std::vector* tests; } bool RegisterTest(const char* base, const char* name, void (*func)()) { - if (tests == NULL) { + if (tests == nullptr) { tests = new std::vector; } Test t; @@ -41,14 +41,14 @@ int RunAllTests() { const char* matcher = getenv("LEVELDB_TESTS"); int num = 0; - if (tests != NULL) { + if (tests != nullptr) { for (size_t i = 0; i < tests->size(); i++) { const Test& t = (*tests)[i]; - if (matcher != NULL) { + if (matcher != nullptr) { std::string name = t.base; name.push_back('.'); name.append(t.name); - if (strstr(name.c_str(), matcher) == NULL) { + if (strstr(name.c_str(), matcher) == nullptr) { continue; } } @@ -70,7 +70,7 @@ std::string TmpDir() { int RandomSeed() { const char* env = getenv("TEST_RANDOM_SEED"); - int result = (env != NULL ? atoi(env) : 301); + int result = (env != nullptr ? atoi(env) : 301); if (result <= 0) { result = 301; } diff --git a/util/testutil.h b/util/testutil.h index 8726bf7d84..dc77ac3767 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -40,7 +40,7 @@ class ErrorEnv : public EnvWrapper { WritableFile** result) { if (writable_file_error_) { ++num_writable_file_errors_; - *result = NULL; + *result = nullptr; return Status::IOError(fname, "fake error"); } return target()->NewWritableFile(fname, result); @@ -50,7 +50,7 @@ class ErrorEnv : public EnvWrapper { WritableFile** result) { if (writable_file_error_) { ++num_writable_file_errors_; - *result = NULL; + *result = nullptr; return Status::IOError(fname, "fake error"); } return target()->NewAppendableFile(fname, result); From 1cc8b10b8232e174d5bd1313959825727e03faa7 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 11 Apr 2018 12:27:57 -0700 Subject: [PATCH 090/174] Document the building process. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192490601 --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a010c50858..15fbdc227c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) # Features + * Keys and values are arbitrary byte arrays. * Data is stored sorted by key. * Callers can provide a custom comparison function to override the sort order. @@ -16,15 +17,30 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. # Documentation - [LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code. + [LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code. # Limitations + * This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes. * Only a single process (possibly multi-threaded) can access a particular database at a time. * There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library. +# Building + +This project supports [CMake](https://cmake.org/) out of the box. + +Quick start: + +```bash +mkdir -p build && cd build +cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . +``` + +Please see the CMake documentation and `CMakeLists.txt` for more advanced usage. + # Contributing to the leveldb Project + The leveldb project welcomes contributions. leveldb's primary goal is to be a reliable and fast key/value store. Changes that are in line with the features/limitations outlined above, and meet the requirements below, @@ -45,6 +61,7 @@ Contribution requirements: a sufficient explanation as to why a new (or changed) test is not required. ## Submitting a Pull Request + Before any pull request will be accepted the author must first sign a Contributor License Agreement (CLA) at https://cla.developers.google.com/. From 1f7dd5d5f6822f2b0b9f9e4c7d87d4535c122c0e Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 13 Apr 2018 14:12:06 -0700 Subject: [PATCH 091/174] Add tests for ConsumeDecimalNumber. ConsumeDecimalNumber has fairly non-trivial logic, and a previous version has crashed inexplicably on Android. Having some test coverage will make it easier to tweak / simplify the function later on. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192821751 --- CMakeLists.txt | 1 + util/logging_test.cc | 145 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 util/logging_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index cc1022f6ae..55881203a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,6 +326,7 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("${PROJECT_SOURCE_DIR}/util/coding_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/crc32c_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/hash_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/logging_test.cc") # TODO(costan): This test also uses # "${PROJECT_SOURCE_DIR}/util/env_posix_test_helper.h" diff --git a/util/logging_test.cc b/util/logging_test.cc new file mode 100644 index 0000000000..11665fc7e5 --- /dev/null +++ b/util/logging_test.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include + +#include "leveldb/slice.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class Logging { }; + +TEST(Logging, NumberToString) { + ASSERT_EQ("0", NumberToString(0)); + ASSERT_EQ("1", NumberToString(1)); + ASSERT_EQ("9", NumberToString(9)); + + ASSERT_EQ("10", NumberToString(10)); + ASSERT_EQ("11", NumberToString(11)); + ASSERT_EQ("19", NumberToString(19)); + ASSERT_EQ("99", NumberToString(99)); + + ASSERT_EQ("100", NumberToString(100)); + ASSERT_EQ("109", NumberToString(109)); + ASSERT_EQ("190", NumberToString(190)); + ASSERT_EQ("123", NumberToString(123)); + ASSERT_EQ("12345678", NumberToString(12345678)); + + static_assert(std::numeric_limits::max() == 18446744073709551615U, + "Test consistency check"); + ASSERT_EQ("18446744073709551000", NumberToString(18446744073709551000U)); + ASSERT_EQ("18446744073709551600", NumberToString(18446744073709551600U)); + ASSERT_EQ("18446744073709551610", NumberToString(18446744073709551610U)); + ASSERT_EQ("18446744073709551614", NumberToString(18446744073709551614U)); + ASSERT_EQ("18446744073709551615", NumberToString(18446744073709551615U)); +} + +void ConsumeDecimalNumberRoundtripTest(uint64_t number, + const std::string& padding = "") { + std::string decimal_number = NumberToString(number); + std::string input_string = decimal_number + padding; + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_TRUE(ConsumeDecimalNumber(&output, &result)); + ASSERT_EQ(number, result); + ASSERT_EQ(decimal_number.size(), output.data() - input.data()); + ASSERT_EQ(padding.size(), output.size()); +} + +TEST(Logging, ConsumeDecimalNumberRoundtrip) { + ConsumeDecimalNumberRoundtripTest(0); + ConsumeDecimalNumberRoundtripTest(1); + ConsumeDecimalNumberRoundtripTest(9); + + ConsumeDecimalNumberRoundtripTest(10); + ConsumeDecimalNumberRoundtripTest(11); + ConsumeDecimalNumberRoundtripTest(19); + ConsumeDecimalNumberRoundtripTest(99); + + ConsumeDecimalNumberRoundtripTest(100); + ConsumeDecimalNumberRoundtripTest(109); + ConsumeDecimalNumberRoundtripTest(190); + ConsumeDecimalNumberRoundtripTest(123); + ASSERT_EQ("12345678", NumberToString(12345678)); + + for (uint64_t i = 0; i < 100; ++i) { + uint64_t large_number = std::numeric_limits::max() - i; + ConsumeDecimalNumberRoundtripTest(large_number); + } +} + +TEST(Logging, ConsumeDecimalNumberRoundtripWithPadding) { + ConsumeDecimalNumberRoundtripTest(0, " "); + ConsumeDecimalNumberRoundtripTest(1, "abc"); + ConsumeDecimalNumberRoundtripTest(9, "x"); + + ConsumeDecimalNumberRoundtripTest(10, "_"); + ConsumeDecimalNumberRoundtripTest(11, std::string("\0\0\0", 3)); + ConsumeDecimalNumberRoundtripTest(19, "abc"); + ConsumeDecimalNumberRoundtripTest(99, "padding"); + + ConsumeDecimalNumberRoundtripTest(100, " "); + + for (uint64_t i = 0; i < 100; ++i) { + uint64_t large_number = std::numeric_limits::max() - i; + ConsumeDecimalNumberRoundtripTest(large_number, "pad"); + } +} + +void ConsumeDecimalNumberOverflowTest(const std::string& input_string) { + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_EQ(false, ConsumeDecimalNumber(&output, &result)); +} + +TEST(Logging, ConsumeDecimalNumberOverflow) { + static_assert(std::numeric_limits::max() == 18446744073709551615U, + "Test consistency check"); + ConsumeDecimalNumberOverflowTest("18446744073709551616"); + ConsumeDecimalNumberOverflowTest("18446744073709551617"); + ConsumeDecimalNumberOverflowTest("18446744073709551618"); + ConsumeDecimalNumberOverflowTest("18446744073709551619"); + ConsumeDecimalNumberOverflowTest("18446744073709551620"); + ConsumeDecimalNumberOverflowTest("18446744073709551621"); + ConsumeDecimalNumberOverflowTest("18446744073709551622"); + ConsumeDecimalNumberOverflowTest("18446744073709551623"); + ConsumeDecimalNumberOverflowTest("18446744073709551624"); + ConsumeDecimalNumberOverflowTest("18446744073709551625"); + ConsumeDecimalNumberOverflowTest("18446744073709551626"); + + ConsumeDecimalNumberOverflowTest("18446744073709551700"); + + ConsumeDecimalNumberOverflowTest("99999999999999999999"); +} + +void ConsumeDecimalNumberNoDigitsTest(const std::string& input_string) { + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_EQ(false, ConsumeDecimalNumber(&output, &result)); + ASSERT_EQ(input.data(), output.data()); + ASSERT_EQ(input.size(), output.size()); +} + +TEST(Logging, ConsumeDecimalNumberNoDigits) { + ConsumeDecimalNumberNoDigitsTest(""); + ConsumeDecimalNumberNoDigitsTest(" "); + ConsumeDecimalNumberNoDigitsTest("a"); + ConsumeDecimalNumberNoDigitsTest(" 123"); + ConsumeDecimalNumberNoDigitsTest("a123"); + ConsumeDecimalNumberNoDigitsTest(std::string("\000123", 4)); + ConsumeDecimalNumberNoDigitsTest(std::string("\177123", 4)); + ConsumeDecimalNumberNoDigitsTest(std::string("\377123", 4)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} From a0008deb679480fd30e845d7e52421af72160c2c Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 13 Apr 2018 15:17:46 -0700 Subject: [PATCH 092/174] Reimplement ConsumeDecimalNumber. The old implementation caused odd crashes on ARM, which were fixed by changing a local variable type. The main suspect is the use of a static local variable. This CL replaces the static local variable with constexpr, which still ensures the compiler sees the expressions as constants. The CL also replaces Slice operations in the functions' inner loop with iterator-style pointer operations, which can help the compiler generate less code. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192832175 --- util/logging.cc | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/util/logging.cc b/util/logging.cc index d48b6dd289..0c2890f911 100644 --- a/util/logging.cc +++ b/util/logging.cc @@ -46,28 +46,38 @@ std::string EscapeString(const Slice& value) { } bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { - uint64_t v = 0; - int digits = 0; - while (!in->empty()) { - char c = (*in)[0]; - if (c >= '0' && c <= '9') { - ++digits; - // |delta| intentionally unit64_t to avoid Android crash (see log). - const uint64_t delta = (c - '0'); - static const uint64_t kMaxUint64 = ~static_cast(0); - if (v > kMaxUint64/10 || - (v == kMaxUint64/10 && delta > kMaxUint64%10)) { - // Overflow - return false; - } - v = (v * 10) + delta; - in->remove_prefix(1); - } else { + // Constants that will be optimized away. + constexpr const uint64_t kMaxUint64 = std::numeric_limits::max(); + constexpr const char kLastDigitOfMaxUint64 = + '0' + static_cast(kMaxUint64 % 10); + + uint64_t value = 0; + + // reinterpret_cast-ing from char* to unsigned char* to avoid signedness. + const unsigned char* start = + reinterpret_cast(in->data()); + + const unsigned char* end = start + in->size(); + const unsigned char* current = start; + for (; current != end; ++current) { + const unsigned char ch = *current; + if (ch < '0' || ch > '9') break; + + // Overflow check. + // kMaxUint64 / 10 is also constant and will be optimized away. + if (value > kMaxUint64 / 10 || + (value == kMaxUint64 / 10 && ch > kLastDigitOfMaxUint64)) { + return false; } + + value = (value * 10) + (ch - '0'); } - *val = v; - return (digits > 0); + + *val = value; + const size_t digits_consumed = current - start; + in->remove_prefix(digits_consumed); + return digits_consumed != 0; } } // namespace leveldb From 8046a51b21114d3575421bfc78b1d98b1678720a Mon Sep 17 00:00:00 2001 From: costan Date: Fri, 13 Apr 2018 16:17:51 -0700 Subject: [PATCH 093/174] Add forgotten header to util/logging.cc. Commit a0008deb679480fd30e845d7e52421af72160c2c introduced std::numeric_limits usage in logging.cc, but didn't #include ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192840190 --- util/logging.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/util/logging.cc b/util/logging.cc index 0c2890f911..411a30360d 100644 --- a/util/logging.cc +++ b/util/logging.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "leveldb/env.h" #include "leveldb/slice.h" From 14cce848e7b8a040a8f457d5a796722a55e19597 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 16 Apr 2018 12:27:06 -0700 Subject: [PATCH 094/174] Fix sign mismatch warnings in GCC. This was contributed in https://github.com/google/leveldb/pull/492 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193080913 --- db/memtable.cc | 2 +- db/version_set.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/memtable.cc b/db/memtable.cc index bfec0a7e7a..287afdbdcb 100644 --- a/db/memtable.cc +++ b/db/memtable.cc @@ -101,7 +101,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, p += 8; p = EncodeVarint32(p, val_size); memcpy(p, value.data(), val_size); - assert((p + val_size) - buf == encoded_len); + assert(p + val_size == buf + encoded_len); table_.Insert(buf); } diff --git a/db/version_set.cc b/db/version_set.cc index 02cc66f06f..c27ccadc0b 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -20,7 +20,7 @@ namespace leveldb { -static int TargetFileSize(const Options* options) { +static size_t TargetFileSize(const Options* options) { return options->max_file_size; } From d177a0263cce4344d05188521ad53459c369b940 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 17 Apr 2018 13:23:10 -0700 Subject: [PATCH 095/174] Replace port_posix with port_stdcxx. The porting layer implements threading primitives: atomic pointers, condition variables, mutexes, thread-safe initialization. These are all specified in C++11, so the reference open source port implementation can become platform-independent. The porting layer will remain in place to allow the use of other implementations with more features, such as the built-in deadlock detection in abseil's Mutex. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193245934 --- CMakeLists.txt | 64 ++++++------------------ port/README | 2 +- port/port.h | 2 +- port/port_posix.cc | 53 -------------------- port/{port_posix.h => port_stdcxx.h} | 74 ++++++++++++++++------------ util/env_posix.cc | 6 +++ 6 files changed, 66 insertions(+), 135 deletions(-) delete mode 100644 port/port_posix.cc rename port/{port_posix.h => port_stdcxx.h} (62%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55881203a0..4bf3df3a3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,33 +79,10 @@ if(BUILD_SHARED_LIBS) add_compile_options(-fvisibility=hidden) endif(BUILD_SHARED_LIBS) -# POSIX code is specified separately so we can leave it out in the future. -add_library(leveldb_port_posix OBJECT "") -target_sources(leveldb_port_posix - PRIVATE - "${PROJECT_SOURCE_DIR}/port/port_posix.cc" - - PUBLIC - # The headers below are dependencies for leveldb, but aren't needed by users - # that link to the installed version of leveldb and rely on its public API. - $ - $ - $ - $ -) -if (NOT HAVE_CXX17_HAS_INCLUDE) - target_compile_definitions(leveldb_port_posix - PRIVATE - LEVELDB_HAS_PORT_CONFIG_H=1 - ) -endif(NOT HAVE_CXX17_HAS_INCLUDE) -if(BUILD_SHARED_LIBS) - set_property(TARGET leveldb_port_posix PROPERTY POSITION_INDEPENDENT_CODE ON) -endif(BUILD_SHARED_LIBS) - add_library(leveldb "") target_sources(leveldb PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" "${PROJECT_SOURCE_DIR}/db/builder.cc" "${PROJECT_SOURCE_DIR}/db/builder.h" "${PROJECT_SOURCE_DIR}/db/c.cc" @@ -136,6 +113,8 @@ target_sources(leveldb "${PROJECT_SOURCE_DIR}/db/version_set.h" "${PROJECT_SOURCE_DIR}/db/write_batch_internal.h" "${PROJECT_SOURCE_DIR}/db/write_batch.cc" + "${PROJECT_SOURCE_DIR}/port/atomic_pointer.h" + "${PROJECT_SOURCE_DIR}/port/port_stdcxx.h" "${PROJECT_SOURCE_DIR}/port/port.h" "${PROJECT_SOURCE_DIR}/port/thread_annotations.h" "${PROJECT_SOURCE_DIR}/table/block_builder.cc" @@ -163,7 +142,6 @@ target_sources(leveldb "${PROJECT_SOURCE_DIR}/util/comparator.cc" "${PROJECT_SOURCE_DIR}/util/crc32c.cc" "${PROJECT_SOURCE_DIR}/util/crc32c.h" - "${PROJECT_SOURCE_DIR}/util/env_posix.cc" "${PROJECT_SOURCE_DIR}/util/env.cc" "${PROJECT_SOURCE_DIR}/util/filter_policy.cc" "${PROJECT_SOURCE_DIR}/util/hash.cc" @@ -172,10 +150,8 @@ target_sources(leveldb "${PROJECT_SOURCE_DIR}/util/logging.h" "${PROJECT_SOURCE_DIR}/util/mutexlock.h" "${PROJECT_SOURCE_DIR}/util/options.cc" - "${PROJECT_SOURCE_DIR}/util/posix_logger.h" "${PROJECT_SOURCE_DIR}/util/random.h" "${PROJECT_SOURCE_DIR}/util/status.cc" - $ # Only CMake 3.3+ supports PUBLIC sources in targets exported by "install". $<$:PUBLIC> @@ -195,12 +171,21 @@ target_sources(leveldb "${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" ) + +# POSIX code is specified separately so we can leave it out in the future. +target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/util/env_posix.cc" + "${PROJECT_SOURCE_DIR}/util/posix_logger.h" +) + # MemEnv is not part of the interface and could be pulled to a separate library. target_sources(leveldb PRIVATE "${PROJECT_SOURCE_DIR}/helpers/memenv/memenv.cc" "${PROJECT_SOURCE_DIR}/helpers/memenv/memenv.h" ) + target_include_directories(leveldb PUBLIC $ @@ -234,11 +219,6 @@ if(HAVE_CLANG_THREAD_SAFETY) -Werror -Wthread-safety) endif(HAVE_CLANG_THREAD_SAFETY) -# TODO(costan): This is only needed for port_posix. -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) -target_link_libraries(leveldb Threads::Threads) - if(HAVE_CRC32C) target_link_libraries(leveldb crc32c) endif(HAVE_CRC32C) @@ -249,6 +229,10 @@ if(HAVE_TCMALLOC) target_link_libraries(leveldb tcmalloc) endif(HAVE_TCMALLOC) +# Needed by port_stdcxx.h +find_package(Threads REQUIRED) +target_link_libraries(leveldb Threads::Threads) + add_executable(leveldbutil "${PROJECT_SOURCE_DIR}/db/leveldbutil.cc" ) @@ -271,14 +255,6 @@ if(LEVELDB_BUILD_TESTS) "${test_file}" ) - if(BUILD_SHARED_LIBS) - # Port functions aren't exposed in the shared library build. - target_sources("${test_target_name}" - PRIVATE - $ - ) - endif(BUILD_SHARED_LIBS) - target_link_libraries("${test_target_name}" leveldb) target_compile_definitions("${test_target_name}" PRIVATE @@ -351,14 +327,6 @@ if(LEVELDB_BUILD_BENCHMARKS) "${bench_file}" ) - if(BUILD_SHARED_LIBS) - # Port functions aren't exposed in the shared library build. - target_sources("${bench_target_name}" - PRIVATE - $ - ) - endif(BUILD_SHARED_LIBS) - target_link_libraries("${bench_target_name}" leveldb) target_compile_definitions("${bench_target_name}" PRIVATE diff --git a/port/README b/port/README index 422563e25c..8b171532e1 100644 --- a/port/README +++ b/port/README @@ -5,6 +5,6 @@ Code in the rest of the package includes "port.h" from this directory. "port.h" in turn includes a platform specific "port_.h" file that provides the platform specific implementation. -See port_posix.h for an example of what must be provided in a platform +See port_stdcxx.h for an example of what must be provided in a platform specific header file. diff --git a/port/port.h b/port/port.h index e667db40d0..0975fed0d1 100644 --- a/port/port.h +++ b/port/port.h @@ -11,7 +11,7 @@ // porting to a new platform, see "port_example.h" for documentation // of what the new port_.h file must provide. #if defined(LEVELDB_PLATFORM_POSIX) -# include "port/port_posix.h" +# include "port/port_stdcxx.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) # include "port/port_chromium.h" #endif diff --git a/port/port_posix.cc b/port/port_posix.cc deleted file mode 100644 index 04095bbf85..0000000000 --- a/port/port_posix.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -#include "port/port_posix.h" - -#include -#include -#include - -namespace leveldb { -namespace port { - -static void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } -} - -Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, nullptr)); } - -Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } - -void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } - -void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } - -CondVar::CondVar(Mutex* mu) - : mu_(mu) { - PthreadCall("init cv", pthread_cond_init(&cv_, nullptr)); -} - -CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } - -void CondVar::Wait() { - PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); -} - -void CondVar::Signal() { - PthreadCall("signal", pthread_cond_signal(&cv_)); -} - -void CondVar::SignalAll() { - PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); -} - -void InitOnce(OnceType* once, void (*initializer)()) { - PthreadCall("once", pthread_once(once, initializer)); -} - -} // namespace port -} // namespace leveldb diff --git a/port/port_posix.h b/port/port_stdcxx.h similarity index 62% rename from port/port_posix.h rename to port/port_stdcxx.h index 54b07b6ece..4e58cba2c2 100644 --- a/port/port_posix.h +++ b/port/port_stdcxx.h @@ -1,11 +1,9 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -// -// See port_example.h for documentation for the following types/functions. -#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ -#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#ifndef STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ +#define STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ // port/port_config.h availability is automatically detected via __has_include // in newer compilers. If LEVELDB_HAS_PORT_CONFIG_H is defined, it overrides the @@ -24,22 +22,22 @@ #endif // defined(LEVELDB_HAS_PORT_CONFIG_H) -#include #if HAVE_CRC32C #include #endif // HAVE_CRC32C #if HAVE_SNAPPY #include #endif // HAVE_SNAPPY + +#include #include +#include +#include // NOLINT +#include // NOLINT #include #include "port/atomic_pointer.h" #include "port/thread_annotations.h" -#if !HAVE_FDATASYNC -#define fdatasync fsync -#endif // !HAVE_FDATASYNC - namespace leveldb { namespace port { @@ -47,39 +45,52 @@ static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN; class CondVar; +// Thinly wraps std::mutex. class LOCKABLE Mutex { public: - Mutex(); - ~Mutex(); + Mutex() = default; + ~Mutex() = default; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; - void Lock() EXCLUSIVE_LOCK_FUNCTION(); - void Unlock() UNLOCK_FUNCTION(); + void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); } + void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); } void AssertHeld() ASSERT_EXCLUSIVE_LOCK() { } private: friend class CondVar; - pthread_mutex_t mu_; - - // No copying - Mutex(const Mutex&); - void operator=(const Mutex&); + std::mutex mu_; }; +// Thinly wraps std::condition_variable. class CondVar { public: - explicit CondVar(Mutex* mu); - ~CondVar(); - void Wait(); - void Signal(); - void SignalAll(); + explicit CondVar(Mutex* mu) : mu_(mu) { assert(mu != nullptr); } + ~CondVar() = default; + + CondVar(const CondVar&) = delete; + CondVar& operator=(const CondVar&) = delete; + + void Wait() { + std::unique_lock lock(mu_->mu_, std::adopt_lock); + cv_.wait(lock); + lock.release(); + } + void Signal() { cv_.notify_one(); } + void SignalAll() { cv_.notify_all(); } private: - pthread_cond_t cv_; - Mutex* mu_; + std::condition_variable cv_; + Mutex* const mu_; }; -typedef pthread_once_t OnceType; -#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT -void InitOnce(OnceType* once, void (*initializer)()); +using OnceType = std::once_flag; +#define LEVELDB_ONCE_INIT {} + +// Thinly wraps std::call_once. +inline void InitOnce(OnceType* once, void (*initializer)()) { + std::call_once(*once, *initializer); +} inline bool Snappy_Compress(const char* input, size_t length, ::std::string* output) { @@ -103,8 +114,7 @@ inline bool Snappy_GetUncompressedLength(const char* input, size_t length, #endif // HAVE_SNAPPY } -inline bool Snappy_Uncompress(const char* input, size_t length, - char* output) { +inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { #if HAVE_SNAPPY return snappy::RawUncompress(input, length, output); #else @@ -127,4 +137,4 @@ inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { } // namespace port } // namespace leveldb -#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#endif // STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ diff --git a/util/env_posix.cc b/util/env_posix.cc index e758d5f2ee..51844addab 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -28,6 +28,12 @@ #include "util/posix_logger.h" #include "util/env_posix_test_helper.h" +// HAVE_FDATASYNC is defined in the auto-generated port_config.h, which is +// included by port_stdcxx.h. +#if !HAVE_FDATASYNC +#define fdatasync fsync +#endif // !HAVE_FDATASYNC + namespace leveldb { namespace { From 4de9594f6fbfd69043239a5705b5f32065f02d34 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 23 Apr 2018 16:15:21 -0700 Subject: [PATCH 096/174] Add move constructor to Status. This will result in smaller code generation when Status instances are passed around. Benchmarks don't indicate a significant change either way. CPU: 48 * Intel(R) Xeon(R) CPU E5-2690 v3 @ 2.60GHz CPUCache: 30720 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) Baseline: fillseq : 3.589 micros/op; 30.8 MB/s fillsync : 4165.299 micros/op; 0.0 MB/s (1000 ops) fillrandom : 5.864 micros/op; 18.9 MB/s overwrite : 7.830 micros/op; 14.1 MB/s readrandom : 5.534 micros/op; (1000000 of 1000000 found) readrandom : 4.292 micros/op; (1000000 of 1000000 found) readseq : 0.312 micros/op; 354.1 MB/s readreverse : 0.501 micros/op; 220.8 MB/s compact : 886211.000 micros/op; readrandom : 3.518 micros/op; (1000000 of 1000000 found) readseq : 0.251 micros/op; 441.2 MB/s readreverse : 0.456 micros/op; 242.4 MB/s fill100K : 1329.723 micros/op; 71.7 MB/s (1000 ops) crc32c : 1.976 micros/op; 1976.7 MB/s (4K per op) snappycomp : 4.705 micros/op; 830.2 MB/s (output: 55.1%) snappyuncomp : 0.958 micros/op; 4079.1 MB/s acquireload : 0.727 micros/op; (each op is 1000 loads) New: fillseq : 3.129 micros/op; 35.4 MB/s fillsync : 2748.099 micros/op; 0.0 MB/s (1000 ops) fillrandom : 5.394 micros/op; 20.5 MB/s overwrite : 7.253 micros/op; 15.3 MB/s readrandom : 5.655 micros/op; (1000000 of 1000000 found) readrandom : 4.425 micros/op; (1000000 of 1000000 found) readseq : 0.298 micros/op; 371.3 MB/s readreverse : 0.508 micros/op; 217.9 MB/s compact : 885842.000 micros/op; readrandom : 3.545 micros/op; (1000000 of 1000000 found) readseq : 0.252 micros/op; 438.2 MB/s readreverse : 0.425 micros/op; 260.2 MB/s fill100K : 1418.347 micros/op; 67.2 MB/s (1000 ops) crc32c : 1.987 micros/op; 1966.0 MB/s (4K per op) snappycomp : 4.767 micros/op; 819.4 MB/s (output: 55.1%) snappyuncomp : 0.916 micros/op; 4264.9 MB/s acquireload : 0.665 micros/op; (each op is 1000 loads) ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=194002392 --- CMakeLists.txt | 1 + include/leveldb/status.h | 30 +++++++++++++++++----------- util/status_test.cc | 42 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 util/status_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bf3df3a3e..d49c31e352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,6 +277,7 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue200_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/env_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/status_test.cc") if(NOT BUILD_SHARED_LIBS) leveldb_test("${PROJECT_SOURCE_DIR}/db/autocompact_test.cc") diff --git a/include/leveldb/status.h b/include/leveldb/status.h index 39d692df36..ee9fac2067 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -13,6 +13,7 @@ #ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ #define STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#include #include #include "leveldb/export.h" #include "leveldb/slice.h" @@ -22,12 +23,14 @@ namespace leveldb { class LEVELDB_EXPORT Status { public: // Create a success status. - Status() : state_(nullptr) { } + Status() noexcept : state_(nullptr) { } ~Status() { delete[] state_; } - // Copy the specified status. - Status(const Status& s); - void operator=(const Status& s); + Status(const Status& rhs); + Status& operator=(const Status& rhs); + + Status(Status&& rhs) noexcept : state_(rhs.state_) { rhs.state_ = nullptr; } + Status& operator=(Status&& rhs) noexcept; // Return a success status. static Status OK() { return Status(); } @@ -96,16 +99,21 @@ class LEVELDB_EXPORT Status { static const char* CopyState(const char* s); }; -inline Status::Status(const Status& s) { - state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); +inline Status::Status(const Status& rhs) { + state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_); } -inline void Status::operator=(const Status& s) { - // The following condition catches both aliasing (when this == &s), - // and the common case where both s and *this are ok. - if (state_ != s.state_) { +inline Status& Status::operator=(const Status& rhs) { + // The following condition catches both aliasing (when this == &rhs), + // and the common case where both rhs and *this are ok. + if (state_ != rhs.state_) { delete[] state_; - state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); + state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_); } + return *this; +} +inline Status& Status::operator=(Status&& rhs) noexcept { + std::swap(state_, rhs.state_); + return *this; } } // namespace leveldb diff --git a/util/status_test.cc b/util/status_test.cc new file mode 100644 index 0000000000..7ed3b9eba9 --- /dev/null +++ b/util/status_test.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include + +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "util/testharness.h" + +namespace leveldb { + +TEST(Status, MoveConstructor) { + { + Status ok = Status::OK(); + Status ok2 = std::move(ok); + + ASSERT_TRUE(ok2.ok()); + } + + { + Status status = Status::NotFound("custom NotFound status message"); + Status status2 = std::move(status); + + ASSERT_TRUE(status2.IsNotFound()); + ASSERT_EQ("NotFound: custom NotFound status message", status2.ToString()); + } + + { + Status self_moved = Status::IOError("custom IOError status message"); + + // Needed to bypass compiler warning about explicit move-assignment. + Status& self_moved_reference = self_moved; + self_moved_reference = std::move(self_moved); + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} From bc23e00f955eadb9e26f8ce07c1c664e7b985ff0 Mon Sep 17 00:00:00 2001 From: cmumford Date: Fri, 27 Apr 2018 09:00:28 -0700 Subject: [PATCH 097/174] Update default log file size in doc. The default size was changed in #f779e7a5 but the documentation was never updated. This fixes #566 reported on GitHub. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=194547959 --- doc/impl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/impl.md b/doc/impl.md index 4b13f2a6ba..6e6b2ab127 100644 --- a/doc/impl.md +++ b/doc/impl.md @@ -64,7 +64,7 @@ Other files used for miscellaneous purposes may also be present (LOCK, *.dbtmp). ## Level 0 -When the log file grows above a certain size (1MB by default): +When the log file grows above a certain size (4MB by default): Create a brand new memtable and log file and direct future updates here In the background: Write the contents of the previous memtable to an sstable From e7840de9f3db1a5eddedfecbbbc1ff72a4c2631a Mon Sep 17 00:00:00 2001 From: cmumford Date: Fri, 27 Apr 2018 09:14:32 -0700 Subject: [PATCH 098/174] Fix documentation for log file growth. This fixes #546 reported on GitHub. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=194549692 --- doc/impl.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/impl.md b/doc/impl.md index 6e6b2ab127..cacabb96fc 100644 --- a/doc/impl.md +++ b/doc/impl.md @@ -65,12 +65,14 @@ Other files used for miscellaneous purposes may also be present (LOCK, *.dbtmp). ## Level 0 When the log file grows above a certain size (4MB by default): -Create a brand new memtable and log file and direct future updates here +Create a brand new memtable and log file and direct future updates here. + In the background: -Write the contents of the previous memtable to an sstable -Discard the memtable -Delete the old log file and the old memtable -Add the new sstable to the young (level-0) level. + +1. Write the contents of the previous memtable to an sstable. +2. Discard the memtable. +3. Delete the old log file and the old memtable. +4. Add the new sstable to the young (level-0) level. ## Compactions From 18683981505dc374ce29211c80a9552f8f2f4571 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 30 Apr 2018 15:11:03 -0700 Subject: [PATCH 099/174] Clean up SnapshotImpl. * Omit SnapshotImpl::list_ when assert() isn't on * Make SnapshotImpl::number_ const and set it in the constructor * Make SnapshotImpl::number_ private and access it via a getter * Rename SnapshotImpl::number_ to SnapshotImpl::sequence_number_ * Rename SnapshotList::list_ to SnapshotList::head_ * Wrap casting from Snapshot* to SnapshotImpl* in ToSnapshotImpl() ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=194852828 --- db/db_impl.cc | 11 ++++---- db/db_test.cc | 49 +++++++++++++++++++++++++++++++++++ db/snapshot.h | 72 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 02a6872820..fefb883a06 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -906,7 +906,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (snapshots_.empty()) { compact->smallest_snapshot = versions_->LastSequence(); } else { - compact->smallest_snapshot = snapshots_.oldest()->number_; + compact->smallest_snapshot = snapshots_.oldest()->sequence_number(); } // Release mutex while we're actually doing the compaction work @@ -1121,7 +1121,8 @@ Status DBImpl::Get(const ReadOptions& options, MutexLock l(&mutex_); SequenceNumber snapshot; if (options.snapshot != nullptr) { - snapshot = reinterpret_cast(options.snapshot)->number_; + snapshot = + static_cast(options.snapshot)->sequence_number(); } else { snapshot = versions_->LastSequence(); } @@ -1168,7 +1169,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& options) { return NewDBIterator( this, user_comparator(), iter, (options.snapshot != nullptr - ? reinterpret_cast(options.snapshot)->number_ + ? static_cast(options.snapshot)->sequence_number() : latest_snapshot), seed); } @@ -1185,9 +1186,9 @@ const Snapshot* DBImpl::GetSnapshot() { return snapshots_.New(versions_->LastSequence()); } -void DBImpl::ReleaseSnapshot(const Snapshot* s) { +void DBImpl::ReleaseSnapshot(const Snapshot* snapshot) { MutexLock l(&mutex_); - snapshots_.Delete(reinterpret_cast(s)); + snapshots_.Delete(static_cast(snapshot)); } // Convenience methods diff --git a/db/db_test.cc b/db/db_test.cc index 47e3287e0b..878b7d4e03 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -631,6 +631,55 @@ TEST(DBTest, GetSnapshot) { } while (ChangeOptions()); } +TEST(DBTest, GetIdenticalSnapshots) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + const Snapshot* s2 = db_->GetSnapshot(); + const Snapshot* s3 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + ASSERT_EQ("v1", Get(key, s2)); + ASSERT_EQ("v1", Get(key, s3)); + db_->ReleaseSnapshot(s1); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s2)); + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v1", Get(key, s3)); + db_->ReleaseSnapshot(s3); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IterateOverEmptySnapshot) { + do { + const Snapshot* snapshot = db_->GetSnapshot(); + ReadOptions read_options; + read_options.snapshot = snapshot; + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + + Iterator* iterator1 = db_->NewIterator(read_options); + iterator1->SeekToFirst(); + ASSERT_TRUE(!iterator1->Valid()); + delete iterator1; + + dbfull()->TEST_CompactMemTable(); + + Iterator* iterator2 = db_->NewIterator(read_options); + iterator2->SeekToFirst(); + ASSERT_TRUE(!iterator2->Valid()); + delete iterator2; + + db_->ReleaseSnapshot(snapshot); + } while (ChangeOptions()); +} + TEST(DBTest, GetLevel0Ordering) { do { // Check that we process level-0 files in correct order. The code diff --git a/db/snapshot.h b/db/snapshot.h index 6ed413c42d..c43d9f900b 100644 --- a/db/snapshot.h +++ b/db/snapshot.h @@ -16,50 +16,72 @@ class SnapshotList; // Each SnapshotImpl corresponds to a particular sequence number. class SnapshotImpl : public Snapshot { public: - SequenceNumber number_; // const after creation + SnapshotImpl(SequenceNumber sequence_number) + : sequence_number_(sequence_number) {} + + SequenceNumber sequence_number() const { return sequence_number_; } private: friend class SnapshotList; - // SnapshotImpl is kept in a doubly-linked circular list + // SnapshotImpl is kept in a doubly-linked circular list. The SnapshotList + // implementation operates on the next/previous fields direcly. SnapshotImpl* prev_; SnapshotImpl* next_; - SnapshotList* list_; // just for sanity checks + const SequenceNumber sequence_number_; + +#if !defined(NDEBUG) + SnapshotList* list_ = nullptr; +#endif // !defined(NDEBUG) }; class SnapshotList { public: - SnapshotList() { - list_.prev_ = &list_; - list_.next_ = &list_; + SnapshotList() : head_(0) { + head_.prev_ = &head_; + head_.next_ = &head_; } - bool empty() const { return list_.next_ == &list_; } - SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } - SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } - - const SnapshotImpl* New(SequenceNumber seq) { - SnapshotImpl* s = new SnapshotImpl; - s->number_ = seq; - s->list_ = this; - s->next_ = &list_; - s->prev_ = list_.prev_; - s->prev_->next_ = s; - s->next_->prev_ = s; - return s; + bool empty() const { return head_.next_ == &head_; } + SnapshotImpl* oldest() const { assert(!empty()); return head_.next_; } + SnapshotImpl* newest() const { assert(!empty()); return head_.prev_; } + + // Creates a SnapshotImpl and appends it to the end of the list. + SnapshotImpl* New(SequenceNumber sequence_number) { + assert(empty() || newest()->sequence_number_ <= sequence_number); + + SnapshotImpl* snapshot = new SnapshotImpl(sequence_number); + +#if !defined(NDEBUG) + snapshot->list_ = this; +#endif // !defined(NDEBUG) + snapshot->next_ = &head_; + snapshot->prev_ = head_.prev_; + snapshot->prev_->next_ = snapshot; + snapshot->next_->prev_ = snapshot; + return snapshot; } - void Delete(const SnapshotImpl* s) { - assert(s->list_ == this); - s->prev_->next_ = s->next_; - s->next_->prev_ = s->prev_; - delete s; + // Removes a SnapshotImpl from this list. + // + // The snapshot must have been created by calling New() on this list. + // + // The snapshot pointer should not be const, because its memory is + // deallocated. However, that would force us to change DB::ReleaseSnapshot(), + // which is in the API, and currently takes a const Snapshot. + void Delete(const SnapshotImpl* snapshot) { +#if !defined(NDEBUG) + assert(snapshot->list_ == this); +#endif // !defined(NDEBUG) + snapshot->prev_->next_ = snapshot->next_; + snapshot->next_->prev_ = snapshot->prev_; + delete snapshot; } private: // Dummy head of doubly-linked list of snapshots - SnapshotImpl list_; + SnapshotImpl head_; }; } // namespace leveldb From 6a6bdafcf10f5d4bef1ca52697c38d10c28b1a8b Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 21 May 2018 13:53:36 -0700 Subject: [PATCH 100/174] Corrected typo in docs: "cache" to "block_cache". ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=197452015 --- doc/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/index.md b/doc/index.md index be8569692b..ea4609dc6a 100644 --- a/doc/index.md +++ b/doc/index.md @@ -338,19 +338,19 @@ options.compression = leveldb::kNoCompression; ### Cache The contents of the database are stored in a set of files in the filesystem and -each file stores a sequence of compressed blocks. If options.cache is non-NULL, -it is used to cache frequently used uncompressed block contents. +each file stores a sequence of compressed blocks. If options.block_cache is +non-NULL, it is used to cache frequently used uncompressed block contents. ```c++ #include "leveldb/cache.h" leveldb::Options options; -options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache +options.block_cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache leveldb::DB* db; leveldb::DB::Open(options, name, &db); ... use the db ... delete db -delete options.cache; +delete options.block_cache; ``` Note that the cache holds uncompressed data, and therefore it should be sized From 6caf73ad9dae0ee91873bcb39554537b85163770 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 4 Jun 2018 12:29:13 -0700 Subject: [PATCH 101/174] Clean up Iterator. This CL renames the private struct Iterator::Cleanup -> Iterator::CleanupNode, to better reflect that it's a linked list node, and extracts duplicated code from its user in IsEmpty() and Run() methods. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=199175058 --- include/leveldb/iterator.h | 16 +++++++--- table/iterator.cc | 61 +++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/include/leveldb/iterator.h b/include/leveldb/iterator.h index 436508adcb..6c1d91b9c9 100644 --- a/include/leveldb/iterator.h +++ b/include/leveldb/iterator.h @@ -77,17 +77,25 @@ class LEVELDB_EXPORT Iterator { // // Note that unlike all of the preceding methods, this method is // not abstract and therefore clients should not override it. - typedef void (*CleanupFunction)(void* arg1, void* arg2); + using CleanupFunction = void (*)(void* arg1, void* arg2); void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); private: - struct Cleanup { + // Cleanup functions are stored in a single-linked list. + // The list's head node is inlined in the iterator. + struct CleanupNode { + // The head node is used if the function pointer is not null. CleanupFunction function; void* arg1; void* arg2; - Cleanup* next; + CleanupNode* next; + + // True if the node is not used. Only head nodes might be unused. + bool IsEmpty() const { return function == nullptr; } + // Invokes the cleanup function. + void Run() { assert(function != nullptr); (*function)(arg1, arg2); } }; - Cleanup cleanup_; + CleanupNode cleanup_head_; }; // Return an empty iterator (yields nothing). diff --git a/table/iterator.cc b/table/iterator.cc index aff0e59fc3..41ec1aa645 100644 --- a/table/iterator.cc +++ b/table/iterator.cc @@ -7,54 +7,59 @@ namespace leveldb { Iterator::Iterator() { - cleanup_.function = nullptr; - cleanup_.next = nullptr; + cleanup_head_.function = nullptr; + cleanup_head_.next = nullptr; } Iterator::~Iterator() { - if (cleanup_.function != nullptr) { - (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); - for (Cleanup* c = cleanup_.next; c != nullptr; ) { - (*c->function)(c->arg1, c->arg2); - Cleanup* next = c->next; - delete c; - c = next; + if (!cleanup_head_.IsEmpty()) { + cleanup_head_.Run(); + for (CleanupNode* node = cleanup_head_.next; node != nullptr; ) { + node->Run(); + CleanupNode* next_node = node->next; + delete node; + node = next_node; } } } void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { assert(func != nullptr); - Cleanup* c; - if (cleanup_.function == nullptr) { - c = &cleanup_; + CleanupNode* node; + if (cleanup_head_.IsEmpty()) { + node = &cleanup_head_; } else { - c = new Cleanup; - c->next = cleanup_.next; - cleanup_.next = c; + node = new CleanupNode(); + node->next = cleanup_head_.next; + cleanup_head_.next = node; } - c->function = func; - c->arg1 = arg1; - c->arg2 = arg2; + node->function = func; + node->arg1 = arg1; + node->arg2 = arg2; } namespace { + class EmptyIterator : public Iterator { public: EmptyIterator(const Status& s) : status_(s) { } - virtual bool Valid() const { return false; } - virtual void Seek(const Slice& target) { } - virtual void SeekToFirst() { } - virtual void SeekToLast() { } - virtual void Next() { assert(false); } - virtual void Prev() { assert(false); } - Slice key() const { assert(false); return Slice(); } - Slice value() const { assert(false); return Slice(); } - virtual Status status() const { return status_; } + ~EmptyIterator() override = default; + + bool Valid() const override { return false; } + void Seek(const Slice& target) override { } + void SeekToFirst() override { } + void SeekToLast() override { } + void Next() override { assert(false); } + void Prev() override { assert(false); } + Slice key() const override { assert(false); return Slice(); } + Slice value() const override { assert(false); return Slice(); } + Status status() const override { return status_; } + private: Status status_; }; -} // namespace + +} // anonymous namespace Iterator* NewEmptyIterator() { return new EmptyIterator(Status::OK()); From f7b0e1d901da26ac5ce6ad7f0a9806ce1440197e Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 14 Aug 2018 15:23:53 -0700 Subject: [PATCH 102/174] Expose WriteBatch::Append(). WriteBatchInternal has a method for efficiently concatenating two WriteBatches. This commit exposes the method to the public API. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=208724311 --- db/write_batch.cc | 4 ++++ db/write_batch_test.cc | 8 ++++---- include/leveldb/write_batch.h | 7 +++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/db/write_batch.cc b/db/write_batch.cc index 7f8f3e8b7d..40eed2eff9 100644 --- a/db/write_batch.cc +++ b/db/write_batch.cc @@ -112,6 +112,10 @@ void WriteBatch::Delete(const Slice& key) { PutLengthPrefixedSlice(&rep_, key); } +void WriteBatch::Append(const WriteBatch &source) { + WriteBatchInternal::Append(this, &source); +} + namespace { class MemTableInserter : public WriteBatch::Handler { public: diff --git a/db/write_batch_test.cc b/db/write_batch_test.cc index 8d38023acd..49c178df6f 100644 --- a/db/write_batch_test.cc +++ b/db/write_batch_test.cc @@ -91,21 +91,21 @@ TEST(WriteBatchTest, Append) { WriteBatch b1, b2; WriteBatchInternal::SetSequence(&b1, 200); WriteBatchInternal::SetSequence(&b2, 300); - WriteBatchInternal::Append(&b1, &b2); + b1.Append(b2); ASSERT_EQ("", PrintContents(&b1)); b2.Put("a", "va"); - WriteBatchInternal::Append(&b1, &b2); + b1.Append(b2); ASSERT_EQ("Put(a, va)@200", PrintContents(&b1)); b2.Clear(); b2.Put("b", "vb"); - WriteBatchInternal::Append(&b1, &b2); + b1.Append(b2); ASSERT_EQ("Put(a, va)@200" "Put(b, vb)@201", PrintContents(&b1)); b2.Delete("foo"); - WriteBatchInternal::Append(&b1, &b2); + b1.Append(b2); ASSERT_EQ("Put(a, va)@200" "Put(b, vb)@202" "Put(b, vb)@201" diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index b6d72cb004..9386ace2d8 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -54,6 +54,13 @@ class LEVELDB_EXPORT WriteBatch { // releases. It is intended for LevelDB usage metrics. size_t ApproximateSize(); + // Copies the operations in "source" to this batch. + // + // This runs in O(source size) time. However, the constant factor is better + // than calling Iterate() over the source batch with a Handler that replicates + // the operations into this batch. + void Append(const WriteBatch& source); + // Support for iterating over the contents of a batch. class Handler { public: From 16a2b8bb3af5b1f54676256e55a5d3f0ec02da42 Mon Sep 17 00:00:00 2001 From: costan Date: Sun, 19 Aug 2018 15:17:37 -0700 Subject: [PATCH 103/174] Expose WriteBatch::Append in the C API. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=209345072 --- db/c.cc | 7 ++++++- db/c_test.c | 8 +++++++- include/leveldb/c.h | 4 +++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/db/c.cc b/db/c.cc index 77b33d5ad7..7756ea3f62 100644 --- a/db/c.cc +++ b/db/c.cc @@ -359,7 +359,7 @@ void leveldb_writebatch_delete( } void leveldb_writebatch_iterate( - leveldb_writebatch_t* b, + const leveldb_writebatch_t* b, void* state, void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), void (*deleted)(void*, const char* k, size_t klen)) { @@ -382,6 +382,11 @@ void leveldb_writebatch_iterate( b->rep.Iterate(&handler); } +void leveldb_writebatch_append(leveldb_writebatch_t *destination, + const leveldb_writebatch_t *source) { + destination->rep.Append(source->rep); +} + leveldb_options_t* leveldb_options_create() { return new leveldb_options_t; } diff --git a/db/c_test.c b/db/c_test.c index 7284de5edf..ae14b998db 100644 --- a/db/c_test.c +++ b/db/c_test.c @@ -228,12 +228,18 @@ int main(int argc, char** argv) { leveldb_writebatch_clear(wb); leveldb_writebatch_put(wb, "bar", 3, "b", 1); leveldb_writebatch_put(wb, "box", 3, "c", 1); - leveldb_writebatch_delete(wb, "bar", 3); + + leveldb_writebatch_t* wb2 = leveldb_writebatch_create(); + leveldb_writebatch_delete(wb2, "bar", 3); + leveldb_writebatch_append(wb, wb2); + leveldb_writebatch_destroy(wb2); + leveldb_write(db, woptions, wb, &err); CheckNoError(err); CheckGet(db, roptions, "foo", "hello"); CheckGet(db, roptions, "bar", NULL); CheckGet(db, roptions, "box", "c"); + int pos = 0; leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); CheckCondition(pos == 3); diff --git a/include/leveldb/c.h b/include/leveldb/c.h index 1124153b73..d8aab5b1d0 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -155,9 +155,11 @@ LEVELDB_EXPORT void leveldb_writebatch_put(leveldb_writebatch_t*, LEVELDB_EXPORT void leveldb_writebatch_delete(leveldb_writebatch_t*, const char* key, size_t klen); LEVELDB_EXPORT void leveldb_writebatch_iterate( - leveldb_writebatch_t*, void* state, + const leveldb_writebatch_t*, void* state, void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), void (*deleted)(void*, const char* k, size_t klen)); +LEVELDB_EXPORT void leveldb_writebatch_append( + leveldb_writebatch_t* destination, const leveldb_writebatch_t* source); /* Options */ From 0ef2310f67f0c0b4ba3e6ad86d8138440af30d67 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 4 Sep 2018 09:15:30 -0700 Subject: [PATCH 104/174] Remove GCC on OSX from the Travis CI matrix. Equivalent of https://github.com/google/snappy/commit/db082d2cd6512981c28849d00dd47a4216768e10 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211467181 --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5999274a97..fd7b52d6b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,13 @@ env: - BUILD_TYPE=Debug - BUILD_TYPE=RelWithDebInfo +matrix: + exclude: + # GCC fails on recent Travis OSX images. + # https://github.com/travis-ci/travis-ci/issues/9640 + - compiler: gcc + os: osx + addons: apt: # List of whitelisted in travis packages for ubuntu-trusty can be found here: From 9b44da73d9b1d839c437e3fdaaa14ea08260dce4 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 4 Sep 2018 09:21:24 -0700 Subject: [PATCH 105/174] Clarify comments for leveldb::Env file reading methods. "Create a brand new [adjective] file" seems like the description for a method that will create a new file, but is used for methods that open existing files for read access. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211468002 --- include/leveldb/env.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 87dc06e63d..59e2a6fef4 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -45,7 +45,7 @@ class LEVELDB_EXPORT Env { // The result of Default() belongs to leveldb and must never be deleted. static Env* Default(); - // Create a brand new sequentially-readable file with the specified name. + // Create an object that sequentially reads the file with the specified name. // On success, stores a pointer to the new file in *result and returns OK. // On failure stores nullptr in *result and returns non-OK. If the file does // not exist, returns a non-OK status. Implementations should return a @@ -55,7 +55,7 @@ class LEVELDB_EXPORT Env { virtual Status NewSequentialFile(const std::string& fname, SequentialFile** result) = 0; - // Create a brand new random access read-only file with the + // Create an object supporting random-access reads from the file with the // specified name. On success, stores a pointer to the new file in // *result and returns OK. On failure stores nullptr in *result and // returns non-OK. If the file does not exist, returns a non-OK From 03064cbbb2c00c3e6e41a78e8111d14a020f7d6f Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 4 Sep 2018 09:31:27 -0700 Subject: [PATCH 106/174] Simplify Limiter in env_posix.cc. Now that we require C++11, we can use std::atomic, which has primitives for most of the logic we need. As a bonus, the happy path for Limiter::Acquire() and Limiter::Release() only performs one atomic operation. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211469518 --- util/env_posix.cc | 58 ++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 51844addab..18e766417f 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -16,9 +16,12 @@ #include #include #include + +#include #include #include #include + #include "leveldb/env.h" #include "leveldb/slice.h" #include "port/port.h" @@ -53,52 +56,41 @@ static Status PosixError(const std::string& context, int err_number) { // Helper class to limit resource usage to avoid exhaustion. // Currently used to limit read-only file descriptors and mmap file usage -// so that we do not end up running out of file descriptors, virtual memory, -// or running into kernel performance problems for very large databases. +// so that we do not run out of file descriptors or virtual memory, or run into +// kernel performance problems for very large databases. class Limiter { public: - // Limit maximum number of resources to |n|. - Limiter(intptr_t n) { - SetAllowed(n); - } + // Limit maximum number of resources to |max_acquires|. + Limiter(int max_acquires) : acquires_allowed_(max_acquires) {} + + Limiter(const Limiter&) = delete; + Limiter operator=(const Limiter&) = delete; // If another resource is available, acquire it and return true. // Else return false. - bool Acquire() LOCKS_EXCLUDED(mu_) { - if (GetAllowed() <= 0) { - return false; - } - MutexLock l(&mu_); - intptr_t x = GetAllowed(); - if (x <= 0) { - return false; - } else { - SetAllowed(x - 1); + bool Acquire() { + int old_acquires_allowed = + acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); + + if (old_acquires_allowed > 0) return true; - } + + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); + return false; } // Release a resource acquired by a previous call to Acquire() that returned // true. - void Release() LOCKS_EXCLUDED(mu_) { - MutexLock l(&mu_); - SetAllowed(GetAllowed() + 1); + void Release() { + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } private: - port::Mutex mu_; - port::AtomicPointer allowed_; - - intptr_t GetAllowed() const { - return reinterpret_cast(allowed_.Acquire_Load()); - } - - void SetAllowed(intptr_t v) EXCLUSIVE_LOCKS_REQUIRED(mu_) { - allowed_.Release_Store(reinterpret_cast(v)); - } - - Limiter(const Limiter&); - void operator=(const Limiter&); + // The number of available resources. + // + // This is a counter and is not tied to the invariants of any other class, so + // it can be operated on safely using std::memory_order_relaxed. + std::atomic acquires_allowed_; }; class PosixSequentialFile: public SequentialFile { From 89af27bde59fbbb3025653812b45fec10a655cb7 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 4 Sep 2018 09:44:56 -0700 Subject: [PATCH 107/174] Remove ssize_t from code that is not POSIX-specific. ssize_t is not standard C++. It is a POSIX extension. Therefore, it does not belong in generic code. This change tweaks the logic in DBIter to remove the need for signed integers, so ssize_t can be replaced with size_t. The impacted method and private member are renamed to better express their purpose. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211471606 --- db/db_iter.cc | 19 +++++++++++-------- db/fault_injection_test.cc | 8 ++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/db/db_iter.cc b/db/db_iter.cc index 3b2035e9e3..4d0f42e424 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -57,7 +57,7 @@ class DBIter: public Iterator { direction_(kForward), valid_(false), rnd_(seed), - bytes_counter_(RandomPeriod()) { + bytes_until_read_sampling_(RandomCompactionPeriod()) { } virtual ~DBIter() { delete iter_; @@ -103,8 +103,8 @@ class DBIter: public Iterator { } } - // Pick next gap with average value of config::kReadBytesPeriod. - ssize_t RandomPeriod() { + // Picks the number of bytes that can be read until a compaction is scheduled. + size_t RandomCompactionPeriod() { return rnd_.Uniform(2*config::kReadBytesPeriod); } @@ -120,7 +120,7 @@ class DBIter: public Iterator { bool valid_; Random rnd_; - ssize_t bytes_counter_; + size_t bytes_until_read_sampling_; // No copying allowed DBIter(const DBIter&); @@ -129,12 +129,15 @@ class DBIter: public Iterator { inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { Slice k = iter_->key(); - ssize_t n = k.size() + iter_->value().size(); - bytes_counter_ -= n; - while (bytes_counter_ < 0) { - bytes_counter_ += RandomPeriod(); + + size_t bytes_read = k.size() + iter_->value().size(); + while (bytes_until_read_sampling_ < bytes_read) { + bytes_until_read_sampling_ += RandomCompactionPeriod(); db_->RecordReadSample(k); } + assert(bytes_until_read_sampling_ >= bytes_read); + bytes_until_read_sampling_ -= bytes_read; + if (!ParseInternalKey(k, ikey)) { status_ = Status::Corruption("corrupted internal key in DBIter"); return false; diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index 7894999ccf..b3429ac922 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -85,9 +85,9 @@ Status Truncate(const std::string& filename, uint64_t length) { struct FileState { std::string filename_; - ssize_t pos_; - ssize_t pos_at_last_sync_; - ssize_t pos_at_last_flush_; + int64_t pos_; + int64_t pos_at_last_sync_; + int64_t pos_at_last_flush_; FileState(const std::string& filename) : filename_(filename), @@ -360,7 +360,7 @@ void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) { } Status FileState::DropUnsyncedData() const { - ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_; + int64_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_; return Truncate(filename_, sync_pos); } From 7b945f200339aa47c24788d3ee9910c09c513843 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 4 Sep 2018 09:50:50 -0700 Subject: [PATCH 108/174] Clean up posix_logger.h. General cleanup principles: * Use override when applicable. * Use const on class members where possible. * Renames where clarity can be improved. * Qualify standard library names with std:: when possible, to distinguish from POSIX names. * Qualify POSIX names with the global namespace (::) when possible, to distinguish from standard library names. This also revamps the logic for putting together a message into the in-memory buffer before that is passed to fwrite(). While correct in practice, the current implementation advances a char pointer past the size of its buffer, which is technically undefined behavior. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211472570 --- util/posix_logger.h | 158 ++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 64 deletions(-) diff --git a/util/posix_logger.h b/util/posix_logger.h index 1909e61282..a01a4feb54 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -8,89 +8,119 @@ #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ -#include -#include #include -#include + +#include +#include +#include +#include +#include + #include "leveldb/env.h" namespace leveldb { -class PosixLogger : public Logger { - private: - FILE* file_; - uint64_t (*gettid_)(); // Return the thread id for the current thread +class PosixLogger final : public Logger { public: - PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } - virtual ~PosixLogger() { - fclose(file_); + PosixLogger(FILE* fp, uint64_t (*gettid)()) : fp_(fp), gettid_(gettid) { + assert(fp != nullptr); + } + + ~PosixLogger() override { + std::fclose(fp_); } - virtual void Logv(const char* format, va_list ap) { + + void Logv(const char* format, va_list arguments) override { + // Record the time as close to the Logv() call as possible. + struct ::timeval now_timeval; + ::gettimeofday(&now_timeval, nullptr); + const std::time_t now_seconds = now_timeval.tv_sec; + struct std::tm now_components; + ::localtime_r(&now_seconds, &now_components); + const uint64_t thread_id = (*gettid_)(); - // We try twice: the first time with a fixed-size stack allocated buffer, - // and the second time with a much larger dynamically allocated buffer. - char buffer[500]; - for (int iter = 0; iter < 2; iter++) { - char* base; - int bufsize; - if (iter == 0) { - bufsize = sizeof(buffer); - base = buffer; - } else { - bufsize = 30000; - base = new char[bufsize]; - } - char* p = base; - char* limit = base + bufsize; - - struct timeval now_tv; - gettimeofday(&now_tv, nullptr); - const time_t seconds = now_tv.tv_sec; - struct tm t; - localtime_r(&seconds, &t); - p += snprintf(p, limit - p, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", - t.tm_year + 1900, - t.tm_mon + 1, - t.tm_mday, - t.tm_hour, - t.tm_min, - t.tm_sec, - static_cast(now_tv.tv_usec), - static_cast(thread_id)); - - // Print the message - if (p < limit) { - va_list backup_ap; - va_copy(backup_ap, ap); - p += vsnprintf(p, limit - p, format, backup_ap); - va_end(backup_ap); - } + // We first attempt to print into a stack-allocated buffer. If this attempt + // fails, we make a second attempt with a dynamically allocated buffer. + constexpr const int kStackBufferSize = 512; + char stack_buffer[kStackBufferSize]; + static_assert(sizeof(stack_buffer) == static_cast(kStackBufferSize), + "sizeof(char) is expected to be 1 in C++"); - // Truncate to available space if necessary - if (p >= limit) { - if (iter == 0) { - continue; // Try again with larger buffer - } else { - p = limit - 1; + int dynamic_buffer_size = 0; // Computed in the first iteration. + for (int iteration = 0; iteration < 2; ++iteration) { + const int buffer_size = + (iteration == 0) ? kStackBufferSize : dynamic_buffer_size; + char* const buffer = + (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; + + // Print the header into the buffer. + int buffer_offset = snprintf( + buffer, buffer_size, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %" PRIx64 " ", + now_components.tm_year + 1900, + now_components.tm_mon + 1, + now_components.tm_mday, + now_components.tm_hour, + now_components.tm_min, + now_components.tm_sec, + static_cast(now_timeval.tv_usec), + thread_id); + + // The header can be at most 48 characters (10 date + 15 time + 3 spacing + // + 20 thread ID), which should fit comfortably into the static buffer. + assert(buffer_offset <= 48); + static_assert(48 < kStackBufferSize, + "stack-allocated buffer may not fit the message header"); + assert(buffer_offset < buffer_size); + + // Print the message into the buffer. + std::va_list arguments_copy; + va_copy(arguments_copy, arguments); + buffer_offset += std::vsnprintf(buffer + buffer_offset, + buffer_size - buffer_offset, format, + arguments_copy); + va_end(arguments_copy); + + // The code below may append a newline at the end of the buffer, which + // requires an extra character. + if (buffer_offset >= buffer_size - 1) { + // The message did not fit into the buffer. + if (iteration == 0) { + // Re-run the loop and use a dynamically-allocated buffer. The buffer + // will be large enough for the log message, an extra newline and a + // null terminator. + dynamic_buffer_size = buffer_offset + 2; + continue; } + + // The dynamically-allocated buffer was incorrectly sized. This should + // not happen, assuming a correct implementation of (v)snprintf. Fail + // in tests, recover by truncating the log message in production. + assert(false); + buffer_offset = buffer_size - 1; } - // Add newline if necessary - if (p == base || p[-1] != '\n') { - *p++ = '\n'; + // Add a newline if necessary. + if (buffer[buffer_offset - 1] != '\n') { + buffer[buffer_offset] = '\n'; + ++buffer_offset; } - assert(p <= limit); - fwrite(base, 1, p - base, file_); - fflush(file_); - if (base != buffer) { - delete[] base; + assert(buffer_offset <= buffer_size); + std::fwrite(buffer, 1, buffer_offset, fp_); + std::fflush(fp_); + + if (iteration != 0) { + delete[] buffer; } break; } } + + private: + std::FILE* const fp_; + uint64_t (* const gettid_)(); // Return the thread id for the current thread. }; } // namespace leveldb From bb88f25115d20a6d73dfb6b16cc298db2f66948b Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 5 Sep 2018 15:23:28 -0700 Subject: [PATCH 109/174] Clean up PosixWritableFile in env_posix.cc. This is separated from the general cleanup because of the logic changes in SyncDirIfManifest(). General cleanup principles: * Use override when applicable. * Remove static when redundant (methods and globals in anonymous namespaces). * Use const on class members where possible. * Standardize on "status" for Status local variables. * Renames where clarity can be improved. * Qualify standard library names with std:: when possible, to distinguish from POSIX names. * Qualify POSIX names with the global namespace (::) when possible, to distinguish from standard library names. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=211709673 --- util/env_posix.cc | 199 +++++++++++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 82 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 18e766417f..b201c5be0c 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,7 @@ namespace { static int open_read_only_file_limit = -1; static int mmap_limit = -1; -static const size_t kBufSize = 65536; +constexpr const size_t kWritableFileBufferSize = 65536; static Status PosixError(const std::string& context, int err_number) { if (err_number == ENOENT) { @@ -213,131 +214,165 @@ class PosixMmapReadableFile: public RandomAccessFile { } }; -class PosixWritableFile : public WritableFile { - private: - // buf_[0, pos_-1] contains data to be written to fd_. - std::string filename_; - int fd_; - char buf_[kBufSize]; - size_t pos_; - +class PosixWritableFile final : public WritableFile { public: - PosixWritableFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd), pos_(0) { } + PosixWritableFile(std::string filename, int fd) + : pos_(0), fd_(fd), is_manifest_(IsManifest(filename)), + filename_(std::move(filename)), dirname_(Dirname(filename_)) {} - ~PosixWritableFile() { + ~PosixWritableFile() override { if (fd_ >= 0) { // Ignoring any potential errors Close(); } } - virtual Status Append(const Slice& data) { - size_t n = data.size(); - const char* p = data.data(); + Status Append(const Slice& data) override { + size_t write_size = data.size(); + const char* write_data = data.data(); // Fit as much as possible into buffer. - size_t copy = std::min(n, kBufSize - pos_); - memcpy(buf_ + pos_, p, copy); - p += copy; - n -= copy; - pos_ += copy; - if (n == 0) { + size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_); + std::memcpy(buf_ + pos_, write_data, copy_size); + write_data += copy_size; + write_size -= copy_size; + pos_ += copy_size; + if (write_size == 0) { return Status::OK(); } // Can't fit in buffer, so need to do at least one write. - Status s = FlushBuffered(); - if (!s.ok()) { - return s; + Status status = FlushBuffer(); + if (!status.ok()) { + return status; } // Small writes go to buffer, large writes are written directly. - if (n < kBufSize) { - memcpy(buf_, p, n); - pos_ = n; + if (write_size < kWritableFileBufferSize) { + std::memcpy(buf_, write_data, write_size); + pos_ = write_size; return Status::OK(); } - return WriteRaw(p, n); + return WriteUnbuffered(write_data, write_size); } - virtual Status Close() { - Status result = FlushBuffered(); - const int r = close(fd_); - if (r < 0 && result.ok()) { - result = PosixError(filename_, errno); + Status Close() override { + Status status = FlushBuffer(); + const int close_result = ::close(fd_); + if (close_result < 0 && status.ok()) { + status = PosixError(filename_, errno); } fd_ = -1; - return result; + return status; } - virtual Status Flush() { - return FlushBuffered(); - } - - Status SyncDirIfManifest() { - const char* f = filename_.c_str(); - const char* sep = strrchr(f, '/'); - Slice basename; - std::string dir; - if (sep == nullptr) { - dir = "."; - basename = f; - } else { - dir = std::string(f, sep - f); - basename = sep + 1; - } - Status s; - if (basename.starts_with("MANIFEST")) { - int fd = open(dir.c_str(), O_RDONLY); - if (fd < 0) { - s = PosixError(dir, errno); - } else { - if (fsync(fd) < 0) { - s = PosixError(dir, errno); - } - close(fd); - } - } - return s; + Status Flush() override { + return FlushBuffer(); } - virtual Status Sync() { + Status Sync() override { // Ensure new files referred to by the manifest are in the filesystem. - Status s = SyncDirIfManifest(); - if (!s.ok()) { - return s; + // + // This needs to happen before the manifest file is flushed to disk, to + // avoid crashing in a state where the manifest refers to files that are not + // yet on disk. + Status status = SyncDirIfManifest(); + if (!status.ok()) { + return status; } - s = FlushBuffered(); - if (s.ok()) { - if (fdatasync(fd_) != 0) { - s = PosixError(filename_, errno); - } + + status = FlushBuffer(); + if (status.ok() && ::fdatasync(fd_) != 0) { + status = PosixError(filename_, errno); } - return s; + return status; } private: - Status FlushBuffered() { - Status s = WriteRaw(buf_, pos_); + Status FlushBuffer() { + Status status = WriteUnbuffered(buf_, pos_); pos_ = 0; - return s; + return status; } - Status WriteRaw(const char* p, size_t n) { - while (n > 0) { - ssize_t r = write(fd_, p, n); - if (r < 0) { + Status WriteUnbuffered(const char* data, size_t size) { + while (size > 0) { + ssize_t write_result = ::write(fd_, data, size); + if (write_result < 0) { if (errno == EINTR) { continue; // Retry } return PosixError(filename_, errno); } - p += r; - n -= r; + data += write_result; + size -= write_result; } return Status::OK(); } + + Status SyncDirIfManifest() { + Status status; + if (!is_manifest_) { + return status; + } + + int fd = ::open(dirname_.c_str(), O_RDONLY); + if (fd < 0) { + status = PosixError(dirname_, errno); + } else { + if (::fsync(fd) < 0) { + status = PosixError(dirname_, errno); + } + ::close(fd); + } + return status; + } + + // Returns the directory name in a path pointing to a file. + // + // Returns "." if the path does not contain any directory separator. + static std::string Dirname(const std::string& filename) { + std::string::size_type separator_pos = filename.rfind('/'); + if (separator_pos == std::string::npos) { + return std::string("."); + } + // The filename component should not contain a path separator. If it does, + // the splitting was done incorrectly. + assert(filename.find('/', separator_pos + 1) == std::string::npos); + + return filename.substr(0, separator_pos); + } + + // Extracts the file name from a path pointing to a file. + // + // The returned Slice points to |filename|'s data buffer, so it is only valid + // while |filename| is alive and unchanged. + static Slice Basename(const std::string& filename) { + std::string::size_type separator_pos = filename.rfind('/'); + if (separator_pos == std::string::npos) { + return Slice(filename); + } + // The filename component should not contain a path separator. If it does, + // the splitting was done incorrectly. + assert(filename.find('/', separator_pos + 1) == std::string::npos); + + return Slice(filename.data() + separator_pos + 1, + filename.length() - separator_pos - 1); + } + + // True if the given file is a manifest file. + static bool IsManifest(const std::string& filename) { + return Basename(filename).starts_with("MANIFEST"); + } + + // buf_[0, pos_ - 1] contains data to be written to fd_. + char buf_[kWritableFileBufferSize]; + size_t pos_; + int fd_; + + const bool is_manifest_; // True if the file's name starts with MANIFEST. + const std::string filename_; + const std::string dirname_; // The directory of filename_. }; static int LockOrUnlock(int fd, bool lock) { From 05709fb43eea34936c9f535edcb74d5e91a0b495 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 10 Sep 2018 15:38:12 -0700 Subject: [PATCH 110/174] Remove InitOnce from the port API. This is not an API-breaking change, because it reduces the API that the leveldb embedder must implement. The project will build just fine against ports that still implement InitOnce. C++11 guarantees thread-safe initialization of static variables inside functions. This is a more restricted form of std::call_once or pthread_once_t (e.g., single call site), so the compiler might be able to generate better code [1]. Equally important, having less code in port_example.h makes it easier to port to other platforms. Due to the change above, this CL introduces a new approach for storing the singleton BytewiseComparatorImpl instance returned by BytewiseComparator(). The new approach avoids a dynamic memory allocation, which eliminates the false positive from LeakSanitizer reported in https://github.com/google/leveldb/issues/200 [1] https://stackoverflow.com/a/27206650/ ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212348004 --- CMakeLists.txt | 2 ++ port/port_example.h | 10 -------- port/port_stdcxx.h | 8 ------- util/comparator.cc | 17 +++++-------- util/no_destructor.h | 47 ++++++++++++++++++++++++++++++++++++ util/no_destructor_test.cc | 49 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 util/no_destructor.h create mode 100644 util/no_destructor_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index d49c31e352..36d6cbd9ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ target_sources(leveldb "${PROJECT_SOURCE_DIR}/util/logging.cc" "${PROJECT_SOURCE_DIR}/util/logging.h" "${PROJECT_SOURCE_DIR}/util/mutexlock.h" + "${PROJECT_SOURCE_DIR}/util/no_destructor.h" "${PROJECT_SOURCE_DIR}/util/options.cc" "${PROJECT_SOURCE_DIR}/util/random.h" "${PROJECT_SOURCE_DIR}/util/status.cc" @@ -278,6 +279,7 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("${PROJECT_SOURCE_DIR}/util/env_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/status_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/util/no_destructor_test.cc") if(NOT BUILD_SHARED_LIBS) leveldb_test("${PROJECT_SOURCE_DIR}/db/autocompact_test.cc") diff --git a/port/port_example.h b/port/port_example.h index 88fc9cbc7b..9c648c374b 100644 --- a/port/port_example.h +++ b/port/port_example.h @@ -62,16 +62,6 @@ class CondVar { void SignallAll(); }; -// Thread-safe initialization. -// Used as follows: -// static port::OnceType init_control = LEVELDB_ONCE_INIT; -// static void Initializer() { ... do something ...; } -// ... -// port::InitOnce(&init_control, &Initializer); -typedef intptr_t OnceType; -#define LEVELDB_ONCE_INIT 0 -void InitOnce(port::OnceType*, void (*initializer)()); - // A type that holds a pointer that can be read or written atomically // (i.e., without word-tearing.) class AtomicPointer { diff --git a/port/port_stdcxx.h b/port/port_stdcxx.h index 4e58cba2c2..4713e26c41 100644 --- a/port/port_stdcxx.h +++ b/port/port_stdcxx.h @@ -84,14 +84,6 @@ class CondVar { Mutex* const mu_; }; -using OnceType = std::once_flag; -#define LEVELDB_ONCE_INIT {} - -// Thinly wraps std::call_once. -inline void InitOnce(OnceType* once, void (*initializer)()) { - std::call_once(*once, *initializer); -} - inline bool Snappy_Compress(const char* input, size_t length, ::std::string* output) { #if HAVE_SNAPPY diff --git a/util/comparator.cc b/util/comparator.cc index 4b7b5724ef..e1e2963307 100644 --- a/util/comparator.cc +++ b/util/comparator.cc @@ -3,11 +3,13 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include -#include +#include +#include + #include "leveldb/comparator.h" #include "leveldb/slice.h" -#include "port/port.h" #include "util/logging.h" +#include "util/no_destructor.h" namespace leveldb { @@ -66,16 +68,9 @@ class BytewiseComparatorImpl : public Comparator { }; } // namespace -static port::OnceType once = LEVELDB_ONCE_INIT; -static const Comparator* bytewise; - -static void InitModule() { - bytewise = new BytewiseComparatorImpl; -} - const Comparator* BytewiseComparator() { - port::InitOnce(&once, InitModule); - return bytewise; + static NoDestructor singleton; + return singleton.get(); } } // namespace leveldb diff --git a/util/no_destructor.h b/util/no_destructor.h new file mode 100644 index 0000000000..4827e4579e --- /dev/null +++ b/util/no_destructor.h @@ -0,0 +1,47 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ +#define STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ + +#include +#include + +namespace leveldb { + +// Wraps an instance whose destructor is never called. +// +// This is intended for use with function-level static variables. +template +class NoDestructor { + public: + template + explicit NoDestructor(ConstructorArgTypes&&... constructor_args) { + static_assert(sizeof(instance_storage_) >= sizeof(InstanceType), + "instance_storage_ is not large enough to hold the instance"); + static_assert( + alignof(decltype(instance_storage_)) >= alignof(InstanceType), + "instance_storage_ does not meet the instance's alignment requirement"); + new (&instance_storage_) InstanceType( + std::forward(constructor_args)...); + } + + ~NoDestructor() = default; + + NoDestructor(const NoDestructor&) = delete; + NoDestructor& operator=(const NoDestructor&) = delete; + + InstanceType* get() { + return reinterpret_cast(&instance_storage_); + } + + private: + typename + std::aligned_storage::type + instance_storage_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ diff --git a/util/no_destructor_test.cc b/util/no_destructor_test.cc new file mode 100644 index 0000000000..7ce26311de --- /dev/null +++ b/util/no_destructor_test.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "util/no_destructor.h" +#include "util/testharness.h" + +namespace leveldb { + +namespace { + +struct DoNotDestruct { + public: + DoNotDestruct(uint32_t a, uint64_t b) : a(a), b(b) {} + ~DoNotDestruct() { std::abort(); } + + // Used to check constructor argument forwarding. + uint32_t a; + uint64_t b; +}; + +constexpr const uint32_t kGoldenA = 0xdeadbeef; +constexpr const uint64_t kGoldenB = 0xaabbccddeeffaabb; + +} // namespace + +class NoDestructorTest { }; + +TEST(NoDestructorTest, StackInstance) { + NoDestructor instance(kGoldenA, kGoldenB); + ASSERT_EQ(kGoldenA, instance.get()->a); + ASSERT_EQ(kGoldenB, instance.get()->b); +} + +TEST(NoDestructorTest, StaticInstance) { + static NoDestructor instance(kGoldenA, kGoldenB); + ASSERT_EQ(kGoldenA, instance.get()->a); + ASSERT_EQ(kGoldenB, instance.get()->b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} From 73d5834eceee8efa9a8ccfec77dc096a9e8ba18a Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 11 Sep 2018 10:39:08 -0700 Subject: [PATCH 111/174] Rework threading in env_posix.cc. This commit replaces the use of pthreads in the POSIX port with std::thread and port::Mutex + port::CondVar. This is intended to simplify porting the env to a different platform. The indirect use of pthreads in PosixLogger is replaced with std::thread::id(), based on an approach prototyped by @cmumfordx@. The pthreads dependency in CMakeFiles is not removed, because some C++ standard library implementations must be linked against pthreads for std::thread use. Figuring out this dependency is left for future work. Switching away from pthreads also fixes https://github.com/google/leveldb/issues/381 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212478311 --- util/env_posix.cc | 140 +++++++++++++++++++------------------------- util/posix_logger.h | 31 ++++++---- 2 files changed, 80 insertions(+), 91 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index b201c5be0c..d6b0d617e7 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -19,9 +18,10 @@ #include #include -#include #include +#include #include +#include #include "leveldb/env.h" #include "leveldb/slice.h" @@ -600,20 +600,13 @@ class PosixEnv : public Env { return Status::OK(); } - static uint64_t gettid() { - pthread_t tid = pthread_self(); - uint64_t thread_id = 0; - memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); - return thread_id; - } - virtual Status NewLogger(const std::string& fname, Logger** result) { FILE* f = fopen(fname.c_str(), "w"); if (f == nullptr) { *result = nullptr; return PosixError(fname, errno); } else { - *result = new PosixLogger(f, &PosixEnv::gettid); + *result = new PosixLogger(f); return Status::OK(); } } @@ -629,29 +622,33 @@ class PosixEnv : public Env { } private: - void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } - } + void BackgroundThreadMain(); - // BGThread() is the body of the background thread - void BGThread(); - static void* BGThreadWrapper(void* arg) { - reinterpret_cast(arg)->BGThread(); - return nullptr; + static void BackgroundThreadEntryPoint(PosixEnv* env) { + env->BackgroundThreadMain(); } - pthread_mutex_t mu_; - pthread_cond_t bgsignal_; - pthread_t bgthread_; - bool started_bgthread_; + // Stores the work item data in a Schedule() call. + // + // Instances are constructed on the thread calling Schedule() and used on the + // background thread. + // + // This structure is thread-safe beacuse it is immutable. + struct BackgroundWorkItem { + explicit BackgroundWorkItem(void (*function)(void* arg), void* arg) + : function(function), arg(arg) {} + + void (* const function)(void*); + void* const arg; + }; + + + port::Mutex background_work_mutex_; + port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_); + bool started_background_thread_ GUARDED_BY(background_work_mutex_); - // Entry per Schedule() call - struct BGItem { void* arg; void (*function)(void*); }; - typedef std::deque BGQueue; - BGQueue queue_; + std::queue background_work_queue_ + GUARDED_BY(background_work_mutex_); PosixLockTable locks_; Limiter mmap_limit_; @@ -687,79 +684,60 @@ static intptr_t MaxOpenFiles() { } PosixEnv::PosixEnv() - : started_bgthread_(false), + : background_work_cv_(&background_work_mutex_), + started_background_thread_(false), mmap_limit_(MaxMmaps()), fd_limit_(MaxOpenFiles()) { - PthreadCall("mutex_init", pthread_mutex_init(&mu_, nullptr)); - PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, nullptr)); } -void PosixEnv::Schedule(void (*function)(void*), void* arg) { - PthreadCall("lock", pthread_mutex_lock(&mu_)); +void PosixEnv::Schedule( + void (*background_work_function)(void* background_work_arg), + void* background_work_arg) { + MutexLock lock(&background_work_mutex_); - // Start background thread if necessary - if (!started_bgthread_) { - started_bgthread_ = true; - PthreadCall( - "create thread", - pthread_create(&bgthread_, nullptr, &PosixEnv::BGThreadWrapper, this)); + // Start the background thread, if we haven't done so already. + if (!started_background_thread_) { + started_background_thread_ = true; + std::thread background_thread(PosixEnv::BackgroundThreadEntryPoint, this); + background_thread.detach(); } - // If the queue is currently empty, the background thread may currently be - // waiting. - if (queue_.empty()) { - PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + // If the queue is empty, the background thread may be waiting for work. + if (background_work_queue_.empty()) { + background_work_cv_.Signal(); } - // Add to priority queue - queue_.push_back(BGItem()); - queue_.back().function = function; - queue_.back().arg = arg; - - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + background_work_queue_.emplace(background_work_function, background_work_arg); } -void PosixEnv::BGThread() { +void PosixEnv::BackgroundThreadMain() { while (true) { - // Wait until there is an item that is ready to run - PthreadCall("lock", pthread_mutex_lock(&mu_)); - while (queue_.empty()) { - PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + background_work_mutex_.Lock(); + + // Wait until there is work to be done. + while (background_work_queue_.empty()) { + background_work_cv_.Wait(); } - void (*function)(void*) = queue_.front().function; - void* arg = queue_.front().arg; - queue_.pop_front(); + assert(!background_work_queue_.empty()); + auto background_work_function = + background_work_queue_.front().function; + void* background_work_arg = background_work_queue_.front().arg; + background_work_queue_.pop(); - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); - (*function)(arg); + background_work_mutex_.Unlock(); + background_work_function(background_work_arg); } } -namespace { -struct StartThreadState { - void (*user_function)(void*); - void* arg; -}; -} -static void* StartThreadWrapper(void* arg) { - StartThreadState* state = reinterpret_cast(arg); - state->user_function(state->arg); - delete state; - return nullptr; -} +} // namespace -void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { - pthread_t t; - StartThreadState* state = new StartThreadState; - state->user_function = function; - state->arg = arg; - PthreadCall("start thread", - pthread_create(&t, nullptr, &StartThreadWrapper, state)); +void PosixEnv::StartThread(void (*thread_main)(void* thread_main_arg), + void* thread_main_arg) { + std::thread new_thread(thread_main, thread_main_arg); + new_thread.detach(); } -} // namespace - static pthread_once_t once = PTHREAD_ONCE_INIT; static Env* default_env; static void InitDefaultEnv() { default_env = new PosixEnv; } diff --git a/util/posix_logger.h b/util/posix_logger.h index a01a4feb54..28b290e1bb 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -11,10 +11,11 @@ #include #include -#include #include #include #include +#include +#include #include "leveldb/env.h" @@ -22,7 +23,10 @@ namespace leveldb { class PosixLogger final : public Logger { public: - PosixLogger(FILE* fp, uint64_t (*gettid)()) : fp_(fp), gettid_(gettid) { + // Creates a logger that writes to the given file. + // + // The PosixLogger instance takes ownership of the file handle. + explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } @@ -38,7 +42,14 @@ class PosixLogger final : public Logger { struct std::tm now_components; ::localtime_r(&now_seconds, &now_components); - const uint64_t thread_id = (*gettid_)(); + // Record the thread ID. + constexpr const int kMaxThreadIdSize = 32; + std::ostringstream thread_stream; + thread_stream << std::this_thread::get_id(); + std::string thread_id = thread_stream.str(); + if (thread_id.size() > kMaxThreadIdSize) { + thread_id.resize(kMaxThreadIdSize); + } // We first attempt to print into a stack-allocated buffer. If this attempt // fails, we make a second attempt with a dynamically allocated buffer. @@ -57,7 +68,7 @@ class PosixLogger final : public Logger { // Print the header into the buffer. int buffer_offset = snprintf( buffer, buffer_size, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %" PRIx64 " ", + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s", now_components.tm_year + 1900, now_components.tm_mon + 1, now_components.tm_mday, @@ -65,12 +76,13 @@ class PosixLogger final : public Logger { now_components.tm_min, now_components.tm_sec, static_cast(now_timeval.tv_usec), - thread_id); + thread_id.c_str()); - // The header can be at most 48 characters (10 date + 15 time + 3 spacing - // + 20 thread ID), which should fit comfortably into the static buffer. - assert(buffer_offset <= 48); - static_assert(48 < kStackBufferSize, + // The header can be at most 28 characters (10 date + 15 time + + // 3 spacing) plus the thread ID, which should fit comfortably into the + // static buffer. + assert(buffer_offset <= 28 + kMaxThreadIdSize); + static_assert(28 + kMaxThreadIdSize < kStackBufferSize, "stack-allocated buffer may not fit the message header"); assert(buffer_offset < buffer_size); @@ -120,7 +132,6 @@ class PosixLogger final : public Logger { private: std::FILE* const fp_; - uint64_t (* const gettid_)(); // Return the thread id for the current thread. }; } // namespace leveldb From 0145a94ab6bec48e596df499e8f6103e138a74ab Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 12 Sep 2018 10:23:16 -0700 Subject: [PATCH 112/174] Update .gitignore. The version in the repository covers the Makefile build. The new version is simpler and contains entries relevant to the CMake build. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212661504 --- .gitignore | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 063025144b..c4b242534f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ -build_config.mk -*.a -*.o -*.dylib* -*.so -*.so.* -*_test -db_bench -leveldbutil +# Editors. +*.sw* +.vscode +.DS_Store + +# Build directory. +build/ +out/ From c43565dd398b2233db8eb49ba05234d62fb42e03 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 17 Sep 2018 23:05:57 -0700 Subject: [PATCH 113/174] C++11 cleanup for util/mutexlock.h. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=213398583 --- util/mutexlock.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/mutexlock.h b/util/mutexlock.h index 1ff5a9efa1..08d709a538 100644 --- a/util/mutexlock.h +++ b/util/mutexlock.h @@ -28,11 +28,11 @@ class SCOPED_LOCKABLE MutexLock { } ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; + private: port::Mutex *const mu_; - // No copying allowed - MutexLock(const MutexLock&); - void operator=(const MutexLock&); }; } // namespace leveldb From a7dc502e9f11c2e5c911ba45b999676c43eaa51f Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 24 Sep 2018 10:50:40 -0700 Subject: [PATCH 114/174] Rework once initialization in env_posix.cc. C++11 guarantees thread-safe initialization of static variables inside functions. This is a more restricted form of std::call_once or pthread_once_t (e.g., single call site), so the compiler might be able to generate better code [1]. Equally important, having less platform-dependent code in env_posix.cc makes it easier to port to other platforms. Due to the change above, this CL introduced a new approach for storing the singleton PosixEnv instance returned by Env::Default(). The new approach avoids a dynamic memory allocation, which eliminates the false positive from LeakSanitizer reported in https://github.com/google/leveldb/issues/539 and https://github.com/google/leveldb/issues/113 [1] https://stackoverflow.com/a/27206650/ ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=214293129 --- util/env_posix.cc | 84 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index d6b0d617e7..68a88082a7 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -17,18 +17,21 @@ #include #include +#include +#include #include #include #include #include +#include #include +#include #include "leveldb/env.h" #include "leveldb/slice.h" +#include "leveldb/status.h" #include "port/port.h" #include "port/thread_annotations.h" -#include "util/logging.h" -#include "util/mutexlock.h" #include "util/posix_logger.h" #include "util/env_posix_test_helper.h" @@ -401,12 +404,15 @@ class PosixLockTable { std::set locked_files_ GUARDED_BY(mu_); public: bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) { - MutexLock l(&mu_); - return locked_files_.insert(fname).second; + mu_.Lock(); + bool succeeded = locked_files_.insert(fname).second; + mu_.Unlock(); + return succeeded; } void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) { - MutexLock l(&mu_); + mu_.Lock(); locked_files_.erase(fname); + mu_.Unlock(); } }; @@ -693,7 +699,7 @@ PosixEnv::PosixEnv() void PosixEnv::Schedule( void (*background_work_function)(void* background_work_arg), void* background_work_arg) { - MutexLock lock(&background_work_mutex_); + background_work_mutex_.Lock(); // Start the background thread, if we haven't done so already. if (!started_background_thread_) { @@ -708,6 +714,7 @@ void PosixEnv::Schedule( } background_work_queue_.emplace(background_work_function, background_work_arg); + background_work_mutex_.Unlock(); } void PosixEnv::BackgroundThreadMain() { @@ -730,6 +737,59 @@ void PosixEnv::BackgroundThreadMain() { } } +// Wraps an Env instance whose destructor is never created. +// +// Intended usage: +// using PlatformSingletonEnv = SingletonEnv; +// void ConfigurePosixEnv(int param) { +// PlatformSingletonEnv::AssertEnvNotInitialized(); +// // set global configuration flags. +// } +// Env* Env::Default() { +// static PlatformSingletonEnv default_env; +// return default_env.env(); +// } +template +class SingletonEnv { + public: + SingletonEnv() { +#if !defined(NDEBUG) + env_initialized_.store(true, std::memory_order::memory_order_relaxed); +#endif // !defined(NDEBUG) + static_assert(sizeof(env_storage_) >= sizeof(EnvType), + "env_storage_ will not fit the Env"); + static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType), + "env_storage_ does not meet the Env's alignment needs"); + new (&env_storage_) EnvType(); + } + ~SingletonEnv() = default; + + SingletonEnv(const SingletonEnv&) = delete; + SingletonEnv& operator=(const SingletonEnv&) = delete; + + Env* env() { return reinterpret_cast(&env_storage_); } + + static void AssertEnvNotInitialized() { +#if !defined(NDEBUG) + assert(!env_initialized_.load(std::memory_order::memory_order_relaxed)); +#endif // !defined(NDEBUG) + } + + private: + typename std::aligned_storage::type + env_storage_; +#if !defined(NDEBUG) + static std::atomic env_initialized_; +#endif // !defined(NDEBUG) +}; + +#if !defined(NDEBUG) +template +std::atomic SingletonEnv::env_initialized_; +#endif // !defined(NDEBUG) + +using PosixDefaultEnv = SingletonEnv; + } // namespace void PosixEnv::StartThread(void (*thread_main)(void* thread_main_arg), @@ -738,23 +798,19 @@ void PosixEnv::StartThread(void (*thread_main)(void* thread_main_arg), new_thread.detach(); } -static pthread_once_t once = PTHREAD_ONCE_INIT; -static Env* default_env; -static void InitDefaultEnv() { default_env = new PosixEnv; } - void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { - assert(default_env == nullptr); + PosixDefaultEnv::AssertEnvNotInitialized(); open_read_only_file_limit = limit; } void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { - assert(default_env == nullptr); + PosixDefaultEnv::AssertEnvNotInitialized(); mmap_limit = limit; } Env* Env::Default() { - pthread_once(&once, InitDefaultEnv); - return default_env; + static PosixDefaultEnv env_container; + return env_container.env(); } } // namespace leveldb From 1cb384088184be9840bd59b4040503a9fa9aee66 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 29 Oct 2018 16:17:46 -0700 Subject: [PATCH 115/174] Clean up env_posix.cc. General cleanup principles: * Use override when applicable. * Remove static when redundant (methods and globals in anonymous namespaces). * Use const on class members where possible. * Standardize on "status" for Status local variables. * Renames where clarity can be improved. * Qualify standard library names with std:: when possible, to distinguish from POSIX names. * Qualify POSIX names with the global namespace (::) when possible, to distinguish from standard library names. This also refactors the background thread synchronization logic so that it's statically analyzable. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=219212089 --- util/env_posix.cc | 547 +++++++++++++++++++++++++--------------------- 1 file changed, 296 insertions(+), 251 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index 68a88082a7..76bb648503 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -3,22 +3,21 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include -#include #include #include -#include -#include #include #include #include #include #include -#include #include #include +#include #include #include +#include +#include #include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include "leveldb/env.h" #include "leveldb/slice.h" @@ -45,16 +45,22 @@ namespace leveldb { namespace { -static int open_read_only_file_limit = -1; -static int mmap_limit = -1; +// Set by EnvPosixTestHelper::SetReadOnlyMMapLimit() and MaxOpenFiles(). +int g_open_read_only_file_limit = -1; + +// Up to 1000 mmap regions for 64-bit binaries; none for 32-bit. +constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0; + +// Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit. +int g_mmap_limit = kDefaultMmapLimit; constexpr const size_t kWritableFileBufferSize = 65536; -static Status PosixError(const std::string& context, int err_number) { - if (err_number == ENOENT) { - return Status::NotFound(context, strerror(err_number)); +Status PosixError(const std::string& context, int error_number) { + if (error_number == ENOENT) { + return Status::NotFound(context, std::strerror(error_number)); } else { - return Status::IOError(context, strerror(err_number)); + return Status::IOError(context, std::strerror(error_number)); } } @@ -97,124 +103,147 @@ class Limiter { std::atomic acquires_allowed_; }; -class PosixSequentialFile: public SequentialFile { - private: - std::string filename_; - int fd_; - +// Implements sequential read access in a file using read(). +// +// Instances of this class are thread-friendly but not thread-safe, as required +// by the SequentialFile API. +class PosixSequentialFile final : public SequentialFile { public: - PosixSequentialFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd) {} - virtual ~PosixSequentialFile() { close(fd_); } + PosixSequentialFile(std::string filename, int fd) + : fd_(fd), filename_(filename) {} + ~PosixSequentialFile() override { close(fd_); } - virtual Status Read(size_t n, Slice* result, char* scratch) { - Status s; + Status Read(size_t n, Slice* result, char* scratch) override { + Status status; while (true) { - ssize_t r = read(fd_, scratch, n); - if (r < 0) { + ::ssize_t read_size = ::read(fd_, scratch, n); + if (read_size < 0) { // Read error. if (errno == EINTR) { continue; // Retry } - s = PosixError(filename_, errno); + status = PosixError(filename_, errno); break; } - *result = Slice(scratch, r); + *result = Slice(scratch, read_size); break; } - return s; + return status; } - virtual Status Skip(uint64_t n) { - if (lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { + Status Skip(uint64_t n) override { + if (::lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { return PosixError(filename_, errno); } return Status::OK(); } -}; -// pread() based random-access -class PosixRandomAccessFile: public RandomAccessFile { private: - std::string filename_; - bool temporary_fd_; // If true, fd_ is -1 and we open on every read. - int fd_; - Limiter* limiter_; + const int fd_; + const std::string filename_; +}; +// Implements random read access in a file using pread(). +// +// Instances of this class are thread-safe, as required by the RandomAccessFile +// API. Instances are immutable and Read() only calls thread-safe library +// functions. +class PosixRandomAccessFile final : public RandomAccessFile { public: - PosixRandomAccessFile(const std::string& fname, int fd, Limiter* limiter) - : filename_(fname), fd_(fd), limiter_(limiter) { - temporary_fd_ = !limiter->Acquire(); - if (temporary_fd_) { - // Open file on every access. - close(fd_); - fd_ = -1; + // The new instance takes ownership of |fd|. |fd_limiter| must outlive this + // instance, and will be used to determine if . + PosixRandomAccessFile(std::string filename, int fd, Limiter* fd_limiter) + : has_permanent_fd_(fd_limiter->Acquire()), + fd_(has_permanent_fd_ ? fd : -1), + fd_limiter_(fd_limiter), + filename_(std::move(filename)) { + if (!has_permanent_fd_) { + assert(fd_ == -1); + ::close(fd); // The file will be opened on every read. } } - virtual ~PosixRandomAccessFile() { - if (!temporary_fd_) { - close(fd_); - limiter_->Release(); + ~PosixRandomAccessFile() override { + if (has_permanent_fd_) { + assert(fd_ != -1); + ::close(fd_); + fd_limiter_->Release(); } } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { int fd = fd_; - if (temporary_fd_) { - fd = open(filename_.c_str(), O_RDONLY); + if (!has_permanent_fd_) { + fd = ::open(filename_.c_str(), O_RDONLY); if (fd < 0) { return PosixError(filename_, errno); } } - Status s; - ssize_t r = pread(fd, scratch, n, static_cast(offset)); - *result = Slice(scratch, (r < 0) ? 0 : r); - if (r < 0) { - // An error: return a non-ok status - s = PosixError(filename_, errno); + assert(fd != -1); + + Status status; + ssize_t read_size = ::pread(fd, scratch, n, static_cast(offset)); + *result = Slice(scratch, (read_size < 0) ? 0 : read_size); + if (read_size < 0) { + // An error: return a non-ok status. + status = PosixError(filename_, errno); } - if (temporary_fd_) { + if (!has_permanent_fd_) { // Close the temporary file descriptor opened earlier. - close(fd); + assert(fd != fd_); + ::close(fd); } - return s; + return status; } -}; -// mmap() based random-access -class PosixMmapReadableFile: public RandomAccessFile { private: - std::string filename_; - void* mmapped_region_; - size_t length_; - Limiter* limiter_; + const bool has_permanent_fd_; // If false, the file is opened on every read. + const int fd_; // -1 if has_permanent_fd_ is false. + Limiter* const fd_limiter_; + const std::string filename_; +}; +// Implements random read access in a file using mmap(). +// +// Instances of this class are thread-safe, as required by the RandomAccessFile +// API. Instances are immutable and Read() only calls thread-safe library +// functions. +class PosixMmapReadableFile final : public RandomAccessFile { public: - // base[0,length-1] contains the mmapped contents of the file. - PosixMmapReadableFile(const std::string& fname, void* base, size_t length, - Limiter* limiter) - : filename_(fname), mmapped_region_(base), length_(length), - limiter_(limiter) { - } + // mmap_base[0, length-1] points to the memory-mapped contents of the file. It + // must be the result of a successful call to mmap(). This instances takes + // over the ownership of the region. + // + // |mmap_limiter| must outlive this instance. The caller must have already + // aquired the right to use one mmap region, which will be released when this + // instance is destroyed. + PosixMmapReadableFile(std::string filename, char* mmap_base, size_t length, + Limiter* mmap_limiter) + : mmap_base_(mmap_base), length_(length), mmap_limiter_(mmap_limiter), + filename_(std::move(filename)) {} - virtual ~PosixMmapReadableFile() { - munmap(mmapped_region_, length_); - limiter_->Release(); + ~PosixMmapReadableFile() override { + ::munmap(static_cast(mmap_base_), length_); + mmap_limiter_->Release(); } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - Status s; + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { if (offset + n > length_) { *result = Slice(); - s = PosixError(filename_, EINVAL); - } else { - *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + return PosixError(filename_, EINVAL); } - return s; + + *result = Slice(mmap_base_ + offset, n); + return Status::OK(); } + + private: + char* const mmap_base_; + const size_t length_; + Limiter* const mmap_limiter_; + const std::string filename_; }; class PosixWritableFile final : public WritableFile { @@ -378,30 +407,39 @@ class PosixWritableFile final : public WritableFile { const std::string dirname_; // The directory of filename_. }; -static int LockOrUnlock(int fd, bool lock) { +int LockOrUnlock(int fd, bool lock) { errno = 0; - struct flock f; - memset(&f, 0, sizeof(f)); - f.l_type = (lock ? F_WRLCK : F_UNLCK); - f.l_whence = SEEK_SET; - f.l_start = 0; - f.l_len = 0; // Lock/unlock entire file - return fcntl(fd, F_SETLK, &f); + struct ::flock file_lock_info; + std::memset(&file_lock_info, 0, sizeof(file_lock_info)); + file_lock_info.l_type = (lock ? F_WRLCK : F_UNLCK); + file_lock_info.l_whence = SEEK_SET; + file_lock_info.l_start = 0; + file_lock_info.l_len = 0; // Lock/unlock entire file. + return ::fcntl(fd, F_SETLK, &file_lock_info); } +// Instances are thread-safe because they are immutable. class PosixFileLock : public FileLock { public: - int fd_; - std::string name_; + PosixFileLock(int fd, std::string filename) + : fd_(fd), filename_(std::move(filename)) {} + + int fd() const { return fd_; } + const std::string& filename() const { return filename_; } + + private: + const int fd_; + const std::string filename_; }; -// Set of locked files. We keep a separate set instead of just -// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide -// any protection against multiple uses from the same process. +// Tracks the files locked by PosixEnv::LockFile(). +// +// We maintain a separate set instead of relying on fcntrl(F_SETLK) because +// fcntl(F_SETLK) does not provide any protection against multiple uses from the +// same process. +// +// Instances are thread-safe because all member data is guarded by a mutex. class PosixLockTable { - private: - port::Mutex mu_; - std::set locked_files_ GUARDED_BY(mu_); public: bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) { mu_.Lock(); @@ -414,217 +452,225 @@ class PosixLockTable { locked_files_.erase(fname); mu_.Unlock(); } + + private: + port::Mutex mu_; + std::set locked_files_ GUARDED_BY(mu_); }; class PosixEnv : public Env { public: PosixEnv(); - virtual ~PosixEnv() { - char msg[] = "Destroying Env::Default()\n"; - fwrite(msg, 1, sizeof(msg), stderr); - abort(); + ~PosixEnv() override { + static char msg[] = "PosixEnv singleton destroyed. Unsupported behavior!\n"; + std::fwrite(msg, 1, sizeof(msg), stderr); + std::abort(); } - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) { - int fd = open(fname.c_str(), O_RDONLY); + Status NewSequentialFile(const std::string& filename, + SequentialFile** result) override { + int fd = ::open(filename.c_str(), O_RDONLY); if (fd < 0) { *result = nullptr; - return PosixError(fname, errno); - } else { - *result = new PosixSequentialFile(fname, fd); - return Status::OK(); + return PosixError(filename, errno); } + + *result = new PosixSequentialFile(filename, fd); + return Status::OK(); } - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) { + Status NewRandomAccessFile(const std::string& filename, + RandomAccessFile** result) override { *result = nullptr; - Status s; - int fd = open(fname.c_str(), O_RDONLY); + int fd = ::open(filename.c_str(), O_RDONLY); if (fd < 0) { - s = PosixError(fname, errno); - } else if (mmap_limit_.Acquire()) { - uint64_t size; - s = GetFileSize(fname, &size); - if (s.ok()) { - void* base = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); - if (base != MAP_FAILED) { - *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); - } else { - s = PosixError(fname, errno); - } - } - close(fd); - if (!s.ok()) { - mmap_limit_.Release(); + return PosixError(filename, errno); + } + + if (!mmap_limiter_.Acquire()) { + *result = new PosixRandomAccessFile(filename, fd, &fd_limiter_); + return Status::OK(); + } + + uint64_t file_size; + Status status = GetFileSize(filename, &file_size); + if (status.ok()) { + void* mmap_base = ::mmap(/*addr=*/nullptr, file_size, PROT_READ, + MAP_SHARED, fd, 0); + if (mmap_base != MAP_FAILED) { + *result = new PosixMmapReadableFile( + filename, reinterpret_cast(mmap_base), file_size, + &mmap_limiter_); + } else { + status = PosixError(filename, errno); } - } else { - *result = new PosixRandomAccessFile(fname, fd, &fd_limit_); } - return s; + ::close(fd); + if (!status.ok()) { + mmap_limiter_.Release(); + } + return status; } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { - Status s; - int fd = open(fname.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); + Status NewWritableFile(const std::string& filename, + WritableFile** result) override { + int fd = ::open(filename.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); if (fd < 0) { *result = nullptr; - s = PosixError(fname, errno); - } else { - *result = new PosixWritableFile(fname, fd); + return PosixError(filename, errno); } - return s; + + *result = new PosixWritableFile(filename, fd); + return Status::OK(); } - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result) { - Status s; - int fd = open(fname.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); + Status NewAppendableFile(const std::string& filename, + WritableFile** result) override { + int fd = ::open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); if (fd < 0) { *result = nullptr; - s = PosixError(fname, errno); - } else { - *result = new PosixWritableFile(fname, fd); + return PosixError(filename, errno); } - return s; + + *result = new PosixWritableFile(filename, fd); + return Status::OK(); } - virtual bool FileExists(const std::string& fname) { - return access(fname.c_str(), F_OK) == 0; + bool FileExists(const std::string& filename) override { + return ::access(filename.c_str(), F_OK) == 0; } - virtual Status GetChildren(const std::string& dir, - std::vector* result) { + Status GetChildren(const std::string& directory_path, + std::vector* result) override { result->clear(); - DIR* d = opendir(dir.c_str()); - if (d == nullptr) { - return PosixError(dir, errno); + ::DIR* dir = ::opendir(directory_path.c_str()); + if (dir == nullptr) { + return PosixError(directory_path, errno); } - struct dirent* entry; - while ((entry = readdir(d)) != nullptr) { - result->push_back(entry->d_name); + struct ::dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + result->emplace_back(entry->d_name); } - closedir(d); + ::closedir(dir); return Status::OK(); } - virtual Status DeleteFile(const std::string& fname) { - Status result; - if (unlink(fname.c_str()) != 0) { - result = PosixError(fname, errno); + Status DeleteFile(const std::string& filename) override { + if (::unlink(filename.c_str()) != 0) { + return PosixError(filename, errno); } - return result; + return Status::OK(); } - virtual Status CreateDir(const std::string& name) { - Status result; - if (mkdir(name.c_str(), 0755) != 0) { - result = PosixError(name, errno); + Status CreateDir(const std::string& dirname) override { + if (::mkdir(dirname.c_str(), 0755) != 0) { + return PosixError(dirname, errno); } - return result; + return Status::OK(); } - virtual Status DeleteDir(const std::string& name) { - Status result; - if (rmdir(name.c_str()) != 0) { - result = PosixError(name, errno); + Status DeleteDir(const std::string& dirname) override { + if (::rmdir(dirname.c_str()) != 0) { + return PosixError(dirname, errno); } - return result; + return Status::OK(); } - virtual Status GetFileSize(const std::string& fname, uint64_t* size) { - Status s; - struct stat sbuf; - if (stat(fname.c_str(), &sbuf) != 0) { + Status GetFileSize(const std::string& filename, uint64_t* size) override { + struct ::stat file_stat; + if (::stat(filename.c_str(), &file_stat) != 0) { *size = 0; - s = PosixError(fname, errno); - } else { - *size = sbuf.st_size; + return PosixError(filename, errno); } - return s; + *size = file_stat.st_size; + return Status::OK(); } - virtual Status RenameFile(const std::string& src, const std::string& target) { - Status result; - if (rename(src.c_str(), target.c_str()) != 0) { - result = PosixError(src, errno); + Status RenameFile(const std::string& from, const std::string& to) override { + if (std::rename(from.c_str(), to.c_str()) != 0) { + return PosixError(from, errno); } - return result; + return Status::OK(); } - virtual Status LockFile(const std::string& fname, FileLock** lock) { + Status LockFile(const std::string& filename, FileLock** lock) override { *lock = nullptr; - Status result; - int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + + int fd = ::open(filename.c_str(), O_RDWR | O_CREAT, 0644); if (fd < 0) { - result = PosixError(fname, errno); - } else if (!locks_.Insert(fname)) { - close(fd); - result = Status::IOError("lock " + fname, "already held by process"); - } else if (LockOrUnlock(fd, true) == -1) { - result = PosixError("lock " + fname, errno); - close(fd); - locks_.Remove(fname); - } else { - PosixFileLock* my_lock = new PosixFileLock; - my_lock->fd_ = fd; - my_lock->name_ = fname; - *lock = my_lock; + return PosixError(filename, errno); + } + + if (!locks_.Insert(filename)) { + ::close(fd); + return Status::IOError("lock " + filename, "already held by process"); + } + + if (LockOrUnlock(fd, true) == -1) { + int lock_errno = errno; + ::close(fd); + locks_.Remove(filename); + return PosixError("lock " + filename, lock_errno); } - return result; + + *lock = new PosixFileLock(fd, filename); + return Status::OK(); } - virtual Status UnlockFile(FileLock* lock) { - PosixFileLock* my_lock = reinterpret_cast(lock); - Status result; - if (LockOrUnlock(my_lock->fd_, false) == -1) { - result = PosixError("unlock", errno); + Status UnlockFile(FileLock* lock) override { + PosixFileLock* posix_file_lock = static_cast(lock); + if (LockOrUnlock(posix_file_lock->fd(), false) == -1) { + return PosixError("unlock " + posix_file_lock->filename(), errno); } - locks_.Remove(my_lock->name_); - close(my_lock->fd_); - delete my_lock; - return result; + locks_.Remove(posix_file_lock->filename()); + ::close(posix_file_lock->fd()); + delete posix_file_lock; + return Status::OK(); } - virtual void Schedule(void (*function)(void*), void* arg); + void Schedule(void (*background_work_function)(void* background_work_arg), + void* background_work_arg) override; - virtual void StartThread(void (*function)(void* arg), void* arg); + void StartThread(void (*thread_main)(void* thread_main_arg), + void* thread_main_arg) override; - virtual Status GetTestDirectory(std::string* result) { - const char* env = getenv("TEST_TMPDIR"); + Status GetTestDirectory(std::string* result) override { + const char* env = std::getenv("TEST_TMPDIR"); if (env && env[0] != '\0') { *result = env; } else { char buf[100]; - snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + std::snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", + static_cast(::geteuid())); *result = buf; } - // Directory may already exist + + // The CreateDir status is ignored because the directory may already exist. CreateDir(*result); + return Status::OK(); } - virtual Status NewLogger(const std::string& fname, Logger** result) { - FILE* f = fopen(fname.c_str(), "w"); - if (f == nullptr) { + Status NewLogger(const std::string& filename, Logger** result) override { + std::FILE* fp = std::fopen(filename.c_str(), "w"); + if (fp == nullptr) { *result = nullptr; - return PosixError(fname, errno); + return PosixError(filename, errno); } else { - *result = new PosixLogger(f); + *result = new PosixLogger(fp); return Status::OK(); } } - virtual uint64_t NowMicros() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + uint64_t NowMicros() override { + static constexpr uint64_t kUsecondsPerSecond = 1000000; + struct ::timeval tv; + ::gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec; } - virtual void SleepForMicroseconds(int micros) { - usleep(micros); + void SleepForMicroseconds(int micros) override { + ::usleep(micros); } private: @@ -656,44 +702,41 @@ class PosixEnv : public Env { std::queue background_work_queue_ GUARDED_BY(background_work_mutex_); - PosixLockTable locks_; - Limiter mmap_limit_; - Limiter fd_limit_; + PosixLockTable locks_; // Thread-safe. + Limiter mmap_limiter_; // Thread-safe. + Limiter fd_limiter_; // Thread-safe. }; // Return the maximum number of concurrent mmaps. -static int MaxMmaps() { - if (mmap_limit >= 0) { - return mmap_limit; - } - // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. - mmap_limit = sizeof(void*) >= 8 ? 1000 : 0; - return mmap_limit; +int MaxMmaps() { + return g_mmap_limit; } // Return the maximum number of read-only files to keep open. -static intptr_t MaxOpenFiles() { - if (open_read_only_file_limit >= 0) { - return open_read_only_file_limit; +int MaxOpenFiles() { + if (g_open_read_only_file_limit >= 0) { + return g_open_read_only_file_limit; } - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim)) { + struct ::rlimit rlim; + if (::getrlimit(RLIMIT_NOFILE, &rlim)) { // getrlimit failed, fallback to hard-coded default. - open_read_only_file_limit = 50; + g_open_read_only_file_limit = 50; } else if (rlim.rlim_cur == RLIM_INFINITY) { - open_read_only_file_limit = std::numeric_limits::max(); + g_open_read_only_file_limit = std::numeric_limits::max(); } else { // Allow use of 20% of available file descriptors for read-only files. - open_read_only_file_limit = rlim.rlim_cur / 5; + g_open_read_only_file_limit = rlim.rlim_cur / 5; } - return open_read_only_file_limit; + return g_open_read_only_file_limit; } +} // namespace + PosixEnv::PosixEnv() : background_work_cv_(&background_work_mutex_), started_background_thread_(false), - mmap_limit_(MaxMmaps()), - fd_limit_(MaxOpenFiles()) { + mmap_limiter_(MaxMmaps()), + fd_limiter_(MaxOpenFiles()) { } void PosixEnv::Schedule( @@ -737,6 +780,8 @@ void PosixEnv::BackgroundThreadMain() { } } +namespace { + // Wraps an Env instance whose destructor is never created. // // Intended usage: @@ -800,12 +845,12 @@ void PosixEnv::StartThread(void (*thread_main)(void* thread_main_arg), void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { PosixDefaultEnv::AssertEnvNotInitialized(); - open_read_only_file_limit = limit; + g_open_read_only_file_limit = limit; } void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { PosixDefaultEnv::AssertEnvNotInitialized(); - mmap_limit = limit; + g_mmap_limit = limit; } Env* Env::Default() { From 58d70545af9ec7f30821f973b604f8e2a2f9ebdb Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 2 Jan 2019 17:58:33 -0800 Subject: [PATCH 116/174] Update Travis CI configuration. The Travis CI configuration updates reflect the following changes: * Container-based builds (sudo: false) have been removed. https://changelog.travis-ci.com/the-container-based-build-environment-is-fully-deprecated-84517 * Ubuntu Xenial (16.04) is available as a base image. https://blog.travis-ci.com/2018-11-08-xenial-release * Homebrew now has a dedicated DSL. https://docs.travis-ci.com/user/installing-dependencies/#installing-packages-on-os-x To take full advantage of VM resources, CI builds now use Ninja https://ninja-build.org/ instead of Make. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=227611641 --- .travis.yml | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd7b52d6b1..3ff5cfc8d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,7 @@ # http://about.travis-ci.org/docs/user/build-configuration/ # This file can be validated on: http://lint.travis-ci.org/ -sudo: false -dist: trusty +dist: xenial language: cpp compiler: @@ -26,38 +25,39 @@ matrix: addons: apt: - # List of whitelisted in travis packages for ubuntu-trusty can be found here: - # https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-trusty - # List of whitelisted in travis apt-sources: - # https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json sources: + - llvm-toolchain-xenial-7 - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-5.0 packages: + - clang-7 - cmake - - gcc-7 - - g++-7 - - clang-5.0 + - gcc-8 + - g++-8 - libgoogle-perftools-dev - libkyotocabinet-dev - libsnappy-dev - libsqlite3-dev + - ninja-build + homebrew: + packages: + - crc32c + - gperftools + - kyotocabinet + - gcc@7 + - ninja + - snappy + - sqlite3 -install: -# Travis doesn't have a DSL for installing homebrew packages yet. Status tracked -# in https://github.com/travis-ci/travis-ci/issues/5377 +before_install: # The Travis VM image for Mac already has a link at /usr/local/include/c++, -# causing Homebrew's gcc@7 installation to error out. This was reported to +# causing Homebrew's gcc installation to error out. This was reported to # Homebrew maintainers at https://github.com/Homebrew/brew/issues/1742 and # removing the link emerged as a workaround. -- if [ "$TRAVIS_OS_NAME" == "osx" ]; then - brew update; - if [ -L /usr/local/include/c++ ]; then rm /usr/local/include/c++; fi; - brew install gcc@7; - brew install crc32c gperftools kyoto-cabinet snappy sqlite3; - fi +- if [ "$TRAVIS_OS_NAME" == "osx" ]; then rm -f /usr/local/include/c++ ; fi + +install: # /usr/bin/gcc is stuck to old versions on both Linux and OSX. -- if [ "$CXX" = "g++" ]; then export CXX="g++-7" CC="gcc-7"; fi +- if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi - echo ${CC} - echo ${CXX} - ${CXX} --version @@ -65,7 +65,7 @@ install: before_script: - mkdir -p build && cd build -- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE +- cmake .. -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build . - cd .. From af7abf06ea061222c2c34d98e1995c5a901f374f Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 7 Jan 2019 11:29:24 -0800 Subject: [PATCH 117/174] Add back space to POSIX Logger. The space in between the header and log message was mistakenly omitted in a prior commit. Re-adding. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228202737 --- util/posix_logger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/posix_logger.h b/util/posix_logger.h index 28b290e1bb..5685fa3e99 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -68,7 +68,7 @@ class PosixLogger final : public Logger { // Print the header into the buffer. int buffer_offset = snprintf( buffer, buffer_size, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s", + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", now_components.tm_year + 1900, now_components.tm_mon + 1, now_components.tm_mday, @@ -79,7 +79,7 @@ class PosixLogger final : public Logger { thread_id.c_str()); // The header can be at most 28 characters (10 date + 15 time + - // 3 spacing) plus the thread ID, which should fit comfortably into the + // 3 delimiters) plus the thread ID, which should fit comfortably into the // static buffer. assert(buffer_offset <= 28 + kMaxThreadIdSize); static_assert(28 + kMaxThreadIdSize < kStackBufferSize, From b70493ca8586285b49e9888e2b528f71806bdc6e Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 8 Jan 2019 13:49:13 -0800 Subject: [PATCH 118/174] Fix fdatasync() feature detection in opensource build. The CMake feature-detection code used check_symbol_exists(), which invokes the C compiler. However, some glibc versions don't expose the fdatasync() declaration when compiled with -std=c11, but do expose it when compiled with -std=c++11. This most likely comes down to how _POSIX_SOURCE is defined -- it needs to be >= 201112L for to expose fdatasync(). This CL switches to check_cxx_symbol_exists(), which uses the C++ compiler. Asides from fixing the problem above, this is the right thing to do, because we use in env_posix.cc, which is compiled with the C++ compiler. This CL also fixes a previously introduced inconsistency, where the macro indicating the fdatasync() feature detection result was referred to as HAVE_FDATASYNC and HAVE_FUNC_FDATASYNC. The former appears to be used in other libraries, so this CL switches all our references to HAVE_FDATASYNC. Fixes https://github.com/google/leveldb/issues/629 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228392612 --- CMakeLists.txt | 9 +++++++-- port/port_config.h.in | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36d6cbd9ee..57a0c74c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,13 @@ check_library_exists(crc32c crc32c_value "" HAVE_CRC32C) check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) check_library_exists(tcmalloc malloc "" HAVE_TCMALLOC) -include(CheckSymbolExists) -check_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) +include(CheckCXXSymbolExists) +# Using check_cxx_symbol_exists() instead of check_c_symbol_exists() because +# we're including the header from C++, and feature detection should use the same +# compiler language that the project will use later. Principles aside, some +# versions of do not expose fdatasync() in in standard C mode +# (-std=c11), but do expose the function in standard C++ mode (-std=c++11). +check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) include(CheckCXXSourceCompiles) diff --git a/port/port_config.h.in b/port/port_config.h.in index 19340554a2..645c78c061 100644 --- a/port/port_config.h.in +++ b/port/port_config.h.in @@ -6,9 +6,9 @@ #define STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ // Define to 1 if you have a definition for fdatasync() in . -#if !defined(HAVE_FUNC_FDATASYNC) -#cmakedefine01 HAVE_FUNC_FDATASYNC -#endif // !defined(HAVE_FUNC_FDATASYNC) +#if !defined(HAVE_FDATASYNC) +#cmakedefine01 HAVE_FDATASYNC +#endif // !defined(HAVE_FDATASYNC) // Define to 1 if you have Google CRC32C. #if !defined(HAVE_CRC32C) From 296de8d5b8e4e57bd1e46c981114dfbe58a8c4fa Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 9 Jan 2019 14:53:09 -0800 Subject: [PATCH 119/174] leveldb: Fix PosixWritableFile::Sync() on Apple systems. Apple doesn't follow POSIX specifications for fsync(). Instead, fsync() guarantees to flush the buffer cache to the device, which means the data will survive kernel panics, but may not survive power outages. Applications that need stronger guarantees (like databases) need to use fcntl(F_FULLFSYNC). This CL switches PosixWritableFile::Sync() to get the stronger guarantees on Apple systems. The improved implementation follows the same principles as SQLite [1] and node.js [2]. Research for the fcntl() to fsync() fallback strategy: Apple's released source code at https://opensource.apple.com/ shows at least three different error codes being returned when a filesystem does not support F_FULLFSYNC. fcntl() is implemented in xnu-4903.221.2 in bsd/kern/kern_descrip.c, where it delegates to fcntl_nocancel(). The documentation for fcntl_nocancel() mentions error codes for some operations, but does not include F_FULLFSYNC. The F_FULLSYNC branch in fcntl_nocancel() calls VNOP_IOCTL(_, F_FULLSYNC, NULL, 0, _), whose return value sets the error code. VNOP_IOCTL() is implemented in bsd/vfs/kpi_vfs.c and calls the ioctl function in the vnode's operation vector. The per-filesystem function names follow the pattern _vnop_ioctl() for all the instances in opensource code: {hfs,msdosfs,nfs,ntfs,smbfs,webdav,zfs}_vnop_ioctl(). hfs-407.30.1, msdosfs-229.200.3, and nfs in xnu-4903.221.2 handle F_FULLFSYNC. ntfs-94.200.1 and smb-759.40.1 do not handle F_FULLFSYNC, and the default branch returns ENOSUP. webdav-380.200.1 also does not handle F_FULLFSYNC, but the default branch returns EINVAL. zfs-59 also does not handle F_FULLSYNC, and its default branch returns ENOTTY. From a different angle, Apple's ntfs-94.200.1 includes utility code that uses fcntl(F_FULLFSYNC) and falls back to fsync() just like we do, supporting the hypothesis that there is no good way to detect lack of F_FULLFSYNC support. Also, Apple's fcntl() man page [3] does not mention a way to detect lack of F_FULLFSYNC support. [1] https://www.sqlite.org/src/doc/trunk/src/os_unix.c [2] https://github.com/libuv/libuv/blob/master/src/unix/fs.c [3] https://developer.apple.com/library/archive/documentatiVon/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html Tested: https://travis-ci.org/pwnall/leveldb/builds/477318498 TAP global presubmit ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228593729 --- CMakeLists.txt | 1 + port/port_config.h.in | 5 +++++ util/env_posix.cc | 46 ++++++++++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57a0c74c58..f6a7c0a409 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ include(CheckCXXSymbolExists) # versions of do not expose fdatasync() in in standard C mode # (-std=c11), but do expose the function in standard C++ mode (-std=c++11). check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) +check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC) include(CheckCXXSourceCompiles) diff --git a/port/port_config.h.in b/port/port_config.h.in index 645c78c061..d6a6d01519 100644 --- a/port/port_config.h.in +++ b/port/port_config.h.in @@ -10,6 +10,11 @@ #cmakedefine01 HAVE_FDATASYNC #endif // !defined(HAVE_FDATASYNC) +// Define to 1 if you have a definition for F_FULLFSYNC in . +#if !defined(HAVE_FULLFSYNC) +#cmakedefine01 HAVE_FULLFSYNC +#endif // !defined(HAVE_FULLFSYNC) + // Define to 1 if you have Google CRC32C. #if !defined(HAVE_CRC32C) #cmakedefine01 HAVE_CRC32C diff --git a/util/env_posix.cc b/util/env_posix.cc index 76bb648503..362adb3b0b 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -35,12 +35,6 @@ #include "util/posix_logger.h" #include "util/env_posix_test_helper.h" -// HAVE_FDATASYNC is defined in the auto-generated port_config.h, which is -// included by port_stdcxx.h. -#if !HAVE_FDATASYNC -#define fdatasync fsync -#endif // !HAVE_FDATASYNC - namespace leveldb { namespace { @@ -314,10 +308,11 @@ class PosixWritableFile final : public WritableFile { } status = FlushBuffer(); - if (status.ok() && ::fdatasync(fd_) != 0) { - status = PosixError(filename_, errno); + if (!status.ok()) { + return status; } - return status; + + return SyncFd(fd_, filename_); } private: @@ -352,14 +347,41 @@ class PosixWritableFile final : public WritableFile { if (fd < 0) { status = PosixError(dirname_, errno); } else { - if (::fsync(fd) < 0) { - status = PosixError(dirname_, errno); - } + status = SyncFd(fd, dirname_); ::close(fd); } return status; } + // Ensures that all the caches associated with the given file descriptor's + // data are flushed all the way to durable media, and can withstand power + // failures. + // + // The path argument is only used to populate the description string in the + // returned Status if an error occurs. + static Status SyncFd(int fd, const std::string& fd_path) { +#if HAVE_FULLFSYNC + // On macOS and iOS, fsync() doesn't guarantee durability past power + // failures. fcntl(F_FULLFSYNC) is required for that purpose. Some + // filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to + // fsync(). + if (::fcntl(fd, F_FULLFSYNC) == 0) { + return Status::OK(); + } +#endif // HAVE_FULLFSYNC + +#if HAVE_FDATASYNC + bool sync_success = ::fdatasync(fd) == 0; +#else + bool sync_success = ::fsync(fd) == 0; +#endif // HAVE_FDATASYNC + + if (sync_success) { + return Status::OK(); + } + return PosixError(fd_path, errno); + } + // Returns the directory name in a path pointing to a file. // // Returns "." if the path does not contain any directory separator. From fe4494804f5e3a2e25485d32aeb0eb7d2f25732e Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 15 Jan 2019 10:29:55 -0800 Subject: [PATCH 120/174] leveldb: Make WriteBatch::ApproximateSize() const. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=229395810 --- db/write_batch.cc | 2 +- include/leveldb/write_batch.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/write_batch.cc b/db/write_batch.cc index 40eed2eff9..23eb00fd42 100644 --- a/db/write_batch.cc +++ b/db/write_batch.cc @@ -39,7 +39,7 @@ void WriteBatch::Clear() { rep_.resize(kHeader); } -size_t WriteBatch::ApproximateSize() { +size_t WriteBatch::ApproximateSize() const { return rep_.size(); } diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 9386ace2d8..9b319f0587 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -52,7 +52,7 @@ class LEVELDB_EXPORT WriteBatch { // // This number is tied to implementation details, and may change across // releases. It is intended for LevelDB usage metrics. - size_t ApproximateSize(); + size_t ApproximateSize() const; // Copies the operations in "source" to this batch. // From 75fceae7003e217e16b04433831da7528ae56881 Mon Sep 17 00:00:00 2001 From: Adam Azarchs Date: Wed, 19 Sep 2018 16:29:00 -0700 Subject: [PATCH 121/174] Add O_CLOEXEC to open calls. This prevents file descriptors from leaking to child processes. When compiled for older (pre-2.6.23) kernels which lack support for O_CLOEXEC there is no change in behavior. With newer kernels, child processes will no longer inherit leveldb's file handles, which reduces the changes of accidentally corrupting the database. Fixes https://github.com/google/leveldb/issues/623 --- CMakeLists.txt | 1 + port/port_config.h.in | 5 ++ util/env_posix.cc | 27 +++++++---- util/env_posix_test.cc | 102 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a7c0a409..9b0042b8d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ include(CheckCXXSymbolExists) # (-std=c11), but do expose the function in standard C++ mode (-std=c++11). check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC) +check_cxx_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC) include(CheckCXXSourceCompiles) diff --git a/port/port_config.h.in b/port/port_config.h.in index d6a6d01519..21273153a3 100644 --- a/port/port_config.h.in +++ b/port/port_config.h.in @@ -15,6 +15,11 @@ #cmakedefine01 HAVE_FULLFSYNC #endif // !defined(HAVE_FULLFSYNC) +// Define to 1 if you have a definition for O_CLOEXEC in . +#if !defined(HAVE_O_CLOEXEC) +#cmakedefine01 HAVE_O_CLOEXEC +#endif // !defined(HAVE_O_CLOEXEC) + // Define to 1 if you have Google CRC32C. #if !defined(HAVE_CRC32C) #cmakedefine01 HAVE_CRC32C diff --git a/util/env_posix.cc b/util/env_posix.cc index 362adb3b0b..7a0f04d777 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -48,6 +48,13 @@ constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0; // Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit. int g_mmap_limit = kDefaultMmapLimit; +// Common flags defined for all posix open operations +#if defined(HAVE_O_CLOEXEC) +constexpr const int O_FLAGS = O_CLOEXEC; +#else +constexpr const int O_FLAGS = 0; +#endif // defined(HAVE_O_CLOEXEC) + constexpr const size_t kWritableFileBufferSize = 65536; Status PosixError(const std::string& context, int error_number) { @@ -168,7 +175,7 @@ class PosixRandomAccessFile final : public RandomAccessFile { char* scratch) const override { int fd = fd_; if (!has_permanent_fd_) { - fd = ::open(filename_.c_str(), O_RDONLY); + fd = ::open(filename_.c_str(), O_RDONLY | O_FLAGS); if (fd < 0) { return PosixError(filename_, errno); } @@ -343,7 +350,7 @@ class PosixWritableFile final : public WritableFile { return status; } - int fd = ::open(dirname_.c_str(), O_RDONLY); + int fd = ::open(dirname_.c_str(), O_RDONLY | O_FLAGS); if (fd < 0) { status = PosixError(dirname_, errno); } else { @@ -491,7 +498,7 @@ class PosixEnv : public Env { Status NewSequentialFile(const std::string& filename, SequentialFile** result) override { - int fd = ::open(filename.c_str(), O_RDONLY); + int fd = ::open(filename.c_str(), O_RDONLY | O_FLAGS); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -504,7 +511,7 @@ class PosixEnv : public Env { Status NewRandomAccessFile(const std::string& filename, RandomAccessFile** result) override { *result = nullptr; - int fd = ::open(filename.c_str(), O_RDONLY); + int fd = ::open(filename.c_str(), O_RDONLY | O_FLAGS); if (fd < 0) { return PosixError(filename, errno); } @@ -536,7 +543,9 @@ class PosixEnv : public Env { Status NewWritableFile(const std::string& filename, WritableFile** result) override { - int fd = ::open(filename.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); + int fd = ::open(filename.c_str(), + O_TRUNC | O_WRONLY | O_CREAT | O_FLAGS, + 0644); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -548,7 +557,9 @@ class PosixEnv : public Env { Status NewAppendableFile(const std::string& filename, WritableFile** result) override { - int fd = ::open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); + int fd = ::open(filename.c_str(), + O_APPEND | O_WRONLY | O_CREAT | O_FLAGS, + 0644); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -618,7 +629,7 @@ class PosixEnv : public Env { Status LockFile(const std::string& filename, FileLock** lock) override { *lock = nullptr; - int fd = ::open(filename.c_str(), O_RDWR | O_CREAT, 0644); + int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_FLAGS, 0644); if (fd < 0) { return PosixError(filename, errno); } @@ -674,7 +685,7 @@ class PosixEnv : public Env { } Status NewLogger(const std::string& filename, Logger** result) override { - std::FILE* fp = std::fopen(filename.c_str(), "w"); + std::FILE* fp = std::fopen(filename.c_str(), "we"); if (fp == nullptr) { *result = nullptr; return PosixError(filename, errno); diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index e28df9a5ea..5519d8d60d 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include +#include + #include "leveldb/env.h" #include "port/port.h" @@ -31,7 +34,7 @@ TEST(EnvPosixTest, TestOpenOnRead) { ASSERT_OK(env_->GetTestDirectory(&test_dir)); std::string test_file = test_dir + "/open_on_read.txt"; - FILE* f = fopen(test_file.c_str(), "w"); + FILE* f = fopen(test_file.c_str(), "we"); ASSERT_TRUE(f != nullptr); const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; fputs(kFileData, f); @@ -56,9 +59,106 @@ TEST(EnvPosixTest, TestOpenOnRead) { ASSERT_OK(env_->DeleteFile(test_file)); } +#if defined(HAVE_O_CLOEXEC) + +TEST(EnvPosixTest, TestCloseOnExec) { + // Test that file handles are not inherited by child processes. + + // Open file handles with each of the open methods. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::vector test_files = { + test_dir + "/close_on_exec_seq.txt", + test_dir + "/close_on_exec_rand.txt", + test_dir + "/close_on_exec_write.txt", + test_dir + "/close_on_exec_append.txt", + test_dir + "/close_on_exec_lock.txt", + test_dir + "/close_on_exec_log.txt", + }; + for (const std::string& test_file : test_files) { + const char kFileData[] = "0123456789"; + ASSERT_OK(WriteStringToFile(env_, kFileData, test_file)); + } + leveldb::SequentialFile* seqFile = nullptr; + leveldb::RandomAccessFile* randFile = nullptr; + leveldb::WritableFile* writeFile = nullptr; + leveldb::WritableFile* appendFile = nullptr; + leveldb::FileLock* lockFile = nullptr; + leveldb::Logger* logFile = nullptr; + ASSERT_OK(env_->NewSequentialFile(test_files[0], &seqFile)); + ASSERT_OK(env_->NewRandomAccessFile(test_files[1], &randFile)); + ASSERT_OK(env_->NewWritableFile(test_files[2], &writeFile)); + ASSERT_OK(env_->NewAppendableFile(test_files[3], &appendFile)); + ASSERT_OK(env_->LockFile(test_files[4], &lockFile)); + ASSERT_OK(env_->NewLogger(test_files[5], &logFile)); + + // Fork a child process and wait for it to complete. + int pid = fork(); + if (pid == 0) { + const char* const child[] = {"/proc/self/exe", "-cloexec-child", nullptr}; + execv(child[0], const_cast(child)); + printf("Error spawning child process: %s\n", strerror(errno)); + exit(6); + } + int status; + waitpid(pid, &status, 0); + ASSERT_EQ(0, WEXITSTATUS(status)); + + // cleanup + ASSERT_OK(env_->UnlockFile(lockFile)); + delete seqFile; + delete randFile; + delete writeFile; + delete appendFile; + delete logFile; + for (const std::string& test_file : test_files) { + ASSERT_OK(env_->DeleteFile(test_file)); + } +} + +#endif // defined(HAVE_O_CLOEXEC) + +int cloexecChild() { + // Checks for open file descriptors in the range 3..FD_SETSIZE. + for (int i = 3; i < FD_SETSIZE; i++) { + int dup_result = dup2(i, i); + if (dup_result != -1) { + printf("Unexpected open file %d\n", i); + char nbuf[28]; + snprintf(nbuf, 28, "/proc/self/fd/%d", i); + char dbuf[1024]; + int result = readlink(nbuf, dbuf, 1024); + if (0 < result && result < 1024) { + dbuf[result] = 0; + printf("File descriptor %d is %s\n", i, dbuf); + if (strstr(dbuf, "close_on_exec_") == nullptr) { + continue; + } + } else if (result >= 1024) { + printf("(file name length is too long)\n"); + } else { + printf("Couldn't get file name: %s\n", strerror(errno)); + } + return 3; + } else { + int e = errno; + if (e != EBADF) { + printf("Unexpected result reading file handle %d: %s\n", i, + strerror(errno)); + return 4; + } + } + } + return 0; +} + } // namespace leveldb int main(int argc, char** argv) { + // Check if this is the child process for TestCloseOnExec + if (argc > 1 && strcmp(argv[1], "-cloexec-child") == 0) { + return leveldb::cloexecChild(); + } // All tests currently run with the same read-only file limits. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, leveldb::kMMapLimit); From c69d33b0ec3dad2a8063ad66da9d51a1d6309f4e Mon Sep 17 00:00:00 2001 From: cmumford Date: Fri, 1 Mar 2019 13:12:01 -0800 Subject: [PATCH 122/174] Added native support for Windows. This change adds a native Windows port (port_windows.h) and a Windows Env (WindowsEnv). Note1: "small" is defined when including so some parameters were renamed to avoid conflict. Note2: leveldb::Env defines the method: "DeleteFile" which is also a constant defined when including . The solution was to ensure this macro is defined in env.h which forces the function, when compiled, to be either DeleteFileA or DeleteFileW when building for MBCS or UNICODE respectively. This resolves #519 on GitHub. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=236364778 --- .appveyor.yml | 38 ++ CMakeLists.txt | 31 +- README.md | 33 +- db/corruption_test.cc | 26 ++ db/db_test.cc | 11 +- db/recovery_test.cc | 3 + db/version_set.cc | 6 +- include/leveldb/env.h | 30 ++ port/atomic_pointer.h | 4 - port/port.h | 2 +- util/env_windows.cc | 742 +++++++++++++++++++++++++++++++++ util/env_windows_test.cc | 63 +++ util/env_windows_test_helper.h | 30 ++ util/windows_logger.h | 124 ++++++ 14 files changed, 1120 insertions(+), 23 deletions(-) create mode 100644 .appveyor.yml create mode 100644 util/env_windows.cc create mode 100644 util/env_windows_test.cc create mode 100644 util/env_windows_test_helper.h create mode 100644 util/windows_logger.h diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000000..78aeaf1d46 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,38 @@ +# Build matrix / environment variables are explained on: +# https://www.appveyor.com/docs/appveyor-yml/ +# This file can be validated on: https://ci.appveyor.com/tools/validate-yaml + +version: "{build}" + +environment: + matrix: + # AppVeyor currently has no custom job name feature. + # http://help.appveyor.com/discussions/questions/1623-can-i-provide-a-friendly-name-for-jobs + - JOB: Visual Studio 2017 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + CMAKE_GENERATOR: Visual Studio 15 2017 + +platform: + - x86 + - x64 + +configuration: + - RelWithDebInfo + - Debug + +build: + verbosity: minimal + +build_script: + - git submodule update --init --recursive + - mkdir build + - cd build + - if "%platform%"=="x64" set CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64 + - cmake --version + - cmake .. -G "%CMAKE_GENERATOR%" + -DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%" + - cmake --build . --config "%CONFIGURATION%" + - cd .. + +test_script: + - cd build ; ctest --verbose ; cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a7c0a409..1eaf48e4fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,14 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +if (WIN32) + set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS) + # TODO(cmumford): Make UNICODE configurable for Windows. + add_definitions(-D_UNICODE -DUNICODE) +else (WIN32) + set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_POSIX) +endif (WIN32) + option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON) option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON) option(LEVELDB_INSTALL "Install LevelDB's header and library" ON) @@ -179,12 +187,19 @@ target_sources(leveldb "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" ) -# POSIX code is specified separately so we can leave it out in the future. +if (WIN32) +target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/util/env_windows.cc" + "${PROJECT_SOURCE_DIR}/util/windows_logger.h" +) +else (WIN32) target_sources(leveldb PRIVATE "${PROJECT_SOURCE_DIR}/util/env_posix.cc" "${PROJECT_SOURCE_DIR}/util/posix_logger.h" ) +endif (WIN32) # MemEnv is not part of the interface and could be pulled to a separate library. target_sources(leveldb @@ -203,7 +218,7 @@ target_compile_definitions(leveldb # Used by include/export.h when building shared libraries. LEVELDB_COMPILE_LIBRARY # Used by port/port.h. - LEVELDB_PLATFORM_POSIX=1 + ${LEVELDB_PLATFORM_NAME}=1 ) if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions(leveldb @@ -265,7 +280,7 @@ if(LEVELDB_BUILD_TESTS) target_link_libraries("${test_target_name}" leveldb) target_compile_definitions("${test_target_name}" PRIVATE - LEVELDB_PLATFORM_POSIX=1 + ${LEVELDB_PLATFORM_NAME}=1 ) if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions("${test_target_name}" @@ -314,8 +329,12 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("${PROJECT_SOURCE_DIR}/util/logging_test.cc") # TODO(costan): This test also uses - # "${PROJECT_SOURCE_DIR}/util/env_posix_test_helper.h" - leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc") + # "${PROJECT_SOURCE_DIR}/util/env_{posix|windows}_test_helper.h" + if (WIN32) + leveldb_test("${PROJECT_SOURCE_DIR}/util/env_windows_test.cc") + else (WIN32) + leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc") + endif (WIN32) endif(NOT BUILD_SHARED_LIBS) endif(LEVELDB_BUILD_TESTS) @@ -339,7 +358,7 @@ if(LEVELDB_BUILD_BENCHMARKS) target_link_libraries("${bench_target_name}" leveldb) target_compile_definitions("${bench_target_name}" PRIVATE - LEVELDB_PLATFORM_POSIX=1 + ${LEVELDB_PLATFORM_NAME}=1 ) if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions("${bench_target_name}" diff --git a/README.md b/README.md index 15fbdc227c..493bdbdb4f 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) This project supports [CMake](https://cmake.org/) out of the box. +### Build for POSIX + Quick start: ```bash @@ -37,6 +39,29 @@ mkdir -p build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . ``` +### Building for Windows + +First generate the Visual Studio 2017 project/solution files: + +```bash +mkdir -p build +cd build +cmake -G "Visual Studio 15" .. +``` +The default default will build for x86. For 64-bit run: + +```bash +cmake -G "Visual Studio 15 Win64" .. +``` + +To compile the Windows solution from the command-line: + +```bash +devenv /build Debug leveldb.sln +``` + +or open leveldb.sln in Visual Studio and build from within. + Please see the CMake documentation and `CMakeLists.txt` for more advanced usage. # Contributing to the leveldb Project @@ -48,10 +73,10 @@ will be considered. Contribution requirements: -1. **POSIX only**. We _generally_ will only accept changes that are both - compiled, and tested on a POSIX platform - usually Linux. Very small - changes will sometimes be accepted, but consider that more of an - exception than the rule. +1. **Tested platforms only**. We _generally_ will only accept changes for + platforms that are compiled and tested. This means POSIX (for Linux and + macOS) or Windows. Very small changes will sometimes be accepted, but + consider that more of an exception than the rule. 2. **Stable API**. We strive very hard to maintain a stable API. Changes that require changes for projects using leveldb _might_ be rejected without diff --git a/db/corruption_test.cc b/db/corruption_test.cc index 0b93c241eb..98aaf8c730 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -20,6 +20,10 @@ #include "util/testharness.h" #include "util/testutil.h" +#if defined(LEVELDB_PLATFORM_WINDOWS) +#include "util/env_windows_test_helper.h" +#endif // defined(LEVELDB_PLATFORM_WINDOWS) + namespace leveldb { static const int kValueSize = 1000; @@ -32,6 +36,17 @@ class CorruptionTest { Options options_; DB* db_; +#if defined(LEVELDB_PLATFORM_WINDOWS) + static void SetFileLimits(int mmap_limit) { + EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } + + // TODO(cmumford): Modify corruption_test to use MemEnv and remove. + static void RelaxFilePermissions() { + EnvWindowsTestHelper::RelaxFilePermissions(); + } +#endif // defined(LEVELDB_PLATFORM_WINDOWS) + CorruptionTest() { tiny_cache_ = NewLRUCache(100); options_.env = &env_; @@ -370,5 +385,16 @@ TEST(CorruptionTest, UnrelatedKeys) { } // namespace leveldb int main(int argc, char** argv) { +#if defined(LEVELDB_PLATFORM_WINDOWS) + // When Windows maps the contents of a file into memory, even if read/write, + // subsequent attempts to open that file for write access will fail. Forcing + // all RandomAccessFile instances to use base file I/O (e.g. ReadFile) + // allows these tests to open files in order to corrupt their contents. + leveldb::CorruptionTest::SetFileLimits(0); + + // Allow this test to write to (and corrupt) files which are normally + // open for exclusive read access. + leveldb::CorruptionTest::RelaxFilePermissions(); +#endif // defined(LEVELDB_PLATFORM_WINDOWS) return leveldb::test::RunAllTests(); } diff --git a/db/db_test.cc b/db/db_test.cc index 878b7d4e03..894ed23976 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -470,11 +470,12 @@ class DBTest { } // Do n memtable compactions, each of which produces an sstable - // covering the range [small,large]. - void MakeTables(int n, const std::string& small, const std::string& large) { + // covering the range [small_key,large_key]. + void MakeTables(int n, const std::string& small_key, + const std::string& large_key) { for (int i = 0; i < n; i++) { - Put(small, "begin"); - Put(large, "end"); + Put(small_key, "begin"); + Put(large_key, "end"); dbfull()->TEST_CompactMemTable(); } } @@ -1655,7 +1656,7 @@ TEST(DBTest, DestroyEmptyDir) { ASSERT_TRUE(env.FileExists(dbname)); std::vector children; ASSERT_OK(env.GetChildren(dbname, &children)); - // The POSIX env does not filter out '.' and '..' special files. + // The stock Env's do not filter out '.' and '..' special files. ASSERT_EQ(2, children.size()); ASSERT_OK(DestroyDB(dbname, opts)); ASSERT_TRUE(!env.FileExists(dbname)); diff --git a/db/recovery_test.cc b/db/recovery_test.cc index c852803a45..87bd53cab2 100644 --- a/db/recovery_test.cc +++ b/db/recovery_test.cc @@ -97,6 +97,9 @@ class RecoveryTest { } size_t DeleteLogFiles() { + // Linux allows unlinking open files, but Windows does not. + // Closing the db allows for file deletion. + Close(); std::vector logs = GetFiles(kLogFile); for (size_t i = 0; i < logs.size(); i++) { ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]); diff --git a/db/version_set.cc b/db/version_set.cc index c27ccadc0b..ae06089b76 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -143,8 +143,8 @@ bool SomeFileOverlapsRange( uint32_t index = 0; if (smallest_user_key != nullptr) { // Find the earliest possible internal key for smallest_user_key - InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); - index = FindFile(icmp, files, small.Encode()); + InternalKey small_key(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + index = FindFile(icmp, files, small_key.Encode()); } if (index >= files.size()) { @@ -700,7 +700,7 @@ class VersionSet::Builder { // same as the compaction of 40KB of data. We are a little // conservative and allow approximately one seek for every 16KB // of data before triggering a compaction. - f->allowed_seeks = (f->file_size / 16384); + f->allowed_seeks = static_cast((f->file_size / 16384U)); if (f->allowed_seeks < 100) f->allowed_seeks = 100; levels_[level].deleted_files.erase(f->number); diff --git a/include/leveldb/env.h b/include/leveldb/env.h index 59e2a6fef4..946ea982d1 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -20,6 +20,27 @@ #include "leveldb/export.h" #include "leveldb/status.h" +#if defined(_WIN32) +// The leveldb::Env class below contains a DeleteFile method. +// At the same time, , a fairly popular header +// file for Windows applications, defines a DeleteFile macro. +// +// Without any intervention on our part, the result of this +// unfortunate coincidence is that the name of the +// leveldb::Env::DeleteFile method seen by the compiler depends on +// whether was included before or after the LevelDB +// headers. +// +// To avoid headaches, we undefined DeleteFile (if defined) and +// redefine it at the bottom of this file. This way +// can be included before this file (or not at all) and the +// exported method will always be leveldb::Env::DeleteFile. +#if defined(DeleteFile) +#undef DeleteFile +#define LEVELDB_DELETEFILE_UNDEFINED +#endif // defined(DeleteFile) +#endif // defined(_WIN32) + namespace leveldb { class FileLock; @@ -356,4 +377,13 @@ class LEVELDB_EXPORT EnvWrapper : public Env { } // namespace leveldb +// Redefine DeleteFile if necessary. +#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED) +#if defined(UNICODE) +#define DeleteFile DeleteFileW +#else +#define DeleteFile DeleteFileA +#endif // defined(UNICODE) +#endif // defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED) + #endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index bb4e183c08..d906f635c7 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -22,10 +22,6 @@ #include -#ifdef OS_WIN -#include -#endif - #if defined(_M_X64) || defined(__x86_64__) #define ARCH_CPU_X86_FAMILY 1 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) diff --git a/port/port.h b/port/port.h index 0975fed0d1..b2210a7553 100644 --- a/port/port.h +++ b/port/port.h @@ -10,7 +10,7 @@ // Include the appropriate platform specific file below. If you are // porting to a new platform, see "port_example.h" for documentation // of what the new port_.h file must provide. -#if defined(LEVELDB_PLATFORM_POSIX) +#if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_WINDOWS) # include "port/port_stdcxx.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) # include "port/port_chromium.h" diff --git a/util/env_windows.cc b/util/env_windows.cc new file mode 100644 index 0000000000..03da266733 --- /dev/null +++ b/util/env_windows.cc @@ -0,0 +1,742 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Prevent Windows headers from defining min/max macros and instead +// use STL. +#define NOMINMAX +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "port/thread_annotations.h" +#include "util/env_windows_test_helper.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/windows_logger.h" + +#if defined(DeleteFile) +#undef DeleteFile +#endif // defined(DeleteFile) + +namespace leveldb { + +namespace { + +constexpr const size_t kWritableFileBufferSize = 65536; + +// Up to 1000 mmaps for 64-bit binaries; none for 32-bit. +constexpr int kDefaultMmapLimit = sizeof(void*) >= 8 ? 1000 : 0; + +// Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit(). +int g_mmap_limit = kDefaultMmapLimit; + +// Relax some file access permissions for testing. +bool g_relax_permissions = false; + +std::string GetWindowsErrorMessage(DWORD error_code) { + std::string message; + char* error_text = nullptr; + // Use MBCS version of FormatMessage to match return value. + size_t error_text_size = ::FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&error_text), 0, nullptr); + if (!error_text) { + return message; + } + message.assign(error_text, error_text_size); + ::LocalFree(error_text); + return message; +} + +Status WindowsError(const std::string& context, DWORD error_code) { + if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND) + return Status::NotFound(context, GetWindowsErrorMessage(error_code)); + return Status::IOError(context, GetWindowsErrorMessage(error_code)); +} + +class ScopedHandle { + public: + ScopedHandle(HANDLE handle) : handle_(handle) {} + ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {} + ~ScopedHandle() { Close(); } + + ScopedHandle& operator=(ScopedHandle&& rhs) noexcept { + if (this != &rhs) handle_ = rhs.Release(); + return *this; + } + + bool Close() { + if (!is_valid()) { + return true; + } + HANDLE h = handle_; + handle_ = INVALID_HANDLE_VALUE; + return ::CloseHandle(h); + } + + bool is_valid() const { + return handle_ != INVALID_HANDLE_VALUE && handle_ != nullptr; + } + + HANDLE get() const { return handle_; } + + HANDLE Release() { + HANDLE h = handle_; + handle_ = INVALID_HANDLE_VALUE; + return h; + } + + private: + HANDLE handle_; +}; + +// Helper class to limit resource usage to avoid exhaustion. +// Currently used to limit mmap file usage so that we do not end +// up running out virtual memory, or running into kernel performance +// problems for very large databases. +class Limiter { + public: + // Limit maximum number of resources to |n|. + Limiter(intptr_t n) { SetAllowed(n); } + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() LOCKS_EXCLUDED(mu_) { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() LOCKS_EXCLUDED(mu_) { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + void SetAllowed(intptr_t v) EXCLUSIVE_LOCKS_REQUIRED(mu_) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + Limiter(const Limiter&); + void operator=(const Limiter&); +}; + +class WindowsSequentialFile : public SequentialFile { + public: + WindowsSequentialFile(std::string fname, ScopedHandle file) + : filename_(fname), file_(std::move(file)) {} + ~WindowsSequentialFile() override {} + + Status Read(size_t n, Slice* result, char* scratch) override { + Status s; + DWORD bytes_read; + // DWORD is 32-bit, but size_t could technically be larger. However leveldb + // files are limited to leveldb::Options::max_file_size which is clamped to + // 1<<30 or 1 GiB. + assert(n <= std::numeric_limits::max()); + if (!::ReadFile(file_.get(), scratch, static_cast(n), &bytes_read, + nullptr)) { + s = WindowsError(filename_, ::GetLastError()); + } else { + *result = Slice(scratch, bytes_read); + } + return s; + } + + Status Skip(uint64_t n) override { + LARGE_INTEGER distance; + distance.QuadPart = n; + if (!::SetFilePointerEx(file_.get(), distance, nullptr, FILE_CURRENT)) { + return WindowsError(filename_, ::GetLastError()); + } + return Status::OK(); + } + + private: + std::string filename_; + ScopedHandle file_; +}; + +class WindowsRandomAccessFile : public RandomAccessFile { + public: + WindowsRandomAccessFile(std::string fname, ScopedHandle handle) + : filename_(fname), handle_(std::move(handle)) {} + + ~WindowsRandomAccessFile() override = default; + + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + DWORD bytes_read = 0; + OVERLAPPED overlapped = {0}; + + overlapped.OffsetHigh = static_cast(offset >> 32); + overlapped.Offset = static_cast(offset); + if (!::ReadFile(handle_.get(), scratch, static_cast(n), &bytes_read, + &overlapped)) { + DWORD error_code = ::GetLastError(); + if (error_code != ERROR_HANDLE_EOF) { + *result = Slice(scratch, 0); + return Status::IOError(filename_, GetWindowsErrorMessage(error_code)); + } + } + + *result = Slice(scratch, bytes_read); + return Status::OK(); + } + + private: + std::string filename_; + ScopedHandle handle_; +}; + +class WindowsMmapReadableFile : public RandomAccessFile { + public: + // base[0,length-1] contains the mmapped contents of the file. + WindowsMmapReadableFile(std::string fname, void* base, size_t length, + Limiter* limiter) + : filename_(std::move(fname)), + mmapped_region_(base), + length_(length), + limiter_(limiter) {} + + ~WindowsMmapReadableFile() override { + ::UnmapViewOfFile(mmapped_region_); + limiter_->Release(); + } + + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = WindowsError(filename_, ERROR_INVALID_PARAMETER); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } + + private: + std::string filename_; + void* mmapped_region_; + size_t length_; + Limiter* limiter_; +}; + +class WindowsWritableFile : public WritableFile { + public: + WindowsWritableFile(std::string fname, ScopedHandle handle) + : filename_(std::move(fname)), handle_(std::move(handle)), pos_(0) {} + + ~WindowsWritableFile() override = default; + + Status Append(const Slice& data) override { + size_t n = data.size(); + const char* p = data.data(); + + // Fit as much as possible into buffer. + size_t copy = std::min(n, kWritableFileBufferSize - pos_); + memcpy(buf_ + pos_, p, copy); + p += copy; + n -= copy; + pos_ += copy; + if (n == 0) { + return Status::OK(); + } + + // Can't fit in buffer, so need to do at least one write. + Status s = FlushBuffered(); + if (!s.ok()) { + return s; + } + + // Small writes go to buffer, large writes are written directly. + if (n < kWritableFileBufferSize) { + memcpy(buf_, p, n); + pos_ = n; + return Status::OK(); + } + return WriteRaw(p, n); + } + + Status Close() override { + Status result = FlushBuffered(); + if (!handle_.Close() && result.ok()) { + result = WindowsError(filename_, ::GetLastError()); + } + return result; + } + + Status Flush() override { return FlushBuffered(); } + + Status Sync() override { + // On Windows no need to sync parent directory. It's metadata will be + // updated via the creation of the new file, without an explicit sync. + return FlushBuffered(); + } + + private: + Status FlushBuffered() { + Status s = WriteRaw(buf_, pos_); + pos_ = 0; + return s; + } + + Status WriteRaw(const char* p, size_t n) { + DWORD bytes_written; + if (!::WriteFile(handle_.get(), p, static_cast(n), &bytes_written, + nullptr)) { + return Status::IOError(filename_, + GetWindowsErrorMessage(::GetLastError())); + } + return Status::OK(); + } + + // buf_[0, pos_-1] contains data to be written to handle_. + const std::string filename_; + ScopedHandle handle_; + char buf_[kWritableFileBufferSize]; + size_t pos_; +}; + +// Lock or unlock the entire file as specified by |lock|. Returns true +// when successful, false upon failure. Caller should call ::GetLastError() +// to determine cause of failure +bool LockOrUnlock(HANDLE handle, bool lock) { + if (lock) { + return ::LockFile(handle, + /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0, + /*nNumberOfBytesToLockLow=*/MAXDWORD, + /*nNumberOfBytesToLockHigh=*/MAXDWORD); + } else { + return ::UnlockFile(handle, + /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0, + /*nNumberOfBytesToLockLow=*/MAXDWORD, + /*nNumberOfBytesToLockHigh=*/MAXDWORD); + } +} + +class WindowsFileLock : public FileLock { + public: + WindowsFileLock(ScopedHandle handle, std::string name) + : handle_(std::move(handle)), name_(std::move(name)) {} + + ScopedHandle& handle() { return handle_; } + const std::string& name() const { return name_; } + + private: + ScopedHandle handle_; + std::string name_; +}; + +class WindowsEnv : public Env { + public: + WindowsEnv(); + ~WindowsEnv() override { + static char msg[] = "Destroying Env::Default()\n"; + fwrite(msg, 1, sizeof(msg), stderr); + abort(); + } + + Status NewSequentialFile(const std::string& fname, + SequentialFile** result) override { + *result = nullptr; + DWORD desired_access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ; + if (g_relax_permissions) { + desired_access |= GENERIC_WRITE; + share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + } + ScopedHandle handle = + ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (!handle.is_valid()) { + return WindowsError(fname, ::GetLastError()); + } + *result = new WindowsSequentialFile(fname, std::move(handle)); + return Status::OK(); + } + + Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) override { + *result = nullptr; + DWORD desired_access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ; + if (g_relax_permissions) { + // desired_access |= GENERIC_WRITE; + share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + } + DWORD file_flags = FILE_ATTRIBUTE_READONLY; + + ScopedHandle handle = + ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, + OPEN_EXISTING, file_flags, nullptr); + if (!handle.is_valid()) { + return WindowsError(fname, ::GetLastError()); + } + if (!mmap_limiter_.Acquire()) { + *result = new WindowsRandomAccessFile(fname, std::move(handle)); + return Status::OK(); + } + + LARGE_INTEGER file_size; + if (!::GetFileSizeEx(handle.get(), &file_size)) { + return WindowsError(fname, ::GetLastError()); + } + + ScopedHandle mapping = + ::CreateFileMappingA(handle.get(), + /*security attributes=*/nullptr, PAGE_READONLY, + /*dwMaximumSizeHigh=*/0, + /*dwMaximumSizeLow=*/0, nullptr); + if (mapping.is_valid()) { + void* base = MapViewOfFile(mapping.get(), FILE_MAP_READ, 0, 0, 0); + if (base) { + *result = new WindowsMmapReadableFile( + fname, base, static_cast(file_size.QuadPart), + &mmap_limiter_); + return Status::OK(); + } + } + Status s = WindowsError(fname, ::GetLastError()); + + if (!s.ok()) { + mmap_limiter_.Release(); + } + return s; + } + + Status NewWritableFile(const std::string& fname, + WritableFile** result) override { + DWORD desired_access = GENERIC_WRITE; + DWORD share_mode = 0; + if (g_relax_permissions) { + desired_access |= GENERIC_READ; + share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + } + + ScopedHandle handle = + ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (!handle.is_valid()) { + *result = nullptr; + return WindowsError(fname, ::GetLastError()); + } + + *result = new WindowsWritableFile(fname, std::move(handle)); + return Status::OK(); + } + + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override { + ScopedHandle handle = + ::CreateFileA(fname.c_str(), FILE_APPEND_DATA, 0, nullptr, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (!handle.is_valid()) { + *result = nullptr; + return WindowsError(fname, ::GetLastError()); + } + + *result = new WindowsWritableFile(fname, std::move(handle)); + return Status::OK(); + } + + bool FileExists(const std::string& fname) override { + return GetFileAttributesA(fname.c_str()) != INVALID_FILE_ATTRIBUTES; + } + + Status GetChildren(const std::string& dir, + std::vector* result) override { + const std::string find_pattern = dir + "\\*"; + WIN32_FIND_DATAA find_data; + HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data); + if (dir_handle == INVALID_HANDLE_VALUE) { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_FILE_NOT_FOUND) { + return Status::OK(); + } + return WindowsError(dir, last_error); + } + do { + char base_name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name, + ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) { + result->emplace_back(std::string(base_name) + ext); + } + } while (::FindNextFileA(dir_handle, &find_data)); + DWORD last_error = ::GetLastError(); + ::FindClose(dir_handle); + if (last_error != ERROR_NO_MORE_FILES) { + return WindowsError(dir, last_error); + } + return Status::OK(); + } + + Status DeleteFile(const std::string& fname) override { + if (!::DeleteFileA(fname.c_str())) { + return WindowsError(fname, ::GetLastError()); + } + return Status::OK(); + } + + Status CreateDir(const std::string& name) override { + if (!::CreateDirectoryA(name.c_str(), nullptr)) { + return WindowsError(name, ::GetLastError()); + } + return Status::OK(); + } + + Status DeleteDir(const std::string& name) override { + if (!::RemoveDirectoryA(name.c_str())) { + return WindowsError(name, ::GetLastError()); + } + return Status::OK(); + } + + Status GetFileSize(const std::string& fname, uint64_t* size) override { + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (!::GetFileAttributesExA(fname.c_str(), GetFileExInfoStandard, &attrs)) { + return WindowsError(fname, ::GetLastError()); + } + ULARGE_INTEGER file_size; + file_size.HighPart = attrs.nFileSizeHigh; + file_size.LowPart = attrs.nFileSizeLow; + *size = file_size.QuadPart; + return Status::OK(); + } + + Status RenameFile(const std::string& src, + const std::string& target) override { + // Try a simple move first. It will only succeed when |to_path| doesn't + // already exist. + if (::MoveFileA(src.c_str(), target.c_str())) { + return Status::OK(); + } + DWORD move_error = ::GetLastError(); + + // Try the full-blown replace if the move fails, as ReplaceFile will only + // succeed when |to_path| does exist. When writing to a network share, we + // may not be able to change the ACLs. Ignore ACL errors then + // (REPLACEFILE_IGNORE_MERGE_ERRORS). + if (::ReplaceFileA(target.c_str(), src.c_str(), nullptr, + REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr)) { + return Status::OK(); + } + DWORD replace_error = ::GetLastError(); + // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely + // that |to_path| does not exist. In this case, the more relevant error + // comes from the call to MoveFile. + if (replace_error == ERROR_FILE_NOT_FOUND || + replace_error == ERROR_PATH_NOT_FOUND) { + return WindowsError(src, move_error); + } else { + return WindowsError(src, replace_error); + } + } + + Status LockFile(const std::string& fname, FileLock** lock) override { + *lock = nullptr; + Status result; + ScopedHandle handle = ::CreateFileA( + fname.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + nullptr); + if (!handle.is_valid()) { + result = WindowsError(fname, ::GetLastError()); + } else if (!LockOrUnlock(handle.get(), true)) { + result = WindowsError("lock " + fname, ::GetLastError()); + } else { + *lock = new WindowsFileLock(std::move(handle), std::move(fname)); + } + return result; + } + + Status UnlockFile(FileLock* lock) override { + std::unique_ptr my_lock( + reinterpret_cast(lock)); + Status result; + if (!LockOrUnlock(my_lock->handle().get(), false)) { + result = WindowsError("unlock", ::GetLastError()); + } + return result; + } + + void Schedule(void (*function)(void*), void* arg) override; + + void StartThread(void (*function)(void* arg), void* arg) override { + std::thread t(function, arg); + t.detach(); + } + + Status GetTestDirectory(std::string* result) override { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + return Status::OK(); + } + + char tmp_path[MAX_PATH]; + if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) { + return WindowsError("GetTempPath", ::GetLastError()); + } + std::stringstream ss; + ss << tmp_path << "leveldbtest-" << std::this_thread::get_id(); + *result = ss.str(); + + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + Status NewLogger(const std::string& fname, Logger** result) override { + ScopedHandle handle = + ::CreateFileA(fname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (!handle.is_valid()) { + return WindowsError("NewLogger", ::GetLastError()); + } + *result = new WindowsLogger(handle.Release()); + return Status::OK(); + } + + uint64_t NowMicros() override { + // GetSystemTimeAsFileTime typically has a resolution of 10-20 msec. + // TODO(cmumford): Switch to GetSystemTimePreciseAsFileTime which is + // available in Windows 8 and later. + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + // Each tick represents a 100-nanosecond intervals since January 1, 1601 + // (UTC). + uint64_t num_ticks = + (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; + return num_ticks / 10; + } + + void SleepForMicroseconds(int micros) override { + std::this_thread::sleep_for(std::chrono::microseconds(micros)); + } + + private: + // BGThread() is the body of the background thread + void BGThread(); + + std::mutex mu_; + std::condition_variable bgsignal_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { + void* arg; + void (*function)(void*); + }; + typedef std::deque BGQueue; + BGQueue queue_; + + Limiter mmap_limiter_; +}; + +// Return the maximum number of concurrent mmaps. +int MaxMmaps() { + if (g_mmap_limit >= 0) { + return g_mmap_limit; + } + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + g_mmap_limit = sizeof(void*) >= 8 ? 1000 : 0; + return g_mmap_limit; +} + +WindowsEnv::WindowsEnv() + : started_bgthread_(false), mmap_limiter_(MaxMmaps()) {} + +void WindowsEnv::Schedule(void (*function)(void*), void* arg) { + std::lock_guard guard(mu_); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + std::thread t(&WindowsEnv::BGThread, this); + t.detach(); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + bgsignal_.notify_one(); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; +} + +void WindowsEnv::BGThread() { + while (true) { + // Wait until there is an item that is ready to run + std::unique_lock lk(mu_); + bgsignal_.wait(lk, [this] { return !queue_.empty(); }); + + void (*function)(void*) = queue_.front().function; + void* arg = queue_.front().arg; + queue_.pop_front(); + + lk.unlock(); + (*function)(arg); + } +} + +} // namespace + +static std::once_flag once; +static Env* default_env; +static void InitDefaultEnv() { default_env = new WindowsEnv(); } + +void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) { + assert(default_env == nullptr); + g_mmap_limit = limit; +} + +void EnvWindowsTestHelper::RelaxFilePermissions() { + assert(default_env == nullptr); + g_relax_permissions = true; +} + +Env* Env::Default() { + std::call_once(once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb diff --git a/util/env_windows_test.cc b/util/env_windows_test.cc new file mode 100644 index 0000000000..4451b9e4d3 --- /dev/null +++ b/util/env_windows_test.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/env_windows_test_helper.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kMMapLimit = 4; + +class EnvWindowsTest { + public: + Env* env_; + EnvWindowsTest() : env_(Env::Default()) {} + + static void SetFileLimits(int mmap_limit) { + EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } +}; + +TEST(EnvWindowsTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "w"); + ASSERT_TRUE(f != nullptr); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // leveldb::WindowsEnv to switch from mapping the file into memory + // to basic file reading. + const int kNumFiles = kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + leveldb::EnvWindowsTest::SetFileLimits(leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/util/env_windows_test_helper.h b/util/env_windows_test_helper.h new file mode 100644 index 0000000000..5ffbe447d8 --- /dev/null +++ b/util/env_windows_test_helper.h @@ -0,0 +1,30 @@ +// Copyright 2018 (c) The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ + +namespace leveldb { + +class EnvWindowsTest; + +// A helper for the Windows Env to facilitate testing. +class EnvWindowsTestHelper { + private: + friend class CorruptionTest; + friend class EnvWindowsTest; + + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); + + // Relax file permissions for tests. This results in most files being opened + // with read-write permissions. This is helpful for corruption tests that + // need to corrupt the database files for open databases. + static void RelaxFilePermissions(); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ diff --git a/util/windows_logger.h b/util/windows_logger.h new file mode 100644 index 0000000000..b2a2caef88 --- /dev/null +++ b/util/windows_logger.h @@ -0,0 +1,124 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation for the Windows platform. + +#ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "leveldb/env.h" + +namespace leveldb { + +class WindowsLogger final : public Logger { + public: + WindowsLogger(HANDLE handle) : handle_(handle) { + assert(handle != INVALID_HANDLE_VALUE); + } + + ~WindowsLogger() override { ::CloseHandle(handle_); } + + void Logv(const char* format, va_list arguments) override { + // Record the time as close to the Logv() call as possible. + SYSTEMTIME now_components; + ::GetLocalTime(&now_components); + + // Record the thread ID. + constexpr const int kMaxThreadIdSize = 32; + std::ostringstream thread_stream; + thread_stream << std::this_thread::get_id(); + std::string thread_id = thread_stream.str(); + if (thread_id.size() > kMaxThreadIdSize) { + thread_id.resize(kMaxThreadIdSize); + } + + // We first attempt to print into a stack-allocated buffer. If this attempt + // fails, we make a second attempt with a dynamically allocated buffer. + constexpr const int kStackBufferSize = 512; + char stack_buffer[kStackBufferSize]; + static_assert(sizeof(stack_buffer) == static_cast(kStackBufferSize), + "sizeof(char) is expected to be 1 in C++"); + + int dynamic_buffer_size = 0; // Computed in the first iteration. + for (int iteration = 0; iteration < 2; ++iteration) { + const int buffer_size = + (iteration == 0) ? kStackBufferSize : dynamic_buffer_size; + char* const buffer = + (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; + + // Print the header into the buffer. + // TODO(costan): Sync this logger with another logger. + int buffer_offset = snprintf( + buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.wYear, now_components.wMonth, now_components.wDay, + now_components.wHour, now_components.wMinute, now_components.wSecond, + static_cast(now_components.wMilliseconds * 1000), + std::stoull(thread_id)); + + // The header can be at most 28 characters (10 date + 15 time + + // 3 spacing) plus the thread ID, which should fit comfortably into the + // static buffer. + assert(buffer_offset <= 28 + kMaxThreadIdSize); + static_assert(28 + kMaxThreadIdSize < kStackBufferSize, + "stack-allocated buffer may not fit the message header"); + assert(buffer_offset < buffer_size); + + // Print the message into the buffer. + std::va_list arguments_copy; + va_copy(arguments_copy, arguments); + buffer_offset += std::vsnprintf(buffer + buffer_offset, + buffer_size - buffer_offset, format, + arguments_copy); + va_end(arguments_copy); + + // The code below may append a newline at the end of the buffer, which + // requires an extra character. + if (buffer_offset >= buffer_size - 1) { + // The message did not fit into the buffer. + if (iteration == 0) { + // Re-run the loop and use a dynamically-allocated buffer. The buffer + // will be large enough for the log message, an extra newline and a + // null terminator. + dynamic_buffer_size = buffer_offset + 2; + continue; + } + + // The dynamically-allocated buffer was incorrectly sized. This should + // not happen, assuming a correct implementation of (v)snprintf. Fail + // in tests, recover by truncating the log message in production. + assert(false); + buffer_offset = buffer_size - 1; + } + + // Add a newline if necessary. + if (buffer[buffer_offset - 1] != '\n') { + buffer[buffer_offset] = '\n'; + ++buffer_offset; + } + + assert(buffer_offset <= buffer_size); + ::WriteFile(handle_, buffer, buffer_offset, nullptr, nullptr); + + if (iteration != 0) { + delete[] buffer; + } + break; + } + } + + private: + HANDLE handle_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ From 808e59ec6a160244960cda64b393968ffbdae72c Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 4 Mar 2019 18:27:03 -0800 Subject: [PATCH 123/174] Improve CI configuration. This CL fixes the following issues: * The Travis CI had the ctest invocation followed by a ";", so non-zero exit codes (indicating test failures) did not cause the build to fail. * The AppVeyor CI had the ctest invocation followed by a ";", causing an error on Windows, where "&" plays the role of ";" [1]. The Windows CI (AppVeyor) will still be red after this CL, as some of the tests are failing. However, this CL is a step forward, as it gets us from failing to start tests to running tests and recording success/error states. [1] https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490954(v=technet.10)#using-multiple-commands-and-conditional-processing-symbols ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=236765633 --- .appveyor.yml | 5 +---- .travis.yml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 78aeaf1d46..c24b17e805 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,9 +20,6 @@ configuration: - RelWithDebInfo - Debug -build: - verbosity: minimal - build_script: - git submodule update --init --recursive - mkdir build @@ -35,4 +32,4 @@ build_script: - cd .. test_script: - - cd build ; ctest --verbose ; cd .. + - cd build && ctest --verbose --build-config "%CONFIGURATION%" && cd .. diff --git a/.travis.yml b/.travis.yml index 3ff5cfc8d3..0e1ad6a744 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,7 +70,7 @@ before_script: - cd .. script: -- cd build ; ctest --verbose ; cd .. +- cd build && ctest --verbose && cd .. - "if [ -f build/db_bench ] ; then build/db_bench ; fi" - "if [ -f build/db_bench_sqlite3 ] ; then build/db_bench_sqlite3 ; fi" - "if [ -f build/db_bench_tree_db ] ; then build/db_bench_tree_db ; fi" From ed76289b259d42d0a57c147e791e2c235ed28805 Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 7 Mar 2019 08:52:24 -0800 Subject: [PATCH 124/174] Align windows_logger with posix_logger. Fixes GitHub issue #657. This CL also makes the Windows CI green. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237255887 --- CMakeLists.txt | 20 ++++++++++---------- util/env_windows.cc | 14 +++++++------- util/windows_logger.h | 35 ++++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eaf48e4fe..1562e3ed05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,17 +188,17 @@ target_sources(leveldb ) if (WIN32) -target_sources(leveldb - PRIVATE - "${PROJECT_SOURCE_DIR}/util/env_windows.cc" - "${PROJECT_SOURCE_DIR}/util/windows_logger.h" -) + target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/util/env_windows.cc" + "${PROJECT_SOURCE_DIR}/util/windows_logger.h" + ) else (WIN32) -target_sources(leveldb - PRIVATE - "${PROJECT_SOURCE_DIR}/util/env_posix.cc" - "${PROJECT_SOURCE_DIR}/util/posix_logger.h" -) + target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/util/env_posix.cc" + "${PROJECT_SOURCE_DIR}/util/posix_logger.h" + ) endif (WIN32) # MemEnv is not part of the interface and could be pulled to a separate library. diff --git a/util/env_windows.cc b/util/env_windows.cc index 03da266733..57932bbaaf 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -621,15 +621,15 @@ class WindowsEnv : public Env { return Status::OK(); } - Status NewLogger(const std::string& fname, Logger** result) override { - ScopedHandle handle = - ::CreateFileA(fname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (!handle.is_valid()) { + Status NewLogger(const std::string& filename, Logger** result) override { + std::FILE* fp = std::fopen(filename.c_str(), "w"); + if (fp == nullptr) { + *result = nullptr; return WindowsError("NewLogger", ::GetLastError()); + } else { + *result = new WindowsLogger(fp); + return Status::OK(); } - *result = new WindowsLogger(handle.Release()); - return Status::OK(); } uint64_t NowMicros() override { diff --git a/util/windows_logger.h b/util/windows_logger.h index b2a2caef88..96799bce99 100644 --- a/util/windows_logger.h +++ b/util/windows_logger.h @@ -7,10 +7,9 @@ #ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ #define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ -#include - #include #include +#include #include #include #include @@ -21,11 +20,16 @@ namespace leveldb { class WindowsLogger final : public Logger { public: - WindowsLogger(HANDLE handle) : handle_(handle) { - assert(handle != INVALID_HANDLE_VALUE); + // Creates a logger that writes to the given file. + // + // The PosixLogger instance takes ownership of the file handle. + explicit WindowsLogger(std::FILE* fp) : fp_(fp) { + assert(fp != nullptr); } - ~WindowsLogger() override { ::CloseHandle(handle_); } + ~WindowsLogger() override { + std::fclose(fp_); + } void Logv(const char* format, va_list arguments) override { // Record the time as close to the Logv() call as possible. @@ -56,16 +60,20 @@ class WindowsLogger final : public Logger { (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; // Print the header into the buffer. - // TODO(costan): Sync this logger with another logger. int buffer_offset = snprintf( - buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", - now_components.wYear, now_components.wMonth, now_components.wDay, - now_components.wHour, now_components.wMinute, now_components.wSecond, + buffer, buffer_size, + "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.wYear, + now_components.wMonth, + now_components.wDay, + now_components.wHour, + now_components.wMinute, + now_components.wSecond, static_cast(now_components.wMilliseconds * 1000), - std::stoull(thread_id)); + thread_id.c_str()); // The header can be at most 28 characters (10 date + 15 time + - // 3 spacing) plus the thread ID, which should fit comfortably into the + // 3 delimiters) plus the thread ID, which should fit comfortably into the // static buffer. assert(buffer_offset <= 28 + kMaxThreadIdSize); static_assert(28 + kMaxThreadIdSize < kStackBufferSize, @@ -106,7 +114,8 @@ class WindowsLogger final : public Logger { } assert(buffer_offset <= buffer_size); - ::WriteFile(handle_, buffer, buffer_offset, nullptr, nullptr); + std::fwrite(buffer, 1, buffer_offset, fp_); + std::fflush(fp_); if (iteration != 0) { delete[] buffer; @@ -116,7 +125,7 @@ class WindowsLogger final : public Logger { } private: - HANDLE handle_; + std::FILE* const fp_; }; } // namespace leveldb From 04470825ac96cab0d9d16e4ed410349d082fbf82 Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 7 Mar 2019 12:08:35 -0800 Subject: [PATCH 125/174] Add AppVeyor (Windows CI) badge to README. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237295321 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 493bdbdb4f..4f8ce63ab0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ **LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.** [![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb) +[![Build status](https://ci.appveyor.com/api/projects/status/g2j5j4rfkda6eyw5/branch/master?svg=true)](https://ci.appveyor.com/project/pwnall/leveldb) Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) From a20508dc6a18a34e05a6fc476a8d587fa9bb6608 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Mon, 11 Mar 2019 19:36:11 +0200 Subject: [PATCH 126/174] Fix typo (#565) --- db/version_set.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/version_set.cc b/db/version_set.cc index ae06089b76..156a0078da 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -345,7 +345,7 @@ Status Version::Get(const ReadOptions& options, // We can search level-by-level since entries never hop across // levels. Therefore we are guaranteed that if we find data - // in an smaller level, later levels are irrelevant. + // in a smaller level, later levels are irrelevant. std::vector tmp; FileMetaData* tmp2; for (int level = 0; level < config::kNumLevels; level++) { From cf1d1ab255de2a741695aec53d83e4f808f9e819 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 11 Mar 2019 10:41:03 -0700 Subject: [PATCH 127/174] leveldb: Remove unused file port/win/stdint.h. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237832823 --- port/win/stdint.h | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 port/win/stdint.h diff --git a/port/win/stdint.h b/port/win/stdint.h deleted file mode 100644 index 39edd0db13..0000000000 --- a/port/win/stdint.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -// MSVC didn't ship with this file until the 2010 version. - -#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ -#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ - -#if !defined(_MSC_VER) -#error This file should only be included when compiling with MSVC. -#endif - -// Define C99 equivalent types. -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef signed long long int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; - -#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ From dd906262fd364c08a652dfa914f9995f6b7608a9 Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 11 Mar 2019 12:32:50 -0700 Subject: [PATCH 128/174] Make InMemoryEnv more consistent with filesystem based Env's. Env's (like the POSIX Env) which use an actual filesystem behave differently than InMemoryEnv with regards to writing data to a currently open file. InMemoryEnv::NewWritableFile would previously delete that file, if it was open, before creating a new file so any previously open file would be unlinked. This change truncates an open file so that subsequent reads will read that new data. This should have no impact on leveldb as it never has the same file open for both read and write access. This change is only being made for tests (specifically a future change to corruption_test) to allow them to be decoupled from the underlying platform and allow them to use an Env. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237858231 --- helpers/memenv/memenv.cc | 46 +++++++++++++++++++++++------------ helpers/memenv/memenv_test.cc | 23 ++++++++++++++++++ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index d44627b8c2..b78a99890f 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -51,9 +51,22 @@ class FileState { } } - uint64_t Size() const { return size_; } + uint64_t Size() const { + MutexLock lock(&blocks_mutex_); + return size_; + } + + void Truncate() { + MutexLock lock(&blocks_mutex_); + for (char*& block : blocks_) { + delete[] block; + } + blocks_.clear(); + size_ = 0; + } Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + MutexLock lock(&blocks_mutex_); if (offset > size_) { return Status::IOError("Offset greater than file size."); } @@ -100,6 +113,7 @@ class FileState { const char* src = data.data(); size_t src_len = data.size(); + MutexLock lock(&blocks_mutex_); while (src_len > 0) { size_t avail; size_t offset = size_ % kBlockSize; @@ -128,10 +142,7 @@ class FileState { private: // Private since only Unref() should be used to delete it. ~FileState() { - for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); - ++i) { - delete [] *i; - } + Truncate(); } // No copying allowed. @@ -141,11 +152,9 @@ class FileState { port::Mutex refs_mutex_; int refs_ GUARDED_BY(refs_mutex_); - // The following fields are not protected by any mutex. They are only mutable - // while the file is being written, and concurrent access is not allowed - // to writable files. - std::vector blocks_; - uint64_t size_; + mutable port::Mutex blocks_mutex_; + std::vector blocks_ GUARDED_BY(blocks_mutex_); + uint64_t size_ GUARDED_BY(blocks_mutex_); enum { kBlockSize = 8 * 1024 }; }; @@ -269,13 +278,18 @@ class InMemoryEnv : public EnvWrapper { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) { MutexLock lock(&mutex_); - if (file_map_.find(fname) != file_map_.end()) { - DeleteFileInternal(fname); - } + FileSystem::iterator it = file_map_.find(fname); - FileState* file = new FileState(); - file->Ref(); - file_map_[fname] = file; + FileState* file; + if (it == file_map_.end()) { + // File is not currently open. + file = new FileState(); + file->Ref(); + file_map_[fname] = file; + } else { + file = it->second; + file->Truncate(); + } *result = new WritableFileImpl(file); return Status::OK(); diff --git a/helpers/memenv/memenv_test.cc b/helpers/memenv/memenv_test.cc index 5cff77613f..466479563f 100644 --- a/helpers/memenv/memenv_test.cc +++ b/helpers/memenv/memenv_test.cc @@ -191,6 +191,29 @@ TEST(MemEnvTest, LargeWrite) { delete [] scratch; } +TEST(MemEnvTest, OverwriteOpenFile) { + const char kWrite1Data[] = "Write #1 data"; + const size_t kFileDataLen = sizeof(kWrite1Data) - 1; + const std::string kTestFileName = test::TmpDir() + "/leveldb-TestFile.dat"; + + ASSERT_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName)); + + RandomAccessFile* rand_file; + ASSERT_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file)); + + const char kWrite2Data[] = "Write #2 data"; + ASSERT_OK(WriteStringToFile(env_, kWrite2Data, kTestFileName)); + + // Verify that overwriting an open file will result in the new file data + // being read from files opened before the write. + Slice result; + char scratch[kFileDataLen]; + ASSERT_OK(rand_file->Read(0, kFileDataLen, &result, scratch)); + ASSERT_EQ(0, result.compare(kWrite2Data)); + + delete rand_file; +} + TEST(MemEnvTest, DBTest) { Options options; options.create_if_missing = true; From 7d8e41e49b8fddda66a2c5f0a6a47f1a916e8d26 Mon Sep 17 00:00:00 2001 From: costan Date: Mon, 11 Mar 2019 13:04:53 -0700 Subject: [PATCH 129/174] leveldb: Replace AtomicPointer with std::atomic. This CL removes AtomicPointer from leveldb's port interface. Its usage is replaced with std::atomic<> from the C++11 standard library. AtomicPointer was used to wrap flags, numbers, and pointers, so its instances are replaced with std::atomic, std::atomic, std::atomic and std::atomic. This CL does not revise the memory ordering. AtomicPointer's methods are replaced mechanically with their std::atomic equivalents, even when the underlying usage is incorrect. (Example: DBImpl::has_imm_ is written using release stores, even though it is always read using relaxed ordering.) Revising the memory ordering is left for future CLs. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237865146 --- db/db_bench.cc | 20 ----- db/db_impl.cc | 33 ++++---- db/db_impl.h | 7 +- db/db_test.cc | 135 +++++++++++++++++---------------- db/skiplist.h | 77 ++++++++++--------- db/skiplist_test.cc | 21 +++--- port/atomic_pointer.h | 171 ------------------------------------------ port/port_example.h | 29 ------- port/port_stdcxx.h | 1 - util/arena.cc | 5 +- util/arena.h | 17 +++-- util/env_test.cc | 57 +++++++------- util/env_windows.cc | 56 ++++++-------- 13 files changed, 210 insertions(+), 419 deletions(-) delete mode 100644 port/atomic_pointer.h diff --git a/db/db_bench.cc b/db/db_bench.cc index 115cf4506b..f9403f497a 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -34,7 +34,6 @@ // seekrandom -- N random seeks // open -- cost of opening a DB // crc32c -- repeated crc32c of 4K of data -// acquireload -- load N*1000 times // Meta operations: // compact -- Compact the entire DB // stats -- Print DB stats @@ -57,7 +56,6 @@ static const char* FLAGS_benchmarks = "crc32c," "snappycomp," "snappyuncomp," - "acquireload," ; // Number of key/values to place in database @@ -510,8 +508,6 @@ class Benchmark { method = &Benchmark::Compact; } else if (name == Slice("crc32c")) { method = &Benchmark::Crc32c; - } else if (name == Slice("acquireload")) { - method = &Benchmark::AcquireLoad; } else if (name == Slice("snappycomp")) { method = &Benchmark::SnappyCompress; } else if (name == Slice("snappyuncomp")) { @@ -639,22 +635,6 @@ class Benchmark { thread->stats.AddMessage(label); } - void AcquireLoad(ThreadState* thread) { - int dummy; - port::AtomicPointer ap(&dummy); - int count = 0; - void *ptr = nullptr; - thread->stats.AddMessage("(each op is 1000 loads)"); - while (count < 100000) { - for (int i = 0; i < 1000; i++) { - ptr = ap.Acquire_Load(); - } - count++; - thread->stats.FinishedSingleOp(); - } - if (ptr == nullptr) exit(1); // Disable unused variable warning. - } - void SnappyCompress(ThreadState* thread) { RandomGenerator gen; Slice input = gen.Generate(Options().block_size); diff --git a/db/db_impl.cc b/db/db_impl.cc index fefb883a06..34688622c4 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -132,10 +133,11 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) dbname_(dbname), table_cache_(new TableCache(dbname_, options_, TableCacheSize(options_))), db_lock_(nullptr), - shutting_down_(nullptr), + shutting_down_(false), background_work_finished_signal_(&mutex_), mem_(nullptr), imm_(nullptr), + has_imm_(false), logfile_(nullptr), logfile_number_(0), log_(nullptr), @@ -144,14 +146,12 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) background_compaction_scheduled_(false), manual_compaction_(nullptr), versions_(new VersionSet(dbname_, &options_, table_cache_, - &internal_comparator_)) { - has_imm_.Release_Store(nullptr); -} + &internal_comparator_)) {} DBImpl::~DBImpl() { - // Wait for background work to finish + // Wait for background work to finish. mutex_.Lock(); - shutting_down_.Release_Store(this); // Any non-null value is ok + shutting_down_.store(true, std::memory_order_release); while (background_compaction_scheduled_) { background_work_finished_signal_.Wait(); } @@ -547,7 +547,7 @@ void DBImpl::CompactMemTable() { Status s = WriteLevel0Table(imm_, &edit, base); base->Unref(); - if (s.ok() && shutting_down_.Acquire_Load()) { + if (s.ok() && shutting_down_.load(std::memory_order_acquire)) { s = Status::IOError("Deleting DB during memtable compaction"); } @@ -562,7 +562,7 @@ void DBImpl::CompactMemTable() { // Commit to the new state imm_->Unref(); imm_ = nullptr; - has_imm_.Release_Store(nullptr); + has_imm_.store(false, std::memory_order_release); DeleteObsoleteFiles(); } else { RecordBackgroundError(s); @@ -610,7 +610,8 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin, } MutexLock l(&mutex_); - while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { + while (!manual.done && !shutting_down_.load(std::memory_order_acquire) && + bg_error_.ok()) { if (manual_compaction_ == nullptr) { // Idle manual_compaction_ = &manual; MaybeScheduleCompaction(); @@ -652,7 +653,7 @@ void DBImpl::MaybeScheduleCompaction() { mutex_.AssertHeld(); if (background_compaction_scheduled_) { // Already scheduled - } else if (shutting_down_.Acquire_Load()) { + } else if (shutting_down_.load(std::memory_order_acquire)) { // DB is being deleted; no more background compactions } else if (!bg_error_.ok()) { // Already got an error; no more changes @@ -673,7 +674,7 @@ void DBImpl::BGWork(void* db) { void DBImpl::BackgroundCall() { MutexLock l(&mutex_); assert(background_compaction_scheduled_); - if (shutting_down_.Acquire_Load()) { + if (shutting_down_.load(std::memory_order_acquire)) { // No more background work when shutting down. } else if (!bg_error_.ok()) { // No more background work after a background error. @@ -752,7 +753,7 @@ void DBImpl::BackgroundCompaction() { if (status.ok()) { // Done - } else if (shutting_down_.Acquire_Load()) { + } else if (shutting_down_.load(std::memory_order_acquire)) { // Ignore compaction errors found during shutting down } else { Log(options_.info_log, @@ -919,9 +920,9 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { std::string current_user_key; bool has_current_user_key = false; SequenceNumber last_sequence_for_key = kMaxSequenceNumber; - for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + for (; input->Valid() && !shutting_down_.load(std::memory_order_acquire); ) { // Prioritize immutable compaction work - if (has_imm_.NoBarrier_Load() != nullptr) { + if (has_imm_.load(std::memory_order_relaxed)) { const uint64_t imm_start = env_->NowMicros(); mutex_.Lock(); if (imm_ != nullptr) { @@ -1014,7 +1015,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { input->Next(); } - if (status.ok() && shutting_down_.Acquire_Load()) { + if (status.ok() && shutting_down_.load(std::memory_order_acquire)) { status = Status::IOError("Deleting DB during compaction"); } if (status.ok() && compact->builder != nullptr) { @@ -1378,7 +1379,7 @@ Status DBImpl::MakeRoomForWrite(bool force) { logfile_number_ = new_log_number; log_ = new log::Writer(lfile); imm_ = mem_; - has_imm_.Release_Store(imm_); + has_imm_.store(true, std::memory_order_release); mem_ = new MemTable(internal_comparator_); mem_->Ref(); force = false; // Do not force another compaction if have room diff --git a/db/db_impl.h b/db/db_impl.h index 00e800a114..ca00d4209a 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -5,8 +5,11 @@ #ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ #define STORAGE_LEVELDB_DB_DB_IMPL_H_ +#include #include #include +#include + #include "db/dbformat.h" #include "db/log_writer.h" #include "db/snapshot.h" @@ -136,11 +139,11 @@ class DBImpl : public DB { // State below is protected by mutex_ port::Mutex mutex_; - port::AtomicPointer shutting_down_; + std::atomic shutting_down_; port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_); MemTable* mem_; MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted - port::AtomicPointer has_imm_; // So bg thread can detect non-null imm_ + std::atomic has_imm_; // So bg thread can detect non-null imm_ WritableFile* logfile_; uint64_t logfile_number_ GUARDED_BY(mutex_); log::Writer* log_; diff --git a/db/db_test.cc b/db/db_test.cc index 894ed23976..e889a74088 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include +#include + #include "leveldb/db.h" #include "leveldb/filter_policy.h" #include "db/db_impl.h" @@ -61,7 +64,7 @@ class AtomicCounter { void DelayMilliseconds(int millis) { Env::Default()->SleepForMicroseconds(millis * 1000); } -} +} // namespace // Test Env to override default Env behavior for testing. class TestEnv : public EnvWrapper { @@ -93,45 +96,45 @@ class TestEnv : public EnvWrapper { bool ignore_dot_files_; }; -// Special Env used to delay background operations +// Special Env used to delay background operations. class SpecialEnv : public EnvWrapper { public: // sstable/log Sync() calls are blocked while this pointer is non-null. - port::AtomicPointer delay_data_sync_; + std::atomic delay_data_sync_; // sstable/log Sync() calls return an error. - port::AtomicPointer data_sync_error_; + std::atomic data_sync_error_; // Simulate no-space errors while this pointer is non-null. - port::AtomicPointer no_space_; + std::atomic no_space_; // Simulate non-writable file system while this pointer is non-null. - port::AtomicPointer non_writable_; + std::atomic non_writable_; // Force sync of manifest files to fail while this pointer is non-null. - port::AtomicPointer manifest_sync_error_; + std::atomic manifest_sync_error_; // Force write to manifest files to fail while this pointer is non-null. - port::AtomicPointer manifest_write_error_; + std::atomic manifest_write_error_; bool count_random_reads_; AtomicCounter random_read_counter_; - explicit SpecialEnv(Env* base) : EnvWrapper(base) { - delay_data_sync_.Release_Store(nullptr); - data_sync_error_.Release_Store(nullptr); - no_space_.Release_Store(nullptr); - non_writable_.Release_Store(nullptr); - count_random_reads_ = false; - manifest_sync_error_.Release_Store(nullptr); - manifest_write_error_.Release_Store(nullptr); + explicit SpecialEnv(Env* base) : EnvWrapper(base), + delay_data_sync_(false), + data_sync_error_(false), + no_space_(false), + non_writable_(false), + manifest_sync_error_(false), + manifest_write_error_(false), + count_random_reads_(false) { } Status NewWritableFile(const std::string& f, WritableFile** r) { class DataFile : public WritableFile { private: - SpecialEnv* env_; - WritableFile* base_; + SpecialEnv* const env_; + WritableFile* const base_; public: DataFile(SpecialEnv* env, WritableFile* base) @@ -140,7 +143,7 @@ class SpecialEnv : public EnvWrapper { } ~DataFile() { delete base_; } Status Append(const Slice& data) { - if (env_->no_space_.Acquire_Load() != nullptr) { + if (env_->no_space_.load(std::memory_order_acquire)) { // Drop writes on the floor return Status::OK(); } else { @@ -150,10 +153,10 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->data_sync_error_.Acquire_Load() != nullptr) { + if (env_->data_sync_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated data sync error"); } - while (env_->delay_data_sync_.Acquire_Load() != nullptr) { + while (env_->delay_data_sync_.load(std::memory_order_acquire)) { DelayMilliseconds(100); } return base_->Sync(); @@ -167,7 +170,7 @@ class SpecialEnv : public EnvWrapper { ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } ~ManifestFile() { delete base_; } Status Append(const Slice& data) { - if (env_->manifest_write_error_.Acquire_Load() != nullptr) { + if (env_->manifest_write_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated writer error"); } else { return base_->Append(data); @@ -176,7 +179,7 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->manifest_sync_error_.Acquire_Load() != nullptr) { + if (env_->manifest_sync_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated sync error"); } else { return base_->Sync(); @@ -184,7 +187,7 @@ class SpecialEnv : public EnvWrapper { } }; - if (non_writable_.Acquire_Load() != nullptr) { + if (non_writable_.load(std::memory_order_acquire)) { return Status::IOError("simulated write error"); } @@ -424,7 +427,7 @@ class DBTest { ASSERT_TRUE( db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), &property)); - return atoi(property.c_str()); + return std::stoi(property); } int TotalTableFiles() { @@ -587,11 +590,13 @@ TEST(DBTest, GetFromImmutableLayer) { ASSERT_OK(Put("foo", "v1")); ASSERT_EQ("v1", Get("foo")); - env_->delay_data_sync_.Release_Store(env_); // Block sync calls - Put("k1", std::string(100000, 'x')); // Fill memtable - Put("k2", std::string(100000, 'y')); // Trigger compaction + // Block sync calls. + env_->delay_data_sync_.store(true, std::memory_order_release); + Put("k1", std::string(100000, 'x')); // Fill memtable. + Put("k2", std::string(100000, 'y')); // Trigger compaction. ASSERT_EQ("v1", Get("foo")); - env_->delay_data_sync_.Release_Store(nullptr); // Release sync calls + // Release sync calls. + env_->delay_data_sync_.store(false, std::memory_order_release); } while (ChangeOptions()); } @@ -608,7 +613,7 @@ TEST(DBTest, GetMemUsage) { ASSERT_OK(Put("foo", "v1")); std::string val; ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val)); - int mem_usage = atoi(val.c_str()); + int mem_usage = std::stoi(val); ASSERT_GT(mem_usage, 0); ASSERT_LT(mem_usage, 5*1024*1024); } while (ChangeOptions()); @@ -1106,7 +1111,7 @@ TEST(DBTest, RepeatedWritesToSameKey) { for (int i = 0; i < 5 * kMaxFiles; i++) { Put("key", value); ASSERT_LE(TotalTableFiles(), kMaxFiles); - fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + fprintf(stderr, "after %d: %d files\n", i + 1, TotalTableFiles()); } } @@ -1271,7 +1276,7 @@ TEST(DBTest, IteratorPinsRef) { // Write to force compactions Put("foo", "newvalue1"); for (int i = 0; i < 100; i++) { - ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values } Put("foo", "newvalue2"); @@ -1459,21 +1464,21 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) { TEST(DBTest, L0_CompactionBug_Issue44_b) { Reopen(); - Put("",""); + Put("", ""); Reopen(); Delete("e"); - Put("",""); + Put("", ""); Reopen(); Put("c", "cv"); Reopen(); - Put("",""); + Put("", ""); Reopen(); - Put("",""); + Put("", ""); DelayMilliseconds(1000); // Wait for compaction to finish Reopen(); - Put("d","dv"); + Put("d", "dv"); Reopen(); - Put("",""); + Put("", ""); Reopen(); Delete("d"); Delete("b"); @@ -1711,13 +1716,14 @@ TEST(DBTest, NoSpace) { ASSERT_EQ("v1", Get("foo")); Compact("a", "z"); const int num_files = CountFiles(); - env_->no_space_.Release_Store(env_); // Force out-of-space errors + // Force out-of-space errors. + env_->no_space_.store(true, std::memory_order_release); for (int i = 0; i < 10; i++) { for (int level = 0; level < config::kNumLevels-1; level++) { dbfull()->TEST_CompactRange(level, nullptr, nullptr); } } - env_->no_space_.Release_Store(nullptr); + env_->no_space_.store(false, std::memory_order_release); ASSERT_LT(CountFiles(), num_files + 3); } @@ -1727,7 +1733,8 @@ TEST(DBTest, NonWritableFileSystem) { options.env = env_; Reopen(&options); ASSERT_OK(Put("foo", "v1")); - env_->non_writable_.Release_Store(env_); // Force errors for new files + // Force errors for new files. + env_->non_writable_.store(true, std::memory_order_release); std::string big(100000, 'x'); int errors = 0; for (int i = 0; i < 20; i++) { @@ -1738,7 +1745,7 @@ TEST(DBTest, NonWritableFileSystem) { } } ASSERT_GT(errors, 0); - env_->non_writable_.Release_Store(nullptr); + env_->non_writable_.store(false, std::memory_order_release); } TEST(DBTest, WriteSyncError) { @@ -1748,7 +1755,7 @@ TEST(DBTest, WriteSyncError) { Options options = CurrentOptions(); options.env = env_; Reopen(&options); - env_->data_sync_error_.Release_Store(env_); + env_->data_sync_error_.store(true, std::memory_order_release); // (b) Normal write should succeed WriteOptions w; @@ -1762,7 +1769,7 @@ TEST(DBTest, WriteSyncError) { ASSERT_EQ("NOT_FOUND", Get("k2")); // (d) make sync behave normally - env_->data_sync_error_.Release_Store(nullptr); + env_->data_sync_error_.store(false, std::memory_order_release); // (e) Do a non-sync write; should fail w.sync = false; @@ -1782,7 +1789,7 @@ TEST(DBTest, ManifestWriteError) { // We iterate twice. In the second iteration, everything is the // same except the log record never makes it to the MANIFEST file. for (int iter = 0; iter < 2; iter++) { - port::AtomicPointer* error_type = (iter == 0) + std::atomic* error_type = (iter == 0) ? &env_->manifest_sync_error_ : &env_->manifest_write_error_; @@ -1802,12 +1809,12 @@ TEST(DBTest, ManifestWriteError) { ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level // Merging compaction (will fail) - error_type->Release_Store(env_); + error_type->store(true, std::memory_order_release); dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail ASSERT_EQ("bar", Get("foo")); // Recovery: should not lose data - error_type->Release_Store(nullptr); + error_type->store(false, std::memory_order_release); Reopen(&options); ASSERT_EQ("bar", Get("foo")); } @@ -1878,7 +1885,7 @@ TEST(DBTest, BloomFilter) { dbfull()->TEST_CompactMemTable(); // Prevent auto compactions triggered by seeks - env_->delay_data_sync_.Release_Store(env_); + env_->delay_data_sync_.store(true, std::memory_order_release); // Lookup present keys. Should rarely read from small sstable. env_->random_read_counter_.Reset(); @@ -1899,7 +1906,7 @@ TEST(DBTest, BloomFilter) { fprintf(stderr, "%d missing => %d reads\n", N, reads); ASSERT_LE(reads, 3*N/100); - env_->delay_data_sync_.Release_Store(nullptr); + env_->delay_data_sync_.store(false, std::memory_order_release); Close(); delete options.block_cache; delete options.filter_policy; @@ -1914,9 +1921,9 @@ static const int kNumKeys = 1000; struct MTState { DBTest* test; - port::AtomicPointer stop; - port::AtomicPointer counter[kNumThreads]; - port::AtomicPointer thread_done[kNumThreads]; + std::atomic stop; + std::atomic counter[kNumThreads]; + std::atomic thread_done[kNumThreads]; }; struct MTThread { @@ -1928,13 +1935,13 @@ static void MTThreadBody(void* arg) { MTThread* t = reinterpret_cast(arg); int id = t->id; DB* db = t->state->test->db_; - uintptr_t counter = 0; + int counter = 0; fprintf(stderr, "... starting thread %d\n", id); Random rnd(1000 + id); std::string value; char valbuf[1500]; - while (t->state->stop.Acquire_Load() == nullptr) { - t->state->counter[id].Release_Store(reinterpret_cast(counter)); + while (!t->state->stop.load(std::memory_order_acquire)) { + t->state->counter[id].store(counter, std::memory_order_release); int key = rnd.Uniform(kNumKeys); char keybuf[20]; @@ -1959,14 +1966,13 @@ static void MTThreadBody(void* arg) { ASSERT_EQ(k, key); ASSERT_GE(w, 0); ASSERT_LT(w, kNumThreads); - ASSERT_LE(static_cast(c), reinterpret_cast( - t->state->counter[w].Acquire_Load())); + ASSERT_LE(c, t->state->counter[w].load(std::memory_order_acquire)); } } counter++; } - t->state->thread_done[id].Release_Store(t); - fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); + t->state->thread_done[id].store(true, std::memory_order_release); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, counter); } } // namespace @@ -1976,10 +1982,10 @@ TEST(DBTest, MultiThreaded) { // Initialize state MTState mt; mt.test = this; - mt.stop.Release_Store(0); + mt.stop.store(false, std::memory_order_release); for (int id = 0; id < kNumThreads; id++) { - mt.counter[id].Release_Store(0); - mt.thread_done[id].Release_Store(0); + mt.counter[id].store(false, std::memory_order_release); + mt.thread_done[id].store(false, std::memory_order_release); } // Start threads @@ -1994,9 +2000,9 @@ TEST(DBTest, MultiThreaded) { DelayMilliseconds(kTestSeconds * 1000); // Stop the threads and wait for them to finish - mt.stop.Release_Store(&mt); + mt.stop.store(true, std::memory_order_release); for (int id = 0; id < kNumThreads; id++) { - while (mt.thread_done[id].Acquire_Load() == nullptr) { + while (!mt.thread_done[id].load(std::memory_order_acquire)) { DelayMilliseconds(100); } } @@ -2100,6 +2106,7 @@ class ModelDB: public DB { virtual Slice key() const { return iter_->first; } virtual Slice value() const { return iter_->second; } virtual Status status() const { return Status::OK(); } + private: const KVMap* const map_; const bool owned_; // Do we own map_ diff --git a/db/skiplist.h b/db/skiplist.h index b806ce0fb4..7ac914bc76 100644 --- a/db/skiplist.h +++ b/db/skiplist.h @@ -27,9 +27,10 @@ // // ... prev vs. next pointer ordering ... -#include -#include -#include "port/port.h" +#include +#include +#include + #include "util/arena.h" #include "util/random.h" @@ -105,11 +106,10 @@ class SkipList { // Modified only by Insert(). Read racily by readers, but stale // values are ok. - port::AtomicPointer max_height_; // Height of the entire list + std::atomic max_height_; // Height of the entire list inline int GetMaxHeight() const { - return static_cast( - reinterpret_cast(max_height_.NoBarrier_Load())); + return max_height_.load(std::memory_order_relaxed); } // Read/written only by Insert(). @@ -144,7 +144,7 @@ class SkipList { // Implementation details follow template -struct SkipList::Node { +struct SkipList::Node { explicit Node(const Key& k) : key(k) { } Key const key; @@ -155,63 +155,63 @@ struct SkipList::Node { assert(n >= 0); // Use an 'acquire load' so that we observe a fully initialized // version of the returned Node. - return reinterpret_cast(next_[n].Acquire_Load()); + return next_[n].load(std::memory_order_acquire); } void SetNext(int n, Node* x) { assert(n >= 0); // Use a 'release store' so that anybody who reads through this // pointer observes a fully initialized version of the inserted node. - next_[n].Release_Store(x); + next_[n].store(x, std::memory_order_release); } // No-barrier variants that can be safely used in a few locations. Node* NoBarrier_Next(int n) { assert(n >= 0); - return reinterpret_cast(next_[n].NoBarrier_Load()); + return next_[n].load(std::memory_order_relaxed); } void NoBarrier_SetNext(int n, Node* x) { assert(n >= 0); - next_[n].NoBarrier_Store(x); + next_[n].store(x, std::memory_order_relaxed); } private: // Array of length equal to the node height. next_[0] is lowest level link. - port::AtomicPointer next_[1]; + std::atomic next_[1]; }; template -typename SkipList::Node* -SkipList::NewNode(const Key& key, int height) { - char* mem = arena_->AllocateAligned( - sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); - return new (mem) Node(key); +typename SkipList::Node* +SkipList::NewNode(const Key& key, int height) { + char* const node_memory = arena_->AllocateAligned( + sizeof(Node) + sizeof(std::atomic) * (height - 1)); + return new (node_memory) Node(key); } template -inline SkipList::Iterator::Iterator(const SkipList* list) { +inline SkipList::Iterator::Iterator(const SkipList* list) { list_ = list; node_ = nullptr; } template -inline bool SkipList::Iterator::Valid() const { +inline bool SkipList::Iterator::Valid() const { return node_ != nullptr; } template -inline const Key& SkipList::Iterator::key() const { +inline const Key& SkipList::Iterator::key() const { assert(Valid()); return node_->key; } template -inline void SkipList::Iterator::Next() { +inline void SkipList::Iterator::Next() { assert(Valid()); node_ = node_->Next(0); } template -inline void SkipList::Iterator::Prev() { +inline void SkipList::Iterator::Prev() { // Instead of using explicit "prev" links, we just search for the // last node that falls before key. assert(Valid()); @@ -222,17 +222,17 @@ inline void SkipList::Iterator::Prev() { } template -inline void SkipList::Iterator::Seek(const Key& target) { +inline void SkipList::Iterator::Seek(const Key& target) { node_ = list_->FindGreaterOrEqual(target, nullptr); } template -inline void SkipList::Iterator::SeekToFirst() { +inline void SkipList::Iterator::SeekToFirst() { node_ = list_->head_->Next(0); } template -inline void SkipList::Iterator::SeekToLast() { +inline void SkipList::Iterator::SeekToLast() { node_ = list_->FindLast(); if (node_ == list_->head_) { node_ = nullptr; @@ -240,7 +240,7 @@ inline void SkipList::Iterator::SeekToLast() { } template -int SkipList::RandomHeight() { +int SkipList::RandomHeight() { // Increase height with probability 1 in kBranching static const unsigned int kBranching = 4; int height = 1; @@ -253,14 +253,15 @@ int SkipList::RandomHeight() { } template -bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { // null n is considered infinite return (n != nullptr) && (compare_(n->key, key) < 0); } template -typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) - const { +typename SkipList::Node* +SkipList::FindGreaterOrEqual(const Key& key, + Node** prev) const { Node* x = head_; int level = GetMaxHeight() - 1; while (true) { @@ -281,8 +282,8 @@ typename SkipList::Node* SkipList::FindGreaterOr } template -typename SkipList::Node* -SkipList::FindLessThan(const Key& key) const { +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { Node* x = head_; int level = GetMaxHeight() - 1; while (true) { @@ -302,7 +303,7 @@ SkipList::FindLessThan(const Key& key) const { } template -typename SkipList::Node* SkipList::FindLast() +typename SkipList::Node* SkipList::FindLast() const { Node* x = head_; int level = GetMaxHeight() - 1; @@ -322,11 +323,11 @@ typename SkipList::Node* SkipList::FindLast() } template -SkipList::SkipList(Comparator cmp, Arena* arena) +SkipList::SkipList(Comparator cmp, Arena* arena) : compare_(cmp), arena_(arena), head_(NewNode(0 /* any key will do */, kMaxHeight)), - max_height_(reinterpret_cast(1)), + max_height_(1), rnd_(0xdeadbeef) { for (int i = 0; i < kMaxHeight; i++) { head_->SetNext(i, nullptr); @@ -334,7 +335,7 @@ SkipList::SkipList(Comparator cmp, Arena* arena) } template -void SkipList::Insert(const Key& key) { +void SkipList::Insert(const Key& key) { // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() // here since Insert() is externally synchronized. Node* prev[kMaxHeight]; @@ -348,8 +349,6 @@ void SkipList::Insert(const Key& key) { for (int i = GetMaxHeight(); i < height; i++) { prev[i] = head_; } - //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); - // It is ok to mutate max_height_ without any synchronization // with concurrent readers. A concurrent reader that observes // the new value of max_height_ will see either the old value of @@ -357,7 +356,7 @@ void SkipList::Insert(const Key& key) { // the loop below. In the former case the reader will // immediately drop to the next level since nullptr sorts after all // keys. In the latter case the reader will use the new node. - max_height_.NoBarrier_Store(reinterpret_cast(height)); + max_height_.store(height, std::memory_order_relaxed); } x = NewNode(key, height); @@ -370,7 +369,7 @@ void SkipList::Insert(const Key& key) { } template -bool SkipList::Contains(const Key& key) const { +bool SkipList::Contains(const Key& key) const { Node* x = FindGreaterOrEqual(key, nullptr); if (x != nullptr && Equal(key, x->key)) { return true; diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index 24e0887ad4..c4cf1462f1 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -3,7 +3,10 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "db/skiplist.h" + +#include #include + #include "leveldb/env.h" #include "port/port.h" #include "port/thread_annotations.h" @@ -188,12 +191,12 @@ class ConcurrentTest { // Per-key generation struct State { - port::AtomicPointer generation[K]; - void Set(int k, intptr_t v) { - generation[k].Release_Store(reinterpret_cast(v)); + std::atomic generation[K]; + void Set(int k, int v) { + generation[k].store(v, std::memory_order_release); } - intptr_t Get(int k) { - return reinterpret_cast(generation[k].Acquire_Load()); + int Get(int k) { + return generation[k].load(std::memory_order_acquire); } State() { @@ -300,7 +303,7 @@ class TestState { public: ConcurrentTest t_; int seed_; - port::AtomicPointer quit_flag_; + std::atomic quit_flag_; enum ReaderState { STARTING, @@ -310,7 +313,7 @@ class TestState { explicit TestState(int s) : seed_(s), - quit_flag_(nullptr), + quit_flag_(false), state_(STARTING), state_cv_(&mu_) {} @@ -340,7 +343,7 @@ static void ConcurrentReader(void* arg) { Random rnd(state->seed_); int64_t reads = 0; state->Change(TestState::RUNNING); - while (!state->quit_flag_.Acquire_Load()) { + while (!state->quit_flag_.load(std::memory_order_acquire)) { state->t_.ReadStep(&rnd); ++reads; } @@ -362,7 +365,7 @@ static void RunConcurrent(int run) { for (int i = 0; i < kSize; i++) { state.t_.WriteStep(&rnd); } - state.quit_flag_.Release_Store(&state); // Any non-null arg will do + state.quit_flag_.store(true, std::memory_order_release); state.Wait(TestState::DONE); } } diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h deleted file mode 100644 index d906f635c7..0000000000 --- a/port/atomic_pointer.h +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -// AtomicPointer provides storage for a lock-free pointer. -// Platform-dependent implementation of AtomicPointer: -// - If the platform provides a cheap barrier, we use it with raw pointers -// - If is present (on newer versions of gcc, it is), we use -// a -based AtomicPointer. However we prefer the memory -// barrier based version, because at least on a gcc 4.4 32-bit build -// on linux, we have encountered a buggy implementation. -// Also, some implementations are much slower than a memory-barrier -// based implementation (~16ns for based acquire-load vs. ~1ns for -// a barrier based acquire-load). -// This code is based on atomicops-internals-* in Google's perftools: -// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase - -#ifndef PORT_ATOMIC_POINTER_H_ -#define PORT_ATOMIC_POINTER_H_ - -#include - -#include - -#if defined(_M_X64) || defined(__x86_64__) -#define ARCH_CPU_X86_FAMILY 1 -#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) -#define ARCH_CPU_X86_FAMILY 1 -#elif defined(__ARMEL__) -#define ARCH_CPU_ARM_FAMILY 1 -#elif defined(__aarch64__) -#define ARCH_CPU_ARM64_FAMILY 1 -#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) -#define ARCH_CPU_PPC_FAMILY 1 -#elif defined(__mips__) -#define ARCH_CPU_MIPS_FAMILY 1 -#endif - -namespace leveldb { -namespace port { - -// Define MemoryBarrier() if available -// Windows on x86 -#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) -// windows.h already provides a MemoryBarrier(void) macro -// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Mac OS -#elif defined(__APPLE__) -inline void MemoryBarrier() { - std::atomic_thread_fence(std::memory_order_seq_cst); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Gcc on x86 -#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - __asm__ __volatile__("" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Sun Studio -#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - asm volatile("" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// ARM Linux -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) -typedef void (*LinuxKernelMemoryBarrierFunc)(void); -// The Linux ARM kernel provides a highly optimized device-specific memory -// barrier function at a fixed memory address that is mapped in every -// user-level process. -// -// This beats using CPU-specific instructions which are, on single-core -// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more -// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking -// shows that the extra function call cost is completely negligible on -// multi-core devices. -// -inline void MemoryBarrier() { - (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// ARM64 -#elif defined(ARCH_CPU_ARM64_FAMILY) -inline void MemoryBarrier() { - asm volatile("dmb sy" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// PPC -#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // TODO for some powerpc expert: is there a cheaper suitable variant? - // Perhaps by having separate barriers for acquire and release ops. - asm volatile("sync" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// MIPS -#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - __asm__ __volatile__("sync" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -#endif - -// AtomicPointer built using platform-specific MemoryBarrier(). -#if defined(LEVELDB_HAVE_MEMORY_BARRIER) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* p) : rep_(p) {} - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } - inline void* Acquire_Load() const { - void* result = rep_; - MemoryBarrier(); - return result; - } - inline void Release_Store(void* v) { - MemoryBarrier(); - rep_ = v; - } -}; - -// AtomicPointer based on C++11 . -#else -class AtomicPointer { - private: - std::atomic rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - return rep_.load(std::memory_order_acquire); - } - inline void Release_Store(void* v) { - rep_.store(v, std::memory_order_release); - } - inline void* NoBarrier_Load() const { - return rep_.load(std::memory_order_relaxed); - } - inline void NoBarrier_Store(void* v) { - rep_.store(v, std::memory_order_relaxed); - } -}; - -#endif - -#undef LEVELDB_HAVE_MEMORY_BARRIER -#undef ARCH_CPU_X86_FAMILY -#undef ARCH_CPU_ARM_FAMILY -#undef ARCH_CPU_ARM64_FAMILY -#undef ARCH_CPU_PPC_FAMILY - -} // namespace port -} // namespace leveldb - -#endif // PORT_ATOMIC_POINTER_H_ diff --git a/port/port_example.h b/port/port_example.h index 9c648c374b..1a8fca24b3 100644 --- a/port/port_example.h +++ b/port/port_example.h @@ -62,35 +62,6 @@ class CondVar { void SignallAll(); }; -// A type that holds a pointer that can be read or written atomically -// (i.e., without word-tearing.) -class AtomicPointer { - private: - intptr_t rep_; - public: - // Initialize to arbitrary value - AtomicPointer(); - - // Initialize to hold v - explicit AtomicPointer(void* v) : rep_(v) { } - - // Read and return the stored pointer with the guarantee that no - // later memory access (read or write) by this thread can be - // reordered ahead of this read. - void* Acquire_Load() const; - - // Set v as the stored pointer with the guarantee that no earlier - // memory access (read or write) by this thread can be reordered - // after this store. - void Release_Store(void* v); - - // Read the stored pointer with no ordering guarantees. - void* NoBarrier_Load() const; - - // Set va as the stored pointer with no ordering guarantees. - void NoBarrier_Store(void* v); -}; - // ------------------ Compression ------------------- // Store the snappy compression of "input[0,input_length-1]" in *output. diff --git a/port/port_stdcxx.h b/port/port_stdcxx.h index 4713e26c41..e21fa70a2d 100644 --- a/port/port_stdcxx.h +++ b/port/port_stdcxx.h @@ -35,7 +35,6 @@ #include // NOLINT #include // NOLINT #include -#include "port/atomic_pointer.h" #include "port/thread_annotations.h" namespace leveldb { diff --git a/util/arena.cc b/util/arena.cc index a0338bf192..a496ad030b 100644 --- a/util/arena.cc +++ b/util/arena.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "util/arena.h" -#include namespace leveldb { @@ -60,8 +59,8 @@ char* Arena::AllocateAligned(size_t bytes) { char* Arena::AllocateNewBlock(size_t block_bytes) { char* result = new char[block_bytes]; blocks_.push_back(result); - memory_usage_.NoBarrier_Store( - reinterpret_cast(MemoryUsage() + block_bytes + sizeof(char*))); + memory_usage_.fetch_add(block_bytes + sizeof(char*), + std::memory_order_relaxed); return result; } diff --git a/util/arena.h b/util/arena.h index 48bab33741..624e262b4a 100644 --- a/util/arena.h +++ b/util/arena.h @@ -5,11 +5,11 @@ #ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ #define STORAGE_LEVELDB_UTIL_ARENA_H_ +#include +#include +#include +#include #include -#include -#include -#include -#include "port/port.h" namespace leveldb { @@ -21,13 +21,13 @@ class Arena { // Return a pointer to a newly allocated memory block of "bytes" bytes. char* Allocate(size_t bytes); - // Allocate memory with the normal alignment guarantees provided by malloc + // Allocate memory with the normal alignment guarantees provided by malloc. char* AllocateAligned(size_t bytes); // Returns an estimate of the total memory usage of data allocated // by the arena. size_t MemoryUsage() const { - return reinterpret_cast(memory_usage_.NoBarrier_Load()); + return memory_usage_.load(std::memory_order_relaxed); } private: @@ -42,7 +42,10 @@ class Arena { std::vector blocks_; // Total memory usage of the arena. - port::AtomicPointer memory_usage_; + // + // TODO(costan): This member is accessed via atomics, but the others are + // accessed without any locking. Is this OK? + std::atomic memory_usage_; // No copying allowed Arena(const Arena&); diff --git a/util/env_test.cc b/util/env_test.cc index 070109b61c..b204089f81 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -5,6 +5,7 @@ #include "leveldb/env.h" #include +#include #include "port/port.h" #include "port/thread_annotations.h" @@ -24,10 +25,15 @@ class EnvTest { EnvTest() : env_(Env::Default()) { } }; -static void SetBool(void* ptr) { - reinterpret_cast(ptr)->NoBarrier_Store(ptr); +namespace { + +static void SetAtomicBool(void* atomic_bool_ptr) { + std::atomic* atomic_bool = + reinterpret_cast*>(atomic_bool_ptr); + atomic_bool->store(true, std::memory_order_relaxed); } +} // namespace TEST(EnvTest, ReadWrite) { Random rnd(test::RandomSeed()); @@ -77,42 +83,41 @@ TEST(EnvTest, ReadWrite) { } TEST(EnvTest, RunImmediately) { - port::AtomicPointer called(nullptr); - env_->Schedule(&SetBool, &called); + std::atomic called(false); + env_->Schedule(&SetAtomicBool, &called); env_->SleepForMicroseconds(kDelayMicros); - ASSERT_TRUE(called.NoBarrier_Load() != nullptr); + ASSERT_TRUE(called.load(std::memory_order_relaxed)); } TEST(EnvTest, RunMany) { - port::AtomicPointer last_id(nullptr); + std::atomic last_id(0); - struct CB { - port::AtomicPointer* last_id_ptr; // Pointer to shared slot - uintptr_t id; // Order# for the execution of this callback + struct Callback { + std::atomic* const last_id_ptr_; // Pointer to shared state. + const int id_; // Order# for the execution of this callback. - CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + Callback(std::atomic* last_id_ptr, int id) + : last_id_ptr_(last_id_ptr), id_(id) { } - static void Run(void* v) { - CB* cb = reinterpret_cast(v); - void* cur = cb->last_id_ptr->NoBarrier_Load(); - ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); - cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + static void Run(void* arg) { + Callback* callback = reinterpret_cast(arg); + int current_id = callback->last_id_ptr_->load(std::memory_order_relaxed); + ASSERT_EQ(callback->id_ - 1, current_id); + callback->last_id_ptr_->store(callback->id_, std::memory_order_relaxed); } }; - // Schedule in different order than start time - CB cb1(&last_id, 1); - CB cb2(&last_id, 2); - CB cb3(&last_id, 3); - CB cb4(&last_id, 4); - env_->Schedule(&CB::Run, &cb1); - env_->Schedule(&CB::Run, &cb2); - env_->Schedule(&CB::Run, &cb3); - env_->Schedule(&CB::Run, &cb4); + Callback callback1(&last_id, 1); + Callback callback2(&last_id, 2); + Callback callback3(&last_id, 3); + Callback callback4(&last_id, 4); + env_->Schedule(&Callback::Run, &callback1); + env_->Schedule(&Callback::Run, &callback2); + env_->Schedule(&Callback::Run, &callback3); + env_->Schedule(&Callback::Run, &callback4); env_->SleepForMicroseconds(kDelayMicros); - void* cur = last_id.Acquire_Load(); - ASSERT_EQ(4, reinterpret_cast(cur)); + ASSERT_EQ(4, last_id.load(std::memory_order_relaxed)); } struct State { diff --git a/util/env_windows.cc b/util/env_windows.cc index 57932bbaaf..3b4496b1ca 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -105,51 +106,42 @@ class ScopedHandle { }; // Helper class to limit resource usage to avoid exhaustion. -// Currently used to limit mmap file usage so that we do not end -// up running out virtual memory, or running into kernel performance -// problems for very large databases. +// Currently used to limit read-only file descriptors and mmap file usage +// so that we do not run out of file descriptors or virtual memory, or run into +// kernel performance problems for very large databases. class Limiter { public: - // Limit maximum number of resources to |n|. - Limiter(intptr_t n) { SetAllowed(n); } + // Limit maximum number of resources to |max_acquires|. + Limiter(int max_acquires) : acquires_allowed_(max_acquires) {} + + Limiter(const Limiter&) = delete; + Limiter operator=(const Limiter&) = delete; // If another resource is available, acquire it and return true. // Else return false. - bool Acquire() LOCKS_EXCLUDED(mu_) { - if (GetAllowed() <= 0) { - return false; - } - MutexLock l(&mu_); - intptr_t x = GetAllowed(); - if (x <= 0) { - return false; - } else { - SetAllowed(x - 1); + bool Acquire() { + int old_acquires_allowed = + acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); + + if (old_acquires_allowed > 0) return true; - } + + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); + return false; } // Release a resource acquired by a previous call to Acquire() that returned // true. - void Release() LOCKS_EXCLUDED(mu_) { - MutexLock l(&mu_); - SetAllowed(GetAllowed() + 1); + void Release() { + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } private: - port::Mutex mu_; - port::AtomicPointer allowed_; - - intptr_t GetAllowed() const { - return reinterpret_cast(allowed_.Acquire_Load()); - } - - void SetAllowed(intptr_t v) EXCLUSIVE_LOCKS_REQUIRED(mu_) { - allowed_.Release_Store(reinterpret_cast(v)); - } - - Limiter(const Limiter&); - void operator=(const Limiter&); + // The number of available resources. + // + // This is a counter and is not tied to the invariants of any other class, so + // it can be operated on safely using std::memory_order_relaxed. + std::atomic acquires_allowed_; }; class WindowsSequentialFile : public SequentialFile { From 9ce30510d482f5b2fa2965201453f0fc914f700c Mon Sep 17 00:00:00 2001 From: cmumford Date: Mon, 11 Mar 2019 13:33:10 -0700 Subject: [PATCH 130/174] Deleted dangling reference to deleted atomic_pointer.h. Forgot one reference to atomic_pointer.h in CMakeLists.txt from prior CL. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237870915 --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1562e3ed05..934f15affd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,7 +127,6 @@ target_sources(leveldb "${PROJECT_SOURCE_DIR}/db/version_set.h" "${PROJECT_SOURCE_DIR}/db/write_batch_internal.h" "${PROJECT_SOURCE_DIR}/db/write_batch.cc" - "${PROJECT_SOURCE_DIR}/port/atomic_pointer.h" "${PROJECT_SOURCE_DIR}/port/port_stdcxx.h" "${PROJECT_SOURCE_DIR}/port/port.h" "${PROJECT_SOURCE_DIR}/port/thread_annotations.h" From 201f77d137f30ea46e789a2ad60e9119b6f990fc Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 19 Mar 2019 14:34:51 -0700 Subject: [PATCH 131/174] Inline defaults in options. This CL moves default values for leveldb::{Options,ReadOptions,WriteOptions} from constructors to member declarations, and removes now-redundant comments stating the defaults. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=239271242 --- db/fault_injection_test.cc | 1 - include/leveldb/options.h | 68 +++++++++++--------------------------- util/options.cc | 15 +-------- 3 files changed, 21 insertions(+), 63 deletions(-) diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index b3429ac922..1f72984b70 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -469,7 +469,6 @@ class FaultInjectionTest { void DeleteAllData() { Iterator* iter = db_->NewIterator(ReadOptions()); - WriteOptions options; for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { ASSERT_OK(db_->Delete(WriteOptions(), iter->key())); } diff --git a/include/leveldb/options.h b/include/leveldb/options.h index b6ddbd85e3..90aa19e149 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -42,20 +42,17 @@ struct LEVELDB_EXPORT Options { const Comparator* comparator; // If true, the database will be created if it is missing. - // Default: false - bool create_if_missing; + bool create_if_missing = false; // If true, an error is raised if the database already exists. - // Default: false - bool error_if_exists; + bool error_if_exists = false; // If true, the implementation will do aggressive checking of the // data it is processing and will stop early if it detects any // errors. This may have unforeseen ramifications: for example, a // corruption of one DB entry may cause a large number of entries to // become unreadable or for the entire DB to become unopenable. - // Default: false - bool paranoid_checks; + bool paranoid_checks = false; // Use the specified object to interact with the environment, // e.g. to read/write files, schedule background work, etc. @@ -65,8 +62,7 @@ struct LEVELDB_EXPORT Options { // Any internal progress/error information generated by the db will // be written to info_log if it is non-null, or to a file stored // in the same directory as the DB contents if info_log is null. - // Default: nullptr - Logger* info_log; + Logger* info_log = nullptr; // ------------------- // Parameters that affect performance @@ -79,39 +75,30 @@ struct LEVELDB_EXPORT Options { // so you may wish to adjust this parameter to control memory usage. // Also, a larger write buffer will result in a longer recovery time // the next time the database is opened. - // - // Default: 4MB - size_t write_buffer_size; + size_t write_buffer_size = 4 * 1024 * 1024; // Number of open files that can be used by the DB. You may need to // increase this if your database has a large working set (budget // one open file per 2MB of working set). - // - // Default: 1000 - int max_open_files; + int max_open_files = 1000; // Control over blocks (user data is stored in a set of blocks, and // a block is the unit of reading from disk). // If non-null, use the specified cache for blocks. // If null, leveldb will automatically create and use an 8MB internal cache. - // Default: nullptr - Cache* block_cache; + Cache* block_cache = nullptr; // Approximate size of user data packed per block. Note that the // block size specified here corresponds to uncompressed data. The // actual size of the unit read from disk may be smaller if // compression is enabled. This parameter can be changed dynamically. - // - // Default: 4K - size_t block_size; + size_t block_size = 4 * 1024; // Number of keys between restart points for delta encoding of keys. // This parameter can be changed dynamically. Most clients should // leave this parameter alone. - // - // Default: 16 - int block_restart_interval; + int block_restart_interval = 16; // Leveldb will write up to this amount of bytes to a file before // switching to a new one. @@ -121,9 +108,7 @@ struct LEVELDB_EXPORT Options { // compactions and hence longer latency/performance hiccups. // Another reason to increase this parameter might be when you are // initially populating a large database. - // - // Default: 2MB - size_t max_file_size; + size_t max_file_size = 2 * 1024 * 1024; // Compress blocks using the specified compression algorithm. This // parameter can be changed dynamically. @@ -139,20 +124,18 @@ struct LEVELDB_EXPORT Options { // worth switching to kNoCompression. Even if the input data is // incompressible, the kSnappyCompression implementation will // efficiently detect that and will switch to uncompressed mode. - CompressionType compression; + CompressionType compression = kSnappyCompression; // EXPERIMENTAL: If true, append to existing MANIFEST and log files // when a database is opened. This can significantly speed up open. // // Default: currently false, but may become true later. - bool reuse_logs; + bool reuse_logs = false; // If non-null, use the specified filter policy to reduce disk reads. // Many applications will benefit from passing the result of // NewBloomFilterPolicy() here. - // - // Default: nullptr - const FilterPolicy* filter_policy; + const FilterPolicy* filter_policy = nullptr; // Create an Options object with default values for all fields. Options(); @@ -162,26 +145,19 @@ struct LEVELDB_EXPORT Options { struct LEVELDB_EXPORT ReadOptions { // If true, all data read from underlying storage will be // verified against corresponding checksums. - // Default: false - bool verify_checksums; + bool verify_checksums = false; // Should the data read for this iteration be cached in memory? // Callers may wish to set this field to false for bulk scans. - // Default: true - bool fill_cache; + bool fill_cache = true; // If "snapshot" is non-null, read as of the supplied snapshot // (which must belong to the DB that is being read and which must // not have been released). If "snapshot" is null, use an implicit // snapshot of the state at the beginning of this read operation. - // Default: nullptr - const Snapshot* snapshot; - - ReadOptions() - : verify_checksums(false), - fill_cache(true), - snapshot(nullptr) { - } + const Snapshot* snapshot = nullptr; + + ReadOptions() = default; }; // Options that control write operations @@ -200,13 +176,9 @@ struct LEVELDB_EXPORT WriteOptions { // crash semantics as the "write()" system call. A DB write // with sync==true has similar crash semantics to a "write()" // system call followed by "fsync()". - // - // Default: false - bool sync; + bool sync = false; - WriteOptions() - : sync(false) { - } + WriteOptions() = default; }; } // namespace leveldb diff --git a/util/options.cc b/util/options.cc index 351fa394e2..63284f8cf3 100644 --- a/util/options.cc +++ b/util/options.cc @@ -11,20 +11,7 @@ namespace leveldb { Options::Options() : comparator(BytewiseComparator()), - create_if_missing(false), - error_if_exists(false), - paranoid_checks(false), - env(Env::Default()), - info_log(nullptr), - write_buffer_size(4<<20), - max_open_files(1000), - block_cache(nullptr), - block_size(4096), - block_restart_interval(16), - max_file_size(2<<20), - compression(kSnappyCompression), - reuse_logs(false), - filter_policy(nullptr) { + env(Env::Default()) { } } // namespace leveldb From ce399ac28af7023b1aff0ede4986cb6d89b3c0b5 Mon Sep 17 00:00:00 2001 From: cmumford Date: Tue, 19 Mar 2019 17:10:56 -0700 Subject: [PATCH 132/174] Always copy bytes to scratch buffer when reading w/MemEnv. FileState::Read (used by InMemoryEnv) creates a new Slice when reading. If all the bytes for the read are in the first block then the Slice points to the private block data in FileState and is not copied to the |scratch| buffer. A recent change allows files in InMemEnv to be overwritten which deletes these blocks and in this case can result in a Slice having a dangling pointer. This change fixes this bug by always copying to the |scratch| buffer. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=239301930 --- helpers/memenv/memenv.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index b78a99890f..ff384e4f05 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -82,13 +82,6 @@ class FileState { assert(offset / kBlockSize <= std::numeric_limits::max()); size_t block = static_cast(offset / kBlockSize); size_t block_offset = offset % kBlockSize; - - if (n <= kBlockSize - block_offset) { - // The requested bytes are all in the first block. - *result = Slice(blocks_[block] + block_offset, n); - return Status::OK(); - } - size_t bytes_to_copy = n; char* dst = scratch; From ea49b27d062c4bc998616cef7944f7f9088a327d Mon Sep 17 00:00:00 2001 From: cmumford Date: Tue, 19 Mar 2019 17:30:42 -0700 Subject: [PATCH 133/174] Switch corruption_test to use InMemEnv. This change switches corruption_test, which previously used direct file I/O to corrupt table files for open databases, to use InMemEnv. Using an Env eliminates some platform dependencies thus simplifying the tests. Also removed EnvWindowsTestHelper::RelaxFilePermissions(). This was only added because the Windows Env opens files for exclusive access. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=239305329 --- db/corruption_test.cc | 58 +++++++--------------------------- util/env_windows.cc | 20 ------------ util/env_windows_test_helper.h | 5 --- util/testutil.h | 6 +++- 4 files changed, 17 insertions(+), 72 deletions(-) diff --git a/db/corruption_test.cc b/db/corruption_test.cc index 98aaf8c730..d50785a9f1 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -4,12 +4,8 @@ #include "leveldb/db.h" -#include -#include -#include #include #include "leveldb/cache.h" -#include "leveldb/env.h" #include "leveldb/table.h" #include "leveldb/write_batch.h" #include "db/db_impl.h" @@ -20,10 +16,6 @@ #include "util/testharness.h" #include "util/testutil.h" -#if defined(LEVELDB_PLATFORM_WINDOWS) -#include "util/env_windows_test_helper.h" -#endif // defined(LEVELDB_PLATFORM_WINDOWS) - namespace leveldb { static const int kValueSize = 1000; @@ -36,22 +28,11 @@ class CorruptionTest { Options options_; DB* db_; -#if defined(LEVELDB_PLATFORM_WINDOWS) - static void SetFileLimits(int mmap_limit) { - EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit); - } - - // TODO(cmumford): Modify corruption_test to use MemEnv and remove. - static void RelaxFilePermissions() { - EnvWindowsTestHelper::RelaxFilePermissions(); - } -#endif // defined(LEVELDB_PLATFORM_WINDOWS) - CorruptionTest() { tiny_cache_ = NewLRUCache(100); options_.env = &env_; options_.block_cache = tiny_cache_; - dbname_ = test::TmpDir() + "/corruption_test"; + dbname_ = "/memenv/corruption_test"; DestroyDB(dbname_, options_); db_ = nullptr; @@ -62,7 +43,6 @@ class CorruptionTest { ~CorruptionTest() { delete db_; - DestroyDB(dbname_, Options()); delete tiny_cache_; } @@ -141,7 +121,7 @@ class CorruptionTest { void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { // Pick file to corrupt std::vector filenames; - ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames)); uint64_t number; FileType type; std::string fname; @@ -156,35 +136,32 @@ class CorruptionTest { } ASSERT_TRUE(!fname.empty()) << filetype; - struct stat sbuf; - if (stat(fname.c_str(), &sbuf) != 0) { - const char* msg = strerror(errno); - ASSERT_TRUE(false) << fname << ": " << msg; - } + uint64_t file_size; + ASSERT_OK(env_.target()->GetFileSize(fname, &file_size)); if (offset < 0) { // Relative to end of file; make it absolute - if (-offset > sbuf.st_size) { + if (-offset > file_size) { offset = 0; } else { - offset = sbuf.st_size + offset; + offset = file_size + offset; } } - if (offset > sbuf.st_size) { - offset = sbuf.st_size; + if (offset > file_size) { + offset = file_size; } - if (offset + bytes_to_corrupt > sbuf.st_size) { - bytes_to_corrupt = sbuf.st_size - offset; + if (offset + bytes_to_corrupt > file_size) { + bytes_to_corrupt = file_size - offset; } // Do it std::string contents; - Status s = ReadFileToString(Env::Default(), fname, &contents); + Status s = ReadFileToString(env_.target(), fname, &contents); ASSERT_TRUE(s.ok()) << s.ToString(); for (int i = 0; i < bytes_to_corrupt; i++) { contents[i + offset] ^= 0x80; } - s = WriteStringToFile(Env::Default(), contents, fname); + s = WriteStringToFile(env_.target(), contents, fname); ASSERT_TRUE(s.ok()) << s.ToString(); } @@ -385,16 +362,5 @@ TEST(CorruptionTest, UnrelatedKeys) { } // namespace leveldb int main(int argc, char** argv) { -#if defined(LEVELDB_PLATFORM_WINDOWS) - // When Windows maps the contents of a file into memory, even if read/write, - // subsequent attempts to open that file for write access will fail. Forcing - // all RandomAccessFile instances to use base file I/O (e.g. ReadFile) - // allows these tests to open files in order to corrupt their contents. - leveldb::CorruptionTest::SetFileLimits(0); - - // Allow this test to write to (and corrupt) files which are normally - // open for exclusive read access. - leveldb::CorruptionTest::RelaxFilePermissions(); -#endif // defined(LEVELDB_PLATFORM_WINDOWS) return leveldb::test::RunAllTests(); } diff --git a/util/env_windows.cc b/util/env_windows.cc index 3b4496b1ca..93555a8cad 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -43,9 +43,6 @@ constexpr int kDefaultMmapLimit = sizeof(void*) >= 8 ? 1000 : 0; // Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit(). int g_mmap_limit = kDefaultMmapLimit; -// Relax some file access permissions for testing. -bool g_relax_permissions = false; - std::string GetWindowsErrorMessage(DWORD error_code) { std::string message; char* error_text = nullptr; @@ -366,10 +363,6 @@ class WindowsEnv : public Env { *result = nullptr; DWORD desired_access = GENERIC_READ; DWORD share_mode = FILE_SHARE_READ; - if (g_relax_permissions) { - desired_access |= GENERIC_WRITE; - share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - } ScopedHandle handle = ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -385,10 +378,6 @@ class WindowsEnv : public Env { *result = nullptr; DWORD desired_access = GENERIC_READ; DWORD share_mode = FILE_SHARE_READ; - if (g_relax_permissions) { - // desired_access |= GENERIC_WRITE; - share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - } DWORD file_flags = FILE_ATTRIBUTE_READONLY; ScopedHandle handle = @@ -433,10 +422,6 @@ class WindowsEnv : public Env { WritableFile** result) override { DWORD desired_access = GENERIC_WRITE; DWORD share_mode = 0; - if (g_relax_permissions) { - desired_access |= GENERIC_READ; - share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - } ScopedHandle handle = ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, @@ -721,11 +706,6 @@ void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) { g_mmap_limit = limit; } -void EnvWindowsTestHelper::RelaxFilePermissions() { - assert(default_env == nullptr); - g_relax_permissions = true; -} - Env* Env::Default() { std::call_once(once, InitDefaultEnv); return default_env; diff --git a/util/env_windows_test_helper.h b/util/env_windows_test_helper.h index 5ffbe447d8..e6f6020561 100644 --- a/util/env_windows_test_helper.h +++ b/util/env_windows_test_helper.h @@ -18,11 +18,6 @@ class EnvWindowsTestHelper { // Set the maximum number of read-only files that will be mapped via mmap. // Must be called before creating an Env. static void SetReadOnlyMMapLimit(int limit); - - // Relax file permissions for tests. This results in most files being opened - // with read-write permissions. This is helpful for corruption tests that - // need to corrupt the database files for open databases. - static void RelaxFilePermissions(); }; } // namespace leveldb diff --git a/util/testutil.h b/util/testutil.h index dc77ac3767..3934242918 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ #define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#include "helpers/memenv/memenv.h" #include "leveldb/env.h" #include "leveldb/slice.h" #include "util/random.h" @@ -32,9 +33,12 @@ class ErrorEnv : public EnvWrapper { bool writable_file_error_; int num_writable_file_errors_; - ErrorEnv() : EnvWrapper(Env::Default()), + ErrorEnv() : EnvWrapper(NewMemEnv(Env::Default())), writable_file_error_(false), num_writable_file_errors_(0) { } + ~ErrorEnv() override { + delete target(); + } virtual Status NewWritableFile(const std::string& fname, WritableFile** result) { From 15e227896621d01ebad4c5d4b3cc82a7a9b5b30b Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 20 Mar 2019 12:41:49 -0700 Subject: [PATCH 134/174] Use override consistently in leveldb::test::ErrorEnv. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=239453565 --- util/testutil.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/testutil.h b/util/testutil.h index 3934242918..a568824e8e 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -40,8 +40,8 @@ class ErrorEnv : public EnvWrapper { delete target(); } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { + Status NewWritableFile(const std::string& fname, + WritableFile** result) override { if (writable_file_error_) { ++num_writable_file_errors_; *result = nullptr; @@ -50,8 +50,8 @@ class ErrorEnv : public EnvWrapper { return target()->NewWritableFile(fname, result); } - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result) { + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override { if (writable_file_error_) { ++num_writable_file_errors_; *result = nullptr; From 6571279d6de21fe33caa31b2ea4170d34b15b10e Mon Sep 17 00:00:00 2001 From: usurai <9886192+usurai@users.noreply.github.com> Date: Thu, 21 Mar 2019 22:58:29 +0800 Subject: [PATCH 135/174] fix a typo in the comment of skiplist_test.cc (#664) --- db/skiplist_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index c4cf1462f1..38c1941dc9 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -131,7 +131,7 @@ TEST(SkipTest, InsertAndLookup) { // concurrent readers (with no synchronization other than when a // reader's iterator is created), the reader always observes all the // data that was present in the skip list when the iterator was -// constructor. Because insertions are happening concurrently, we may +// constructed. Because insertions are happening concurrently, we may // also observe new values that were inserted since the iterator was // constructed, but we should never miss any values that were present // at iterator construction time. From 7035af5fc36657447054617759854a726d31dbe0 Mon Sep 17 00:00:00 2001 From: Felipe Oliveira Carvalho Date: Thu, 21 Mar 2019 16:45:04 +0100 Subject: [PATCH 136/174] Two small fixes for the Windows implementation (#661) * Check if NOMIMMAX is defined before defining it * Pass char* for a %s format in a snprintf call --- util/env_windows.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/env_windows.cc b/util/env_windows.cc index 93555a8cad..14e41e9b4f 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -4,7 +4,9 @@ // Prevent Windows headers from defining min/max macros and instead // use STL. +#ifndef NOMINMAX #define NOMINMAX +#endif // ifndef NOMINMAX #include #include From cf1b5f473259e46c667f3fb5a28bcd884ee3a102 Mon Sep 17 00:00:00 2001 From: Cheng Chang Date: Fri, 22 Mar 2019 17:32:20 +0800 Subject: [PATCH 137/174] Remove unnecessary bit operation. --- util/coding.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/coding.cc b/util/coding.cc index 9e72613440..1a9e3337bd 100644 --- a/util/coding.cc +++ b/util/coding.cc @@ -82,7 +82,7 @@ char* EncodeVarint64(char* dst, uint64_t v) { static const int B = 128; unsigned char* ptr = reinterpret_cast(dst); while (v >= B) { - *(ptr++) = (v & (B-1)) | B; + *(ptr++) = v | B; v >>= 7; } *(ptr++) = static_cast(v); From 6188a54ce95b47cc6bd398d7f2eb45d061857e45 Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 21 Mar 2019 16:15:30 -0700 Subject: [PATCH 138/174] leveldb: Add tests for empty keys and values. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=239695281 --- db/db_test.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/db/db_test.cc b/db/db_test.cc index e889a74088..3ab4aee851 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -558,6 +558,26 @@ TEST(DBTest, Empty) { } while (ChangeOptions()); } +TEST(DBTest, EmptyKey) { + do { + ASSERT_OK(Put("", "v1")); + ASSERT_EQ("v1", Get("")); + ASSERT_OK(Put("", "v2")); + ASSERT_EQ("v2", Get("")); + } while (ChangeOptions()); +} + +TEST(DBTest, EmptyValue) { + do { + ASSERT_OK(Put("key", "v1")); + ASSERT_EQ("v1", Get("key")); + ASSERT_OK(Put("key", "")); + ASSERT_EQ("", Get("key")); + ASSERT_OK(Put("key", "v2")); + ASSERT_EQ("v2", Get("key")); + } while (ChangeOptions()); +} + TEST(DBTest, ReadWrite) { do { ASSERT_OK(Put("foo", "v1")); From bd24b963060861518c6648925f9708178562c992 Mon Sep 17 00:00:00 2001 From: costan Date: Tue, 26 Mar 2019 08:56:12 -0700 Subject: [PATCH 139/174] leveldb: Silence unused argument warnings in MSVC. This CL uses a well-known workaround for silencing arguments that may be unused, depending on the build configuration. The silenced warnings were responsible for a large amount of noise in the MSVC build on Windows. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=240357359 --- port/port_stdcxx.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/port/port_stdcxx.h b/port/port_stdcxx.h index e21fa70a2d..d0609a8fc3 100644 --- a/port/port_stdcxx.h +++ b/port/port_stdcxx.h @@ -91,6 +91,9 @@ inline bool Snappy_Compress(const char* input, size_t length, snappy::RawCompress(input, length, &(*output)[0], &outlen); output->resize(outlen); return true; +#else + // Silence compiler warnings about unused arguments. + (void)input; (void)length; (void)output; #endif // HAVE_SNAPPY return false; @@ -101,6 +104,8 @@ inline bool Snappy_GetUncompressedLength(const char* input, size_t length, #if HAVE_SNAPPY return snappy::GetUncompressedLength(input, length, result); #else + // Silence compiler warnings about unused arguments. + (void)input; (void)length; (void)result; return false; #endif // HAVE_SNAPPY } @@ -109,11 +114,15 @@ inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { #if HAVE_SNAPPY return snappy::RawUncompress(input, length, output); #else + // Silence compiler warnings about unused arguments. + (void)input; (void)length; (void)output; return false; #endif // HAVE_SNAPPY } inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + // Silence compiler warnings about unused arguments. + (void)func; (void)arg; return false; } @@ -121,6 +130,8 @@ inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { #if HAVE_CRC32C return ::crc32c::Extend(crc, reinterpret_cast(buf), size); #else + // Silence compiler warnings about unused arguments. + (void)crc; (void)buf; (void)size; return 0; #endif // HAVE_CRC32C } From da94ac67e91679842a56a876f0b19b429d72de25 Mon Sep 17 00:00:00 2001 From: costan Date: Wed, 27 Mar 2019 12:41:45 -0700 Subject: [PATCH 140/174] leveldb: Minor cleanup in ports. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=240619768 --- port/port_stdcxx.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/port/port_stdcxx.h b/port/port_stdcxx.h index d0609a8fc3..7638dedaef 100644 --- a/port/port_stdcxx.h +++ b/port/port_stdcxx.h @@ -29,12 +29,13 @@ #include #endif // HAVE_SNAPPY -#include -#include #include +#include +#include #include // NOLINT #include // NOLINT #include + #include "port/thread_annotations.h" namespace leveldb { @@ -84,7 +85,7 @@ class CondVar { }; inline bool Snappy_Compress(const char* input, size_t length, - ::std::string* output) { + std::string* output) { #if HAVE_SNAPPY output->resize(snappy::MaxCompressedLength(length)); size_t outlen; From 416344de2fdffb3f17c565b984885d0122bfa1e9 Mon Sep 17 00:00:00 2001 From: costan Date: Thu, 28 Mar 2019 09:17:47 -0700 Subject: [PATCH 141/174] leveldb: Register in copybara whitelist. The documentation recommends modifying the whitelist after evaluating Copybara. However, evaluating requires significant workarounds without the whitelist entry. So, this CL adds leveldb to the whitelist early. leveldb is currently open sourced to https://github.com/google/leveldb using MOE. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=240786286 --- copy.bara.sky | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 copy.bara.sky diff --git a/copy.bara.sky b/copy.bara.sky new file mode 100644 index 0000000000..e69de29bb2 From 35619d248d909b197f68226c7d0a9ff947b82e8a Mon Sep 17 00:00:00 2001 From: leveldb Team Date: Fri, 29 Mar 2019 13:58:44 -0700 Subject: [PATCH 142/174] Project import generated by Copybara. PiperOrigin-RevId: 241045448 --- copy.bara.sky | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 copy.bara.sky diff --git a/copy.bara.sky b/copy.bara.sky deleted file mode 100644 index e69de29bb2..0000000000 From 56178ddaf4d3ba6c8d1cfb218610b1be3f5aa710 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Fri, 29 Mar 2019 14:36:59 -0700 Subject: [PATCH 143/174] Update the version to 1.21 in preparation for a new release. PiperOrigin-RevId: 241053616 --- CMakeLists.txt | 1 + include/leveldb/db.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 934f15affd..e471a2aad5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ # found in the LICENSE file. See the AUTHORS file for names of contributors. cmake_minimum_required(VERSION 3.9) +# Keep the version below in sync with the one in db.h project(leveldb VERSION 1.21.0 LANGUAGES C CXX) # This project can use C11, but will gracefully decay down to C89. diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 84c32bc1bb..0239593368 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -13,9 +13,9 @@ namespace leveldb { -// Update Makefile if you change these +// Update CMakeLists.txt if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 20; +static const int kMinorVersion = 21; struct Options; struct ReadOptions; From 952be04df6edb936b8f7d0f652861100a7f61e97 Mon Sep 17 00:00:00 2001 From: Pavel Pimenov Date: Sun, 31 Mar 2019 10:46:31 +0300 Subject: [PATCH 144/174] Fix mkdir (windows) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f8ce63ab0..0121dd8b63 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . First generate the Visual Studio 2017 project/solution files: ```bash -mkdir -p build +mkdir build cd build cmake -G "Visual Studio 15" .. ``` From 37300aa54b8256dd2edfd504942eb2bd20823647 Mon Sep 17 00:00:00 2001 From: leveldb Team Date: Mon, 1 Apr 2019 08:59:17 -0700 Subject: [PATCH 145/174] Restore soname versioning with CMake build Before: $ readelf -d build/libleveldb.so | grep soname 0x000000000000000e (SONAME) Library soname: [libleveldb.so] After: $ readelf -d build/libleveldb.so | grep soname 0x000000000000000e (SONAME) Library soname: [libleveldb.so.1] This matches the soname from v1.20. PiperOrigin-RevId: 241334113 --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e471a2aad5..ceb5dd910e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,6 +233,10 @@ if(BUILD_SHARED_LIBS) # Used by include/export.h. LEVELDB_SHARED_LIBRARY ) + set_target_properties(leveldb + PROPERTIES VERSION ${PROJECT_VERSION}) + set_target_properties(leveldb + PROPERTIES SOVERSION 1) endif(BUILD_SHARED_LIBS) if(HAVE_CLANG_THREAD_SAFETY) From 20fb601aa9f68ff0aa147df22524b7d01758552b Mon Sep 17 00:00:00 2001 From: Richard Cole Date: Sun, 24 Jan 2016 18:10:16 -0800 Subject: [PATCH 146/174] Fix snapshot compaction bug Closes google/leveldb#320 During compaction it was possible that records from a block b1=(l1,u1) would be pushed down from level i to level i+1. If there is a block b2=(l2,u2) at level i with k1 = user_key(u1) = user_key(l2) then a subsequent search for k1 will yield the record l2 which has a smaller sequence number than u1 because the sort order for records sorts increasing by user key but decreaing by sequence number. This change add a call to a new function AddBoundaryInputs to SetupOtherInputs. AddBoundaryInputs searches for a block b2 matching the criteria above and adds it to the set of files to be compacted. Whenever AddBoundaryInputs is called it is important that the compaction fileset in level i+1 (known as c->inputs_[1] in the code) be recomputed. Each call to AddBoundaryInputs is followed by a call to GetOverlappingInputs. SetupOtherInputs is called on both manual and automated compaction passes. It is called for both level zero and for levels greater than 0. The original change posted in https://github.com/google/leveldb/pull/339 has been modified to also include changed made by Chris Mumford in https://github.com/cmumford/leveldb/commit/4b72cb14f8da2aab12451c24b8e205aff686e9dc 1. Releasing snapshots during test cleanup to avoid memory leak warnings. 2. Refactored test to use testutil.h to be in line with other issue tests and to create the test database in the correct temporary location. 3. Added copyright banner. Otherwise, just minor formatting and limiting character width to 80 characters. Additionally the change was rebased on top of current master and changes previously made to the Makefile were ported to the CMakeLists.txt. Testing Done: A test program (issue320_test) was constructed that performs mutations while snapshots are active. issue320_test fails without this bug fix after 64k writes. It passes with this bug fix. It was run with 200M writes and passed. Unit tests were written for the new function that was added to the code. Make test was run and seen to pass. Signed-off-by: Richard Cole --- CMakeLists.txt | 1 + db/version_set.cc | 79 +++++++++++++++++++++ db/version_set_test.cc | 148 +++++++++++++++++++++++++++++++++++++++- issues/issue320_test.cc | 139 +++++++++++++++++++++++++++++++++++++ 4 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 issues/issue320_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 934f15affd..aada6b77d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,7 @@ if(LEVELDB_BUILD_TESTS) leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue178_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue200_test.cc") + leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue320_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/env_test.cc") leveldb_test("${PROJECT_SOURCE_DIR}/util/status_test.cc") diff --git a/db/version_set.cc b/db/version_set.cc index 156a0078da..7891dfca73 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -1347,9 +1347,87 @@ Compaction* VersionSet::PickCompaction() { return c; } +// find the largest key in a vector of files. returns true if files it not empty +bool FindLargestKey(const InternalKeyComparator & icmp, const std::vector & files, InternalKey *largestKey) { + if (files.empty()) { + return false; + } + *largestKey = files[0]->largest; + for (size_t i = 1; i < files.size(); ++i) { + FileMetaData* f = files[i]; + if (icmp.Compare(f->largest, *largestKey) > 0) { + *largestKey = f->largest; + } + } + return true; +} + +// find minimum file b2=(l2, u2) in level file for which l2 > u1 and user_key(l2) = user_key(u1) +FileMetaData* FindSmallestBoundaryFile(const InternalKeyComparator & icmp, + const std::vector & levelFiles, + const InternalKey & largestKey) { + const Comparator* user_cmp = icmp.user_comparator(); + FileMetaData* smallestBoundaryFile = NULL; + for (size_t i = 0; i < levelFiles.size(); ++i) { + FileMetaData* f = levelFiles[i]; + if (icmp.Compare(f->smallest, largestKey) > 0 && + user_cmp->Compare(f->smallest.user_key(), largestKey.user_key()) == 0) { + if (smallestBoundaryFile == NULL || + icmp.Compare(f->smallest, smallestBoundaryFile->smallest) < 0) { + smallestBoundaryFile = f; + } + } + } + return smallestBoundaryFile; +} + +// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and +// user_key(u1) = user_key(l2), and if we compact b1 but not +// b2 then a subsequent get operation will yield an incorrect +// result because it will return the record from b2 in level +// i rather than from b1 because it searches level by level +// for records matching the supplied user key. +// +// This function extracts the largest file b1 from compactionFiles +// and then searches for a b2 in levelFiles for which user_key(u1) = +// user_key(l2). If it finds such a file b2 (known as a boundary file) +// it adds it to compactionFiles and then searches again using this +// new upper bound. +// +// parameters: +// in levelFiles: list of files to search for boundary files +// in/out compactionFiles: list of files to extend by adding boundary files +void AddBoundaryInputs(const InternalKeyComparator& icmp, + const std::vector& levelFiles, + std::vector* compactionFiles) { + InternalKey largestKey; + + // find largestKey in compactionFiles, quick return if compactionFiles is + // empty + if (!FindLargestKey(icmp, *compactionFiles, &largestKey)) { + return; + } + + bool continueSearching = true; + while (continueSearching) { + FileMetaData* smallestBoundaryFile = + FindSmallestBoundaryFile(icmp, levelFiles, largestKey); + + // if a boundary file was found advance largestKey, otherwise we're done + if (smallestBoundaryFile != NULL) { + compactionFiles->push_back(smallestBoundaryFile); + largestKey = smallestBoundaryFile->largest; + } else { + continueSearching = false; + } + } +} + void VersionSet::SetupOtherInputs(Compaction* c) { const int level = c->level(); InternalKey smallest, largest; + + AddBoundaryInputs(icmp_, current_->files_[level], &c->inputs_[0]); GetRange(c->inputs_[0], &smallest, &largest); current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); @@ -1363,6 +1441,7 @@ void VersionSet::SetupOtherInputs(Compaction* c) { if (!c->inputs_[1].empty()) { std::vector expanded0; current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + AddBoundaryInputs(icmp_, current_->files_[level], &expanded0); const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); const int64_t expanded0_size = TotalFileSize(expanded0); diff --git a/db/version_set_test.cc b/db/version_set_test.cc index d21433e9a2..090f115ed8 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -172,7 +172,153 @@ TEST(FindFileTest, OverlappingFiles) { ASSERT_TRUE(Overlaps("600", "700")); } -} // namespace leveldb +void AddBoundaryInputs(const InternalKeyComparator &icmp, + const std::vector &levelFiles, + std::vector *compactionFiles); + +class AddBoundaryInputsTest { + public: + std::vector levelFiles_; + std::vector compactionFiles_; + std::vector allFiles_; + InternalKeyComparator icmp_; + + AddBoundaryInputsTest() : icmp_(BytewiseComparator()){}; + + ~AddBoundaryInputsTest() { + for (size_t i = 0; i < allFiles_.size(); ++i) { + delete allFiles_[i]; + } + allFiles_.clear(); + }; + + FileMetaData *CreateFileMetaData(uint64_t number, InternalKey smallest, + InternalKey largest) { + FileMetaData *f = new FileMetaData(); + f->number = number; + f->smallest = smallest; + f->largest = largest; + allFiles_.push_back(f); + return f; + } +}; + +TEST(AddBoundaryInputsTest, TestEmptyFileSets) { + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_TRUE(compactionFiles_.empty()); + ASSERT_TRUE(levelFiles_.empty()); +} + +TEST(AddBoundaryInputsTest, TestEmptyLevelFiles) { + FileMetaData *f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + compactionFiles_.push_back(f1); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_EQ(1, compactionFiles_.size()); + ASSERT_EQ(f1, compactionFiles_[0]); + ASSERT_TRUE(levelFiles_.empty()); +} + +TEST(AddBoundaryInputsTest, TestEmptyCompactionFiles) { + FileMetaData *f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + levelFiles_.push_back(f1); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_TRUE(compactionFiles_.empty()); + ASSERT_EQ(1, levelFiles_.size()); + ASSERT_EQ(f1, levelFiles_[0]); +} + +TEST(AddBoundaryInputsTest, TestNoBoundaryFiles) { + FileMetaData *f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + FileMetaData *f2 = + CreateFileMetaData(1, InternalKey("200", 2, kTypeValue), + InternalKey(InternalKey("200", 1, kTypeValue))); + FileMetaData *f3 = + CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + + levelFiles_.push_back(f3); + levelFiles_.push_back(f2); + levelFiles_.push_back(f1); + compactionFiles_.push_back(f2); + compactionFiles_.push_back(f3); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_EQ(2, compactionFiles_.size()); +} + +TEST(AddBoundaryInputsTest, TestOneBoundaryFiles) { + FileMetaData *f1 = + CreateFileMetaData(1, InternalKey("100", 3, kTypeValue), + InternalKey(InternalKey("100", 2, kTypeValue))); + FileMetaData *f2 = + CreateFileMetaData(1, InternalKey("100", 1, kTypeValue), + InternalKey(InternalKey("200", 3, kTypeValue))); + FileMetaData *f3 = + CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + + levelFiles_.push_back(f3); + levelFiles_.push_back(f2); + levelFiles_.push_back(f1); + compactionFiles_.push_back(f1); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_EQ(2, compactionFiles_.size()); + ASSERT_EQ(f1, compactionFiles_[0]); + ASSERT_EQ(f2, compactionFiles_[1]); +} + +TEST(AddBoundaryInputsTest, TestTwoBoundaryFiles) { + FileMetaData *f1 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData *f2 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + FileMetaData *f3 = + CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), + InternalKey(InternalKey("100", 3, kTypeValue))); + + levelFiles_.push_back(f2); + levelFiles_.push_back(f3); + levelFiles_.push_back(f1); + compactionFiles_.push_back(f1); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_EQ(3, compactionFiles_.size()); + ASSERT_EQ(f1, compactionFiles_[0]); + ASSERT_EQ(f3, compactionFiles_[1]); + ASSERT_EQ(f2, compactionFiles_[2]); +} + +TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) { + FileMetaData *f1 = CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData *f2 = CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData *f3 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("300", 1, kTypeValue))); + FileMetaData *f4 = CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), InternalKey(InternalKey("100", 3, kTypeValue))); + + levelFiles_.push_back(f2); + levelFiles_.push_back(f3); + levelFiles_.push_back(f4); + + compactionFiles_.push_back(f1); + + AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); + ASSERT_EQ(3, compactionFiles_.size()); + ASSERT_EQ(f1, compactionFiles_[0]); + ASSERT_EQ(f4, compactionFiles_[1]); + ASSERT_EQ(f3, compactionFiles_[2]); +} + +} // namespace leveldb int main(int argc, char** argv) { return leveldb::test::RunAllTests(); diff --git a/issues/issue320_test.cc b/issues/issue320_test.cc new file mode 100644 index 0000000000..619ddb553d --- /dev/null +++ b/issues/issue320_test.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2019 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; + +namespace leveldb { + +namespace { + +unsigned int random(unsigned int max) { + return std::rand() % max; +} + +string newString(int32_t index) { + const unsigned int len = 1024; + char bytes[len]; + unsigned int i = 0; + while (i < 8) { + bytes[i] = 'a' + ((index >> (4 * i)) & 0xf); + ++i; + } + while (i < sizeof(bytes)) { + bytes[i] = 'a' + random(26); + ++i; + } + return string(bytes, sizeof(bytes)); +} + +} // namespace + +class Issue320 { }; + +TEST(Issue320, Test) { + std::srand(0); + + bool delete_before_put = false; + bool keep_snapshots = true; + + vector*> test_map(10000, nullptr); + vector snapshots(100, nullptr); + + DB* db; + Options options; + options.create_if_missing = true; + + std::string dbpath = test::TmpDir() + "/leveldb_issue320_test"; + ASSERT_OK(DB::Open(options, dbpath, &db)); + + unsigned int target_size = 10000; + unsigned int num_items = 0; + unsigned long count = 0; + string key; + string value, old_value; + + WriteOptions writeOptions; + ReadOptions readOptions; + while (count < 200000) { + if ((++count % 1000) == 0) { + cout << "count: " << count << endl; + } + + unsigned int index = random(test_map.size()); + WriteBatch batch; + + if (test_map[index] == nullptr) { + num_items++; + test_map[index] = + new pair(newString(index), newString(index)); + batch.Put(test_map[index]->first, test_map[index]->second); + } else { + ASSERT_OK(db->Get(readOptions, test_map[index]->first, &old_value)); + if (old_value != test_map[index]->second) { + cout << "ERROR incorrect value returned by Get" << endl; + cout << " count=" << count << endl; + cout << " old value=" << old_value << endl; + cout << " test_map[index]->second=" << test_map[index]->second << endl; + cout << " test_map[index]->first=" << test_map[index]->first << endl; + cout << " index=" << index << endl; + ASSERT_EQ(old_value, test_map[index]->second); + } + + if (num_items >= target_size && random(100) > 30) { + batch.Delete(test_map[index]->first); + delete test_map[index]; + test_map[index] = nullptr; + --num_items; + } else { + test_map[index]->second = newString(index); + if (delete_before_put) batch.Delete(test_map[index]->first); + batch.Put(test_map[index]->first, test_map[index]->second); + } + } + + ASSERT_OK(db->Write(writeOptions, &batch)); + + if (keep_snapshots && random(10) == 0) { + unsigned int i = random(snapshots.size()); + if (snapshots[i] != nullptr) { + db->ReleaseSnapshot(snapshots[i]); + } + snapshots[i] = db->GetSnapshot(); + } + } + + for (Snapshot const* snapshot : snapshots) { + if (snapshot) { + db->ReleaseSnapshot(snapshot); + } + } + + for (size_t i = 0; i < test_map.size(); ++i) { + if (test_map[i] != nullptr) { + delete test_map[i]; + test_map[i] = nullptr; + } + } + + delete db; + DestroyDB(dbpath, options); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} From 09fa8868dbe0cb2701f0560c59ebb63cc17f1271 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Mon, 1 Apr 2019 17:17:47 -0700 Subject: [PATCH 147/174] Align version/soversion CMake setup closer with other repositories. PiperOrigin-RevId: 241432456 --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ceb5dd910e..d50f6c2239 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,10 @@ target_include_directories(leveldb $ $ ) + +set_target_properties(leveldb + PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) + target_compile_definitions(leveldb PRIVATE # Used by include/export.h when building shared libraries. @@ -233,10 +237,6 @@ if(BUILD_SHARED_LIBS) # Used by include/export.h. LEVELDB_SHARED_LIBRARY ) - set_target_properties(leveldb - PROPERTIES VERSION ${PROJECT_VERSION}) - set_target_properties(leveldb - PROPERTIES SOVERSION 1) endif(BUILD_SHARED_LIBS) if(HAVE_CLANG_THREAD_SAFETY) From 71ed7c401ec1b1e38d6f7cb9eb2fcff93c24d1f1 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 11 Apr 2019 12:25:12 -0700 Subject: [PATCH 148/174] Fixed typo in comment in version_set.h. Flagged by presubmit check. PiperOrigin-RevId: 243118632 --- db/version_set.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/version_set.h b/db/version_set.h index 77b98950ab..0beae4dd00 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -375,7 +375,7 @@ class Compaction { // Each compaction reads inputs from "level_" and "level_+1" std::vector inputs_[2]; // The two sets of inputs - // State used to check for number of of overlapping grandparent files + // State used to check for number of overlapping grandparent files // (parent == level_ + 1, grandparent == level_ + 2) std::vector grandparents_; size_t grandparent_index_; // Index in grandparent_starts_ From 65e86f75ea30e44bc65327f92a16328684269acb Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 11 Apr 2019 19:10:37 -0700 Subject: [PATCH 149/174] Fix formatting of recent snapshot compaction fix. Fix variable names, line lengths, namespace use, and a few other minor issues to conform to Google C++ style guide. PiperOrigin-RevId: 243187729 --- db/version_set.cc | 95 ++++++++++++----------- db/version_set_test.cc | 166 +++++++++++++++++++++------------------- issues/issue320_test.cc | 58 +++++++------- 3 files changed, 161 insertions(+), 158 deletions(-) diff --git a/db/version_set.cc b/db/version_set.cc index 7891dfca73..56493acd29 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -1347,78 +1347,81 @@ Compaction* VersionSet::PickCompaction() { return c; } -// find the largest key in a vector of files. returns true if files it not empty -bool FindLargestKey(const InternalKeyComparator & icmp, const std::vector & files, InternalKey *largestKey) { +// Finds the largest key in a vector of files. Returns true if files it not +// empty. +bool FindLargestKey(const InternalKeyComparator& icmp, + const std::vector& files, + InternalKey* largest_key) { if (files.empty()) { return false; } - *largestKey = files[0]->largest; + *largest_key = files[0]->largest; for (size_t i = 1; i < files.size(); ++i) { FileMetaData* f = files[i]; - if (icmp.Compare(f->largest, *largestKey) > 0) { - *largestKey = f->largest; + if (icmp.Compare(f->largest, *largest_key) > 0) { + *largest_key = f->largest; } } return true; } -// find minimum file b2=(l2, u2) in level file for which l2 > u1 and user_key(l2) = user_key(u1) -FileMetaData* FindSmallestBoundaryFile(const InternalKeyComparator & icmp, - const std::vector & levelFiles, - const InternalKey & largestKey) { +// Finds minimum file b2=(l2, u2) in level file for which l2 > u1 and +// user_key(l2) = user_key(u1) +FileMetaData* FindSmallestBoundaryFile( + const InternalKeyComparator& icmp, + const std::vector& level_files, + const InternalKey& largest_key) { const Comparator* user_cmp = icmp.user_comparator(); - FileMetaData* smallestBoundaryFile = NULL; - for (size_t i = 0; i < levelFiles.size(); ++i) { - FileMetaData* f = levelFiles[i]; - if (icmp.Compare(f->smallest, largestKey) > 0 && - user_cmp->Compare(f->smallest.user_key(), largestKey.user_key()) == 0) { - if (smallestBoundaryFile == NULL || - icmp.Compare(f->smallest, smallestBoundaryFile->smallest) < 0) { - smallestBoundaryFile = f; + FileMetaData* smallest_boundary_file = nullptr; + for (size_t i = 0; i < level_files.size(); ++i) { + FileMetaData* f = level_files[i]; + if (icmp.Compare(f->smallest, largest_key) > 0 && + user_cmp->Compare(f->smallest.user_key(), largest_key.user_key()) == + 0) { + if (smallest_boundary_file == nullptr || + icmp.Compare(f->smallest, smallest_boundary_file->smallest) < 0) { + smallest_boundary_file = f; } } } - return smallestBoundaryFile; + return smallest_boundary_file; } -// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and -// user_key(u1) = user_key(l2), and if we compact b1 but not -// b2 then a subsequent get operation will yield an incorrect -// result because it will return the record from b2 in level -// i rather than from b1 because it searches level by level -// for records matching the supplied user key. +// Extracts the largest file b1 from |compaction_files| and then searches for a +// b2 in |level_files| for which user_key(u1) = user_key(l2). If it finds such a +// file b2 (known as a boundary file) it adds it to |compaction_files| and then +// searches again using this new upper bound. // -// This function extracts the largest file b1 from compactionFiles -// and then searches for a b2 in levelFiles for which user_key(u1) = -// user_key(l2). If it finds such a file b2 (known as a boundary file) -// it adds it to compactionFiles and then searches again using this -// new upper bound. +// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and +// user_key(u1) = user_key(l2), and if we compact b1 but not b2 then a +// subsequent get operation will yield an incorrect result because it will +// return the record from b2 in level i rather than from b1 because it searches +// level by level for records matching the supplied user key. // // parameters: -// in levelFiles: list of files to search for boundary files -// in/out compactionFiles: list of files to extend by adding boundary files +// in level_files: List of files to search for boundary files. +// in/out compaction_files: List of files to extend by adding boundary files. void AddBoundaryInputs(const InternalKeyComparator& icmp, - const std::vector& levelFiles, - std::vector* compactionFiles) { - InternalKey largestKey; + const std::vector& level_files, + std::vector* compaction_files) { + InternalKey largest_key; - // find largestKey in compactionFiles, quick return if compactionFiles is - // empty - if (!FindLargestKey(icmp, *compactionFiles, &largestKey)) { + // Quick return if compaction_files is empty. + if (!FindLargestKey(icmp, *compaction_files, &largest_key)) { return; } - bool continueSearching = true; - while (continueSearching) { - FileMetaData* smallestBoundaryFile = - FindSmallestBoundaryFile(icmp, levelFiles, largestKey); + bool continue_searching = true; + while (continue_searching) { + FileMetaData* smallest_boundary_file = + FindSmallestBoundaryFile(icmp, level_files, largest_key); - // if a boundary file was found advance largestKey, otherwise we're done - if (smallestBoundaryFile != NULL) { - compactionFiles->push_back(smallestBoundaryFile); - largestKey = smallestBoundaryFile->largest; + // If a boundary file was found advance largest_key, otherwise we're done. + if (smallest_boundary_file != NULL) { + compaction_files->push_back(smallest_boundary_file); + largest_key = smallest_boundary_file->largest; } else { - continueSearching = false; + continue_searching = false; } } } diff --git a/db/version_set_test.cc b/db/version_set_test.cc index 090f115ed8..b32e2e5e20 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -172,154 +172,160 @@ TEST(FindFileTest, OverlappingFiles) { ASSERT_TRUE(Overlaps("600", "700")); } -void AddBoundaryInputs(const InternalKeyComparator &icmp, - const std::vector &levelFiles, - std::vector *compactionFiles); +void AddBoundaryInputs(const InternalKeyComparator& icmp, + const std::vector& level_files, + std::vector* compaction_files); class AddBoundaryInputsTest { public: - std::vector levelFiles_; - std::vector compactionFiles_; - std::vector allFiles_; + std::vector level_files_; + std::vector compaction_files_; + std::vector all_files_; InternalKeyComparator icmp_; AddBoundaryInputsTest() : icmp_(BytewiseComparator()){}; ~AddBoundaryInputsTest() { - for (size_t i = 0; i < allFiles_.size(); ++i) { - delete allFiles_[i]; + for (size_t i = 0; i < all_files_.size(); ++i) { + delete all_files_[i]; } - allFiles_.clear(); + all_files_.clear(); }; - FileMetaData *CreateFileMetaData(uint64_t number, InternalKey smallest, + FileMetaData* CreateFileMetaData(uint64_t number, InternalKey smallest, InternalKey largest) { - FileMetaData *f = new FileMetaData(); + FileMetaData* f = new FileMetaData(); f->number = number; f->smallest = smallest; f->largest = largest; - allFiles_.push_back(f); + all_files_.push_back(f); return f; } }; TEST(AddBoundaryInputsTest, TestEmptyFileSets) { - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_TRUE(compactionFiles_.empty()); - ASSERT_TRUE(levelFiles_.empty()); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_TRUE(compaction_files_.empty()); + ASSERT_TRUE(level_files_.empty()); } TEST(AddBoundaryInputsTest, TestEmptyLevelFiles) { - FileMetaData *f1 = + FileMetaData* f1 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("100", 1, kTypeValue))); - compactionFiles_.push_back(f1); + compaction_files_.push_back(f1); - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_EQ(1, compactionFiles_.size()); - ASSERT_EQ(f1, compactionFiles_[0]); - ASSERT_TRUE(levelFiles_.empty()); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(1, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_TRUE(level_files_.empty()); } TEST(AddBoundaryInputsTest, TestEmptyCompactionFiles) { - FileMetaData *f1 = + FileMetaData* f1 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("100", 1, kTypeValue))); - levelFiles_.push_back(f1); + level_files_.push_back(f1); - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_TRUE(compactionFiles_.empty()); - ASSERT_EQ(1, levelFiles_.size()); - ASSERT_EQ(f1, levelFiles_[0]); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_TRUE(compaction_files_.empty()); + ASSERT_EQ(1, level_files_.size()); + ASSERT_EQ(f1, level_files_[0]); } TEST(AddBoundaryInputsTest, TestNoBoundaryFiles) { - FileMetaData *f1 = + FileMetaData* f1 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("100", 1, kTypeValue))); - FileMetaData *f2 = + FileMetaData* f2 = CreateFileMetaData(1, InternalKey("200", 2, kTypeValue), InternalKey(InternalKey("200", 1, kTypeValue))); - FileMetaData *f3 = + FileMetaData* f3 = CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), InternalKey(InternalKey("300", 1, kTypeValue))); - levelFiles_.push_back(f3); - levelFiles_.push_back(f2); - levelFiles_.push_back(f1); - compactionFiles_.push_back(f2); - compactionFiles_.push_back(f3); + level_files_.push_back(f3); + level_files_.push_back(f2); + level_files_.push_back(f1); + compaction_files_.push_back(f2); + compaction_files_.push_back(f3); - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_EQ(2, compactionFiles_.size()); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(2, compaction_files_.size()); } TEST(AddBoundaryInputsTest, TestOneBoundaryFiles) { - FileMetaData *f1 = + FileMetaData* f1 = CreateFileMetaData(1, InternalKey("100", 3, kTypeValue), InternalKey(InternalKey("100", 2, kTypeValue))); - FileMetaData *f2 = + FileMetaData* f2 = CreateFileMetaData(1, InternalKey("100", 1, kTypeValue), InternalKey(InternalKey("200", 3, kTypeValue))); - FileMetaData *f3 = + FileMetaData* f3 = CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), InternalKey(InternalKey("300", 1, kTypeValue))); - levelFiles_.push_back(f3); - levelFiles_.push_back(f2); - levelFiles_.push_back(f1); - compactionFiles_.push_back(f1); + level_files_.push_back(f3); + level_files_.push_back(f2); + level_files_.push_back(f1); + compaction_files_.push_back(f1); - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_EQ(2, compactionFiles_.size()); - ASSERT_EQ(f1, compactionFiles_[0]); - ASSERT_EQ(f2, compactionFiles_[1]); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(2, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f2, compaction_files_[1]); } TEST(AddBoundaryInputsTest, TestTwoBoundaryFiles) { - FileMetaData *f1 = + FileMetaData* f1 = CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), InternalKey(InternalKey("100", 5, kTypeValue))); - FileMetaData *f2 = + FileMetaData* f2 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("300", 1, kTypeValue))); - FileMetaData *f3 = + FileMetaData* f3 = CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), InternalKey(InternalKey("100", 3, kTypeValue))); - levelFiles_.push_back(f2); - levelFiles_.push_back(f3); - levelFiles_.push_back(f1); - compactionFiles_.push_back(f1); + level_files_.push_back(f2); + level_files_.push_back(f3); + level_files_.push_back(f1); + compaction_files_.push_back(f1); - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_EQ(3, compactionFiles_.size()); - ASSERT_EQ(f1, compactionFiles_[0]); - ASSERT_EQ(f3, compactionFiles_[1]); - ASSERT_EQ(f2, compactionFiles_[2]); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(3, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f3, compaction_files_[1]); + ASSERT_EQ(f2, compaction_files_[2]); } TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) { - FileMetaData *f1 = CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), InternalKey(InternalKey("100", 5, kTypeValue))); - FileMetaData *f2 = CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), InternalKey(InternalKey("100", 5, kTypeValue))); - FileMetaData *f3 = CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), InternalKey(InternalKey("300", 1, kTypeValue))); - FileMetaData *f4 = CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), InternalKey(InternalKey("100", 3, kTypeValue))); - - levelFiles_.push_back(f2); - levelFiles_.push_back(f3); - levelFiles_.push_back(f4); - - compactionFiles_.push_back(f1); - - AddBoundaryInputs(icmp_, levelFiles_, &compactionFiles_); - ASSERT_EQ(3, compactionFiles_.size()); - ASSERT_EQ(f1, compactionFiles_[0]); - ASSERT_EQ(f4, compactionFiles_[1]); - ASSERT_EQ(f3, compactionFiles_[2]); -} + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData* f2 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData* f3 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + FileMetaData* f4 = + CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), + InternalKey(InternalKey("100", 3, kTypeValue))); -} // namespace leveldb + level_files_.push_back(f2); + level_files_.push_back(f3); + level_files_.push_back(f4); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + compaction_files_.push_back(f1); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(3, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f4, compaction_files_[1]); + ASSERT_EQ(f3, compaction_files_[2]); } + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/issues/issue320_test.cc b/issues/issue320_test.cc index a7c37b1d68..28ef1b813b 100644 --- a/issues/issue320_test.cc +++ b/issues/issue320_test.cc @@ -1,47 +1,43 @@ // Copyright (c) 2019 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include #include -#include #include -#include #include #include -#include - #include "leveldb/db.h" #include "leveldb/write_batch.h" #include "util/testharness.h" -using namespace std; - namespace leveldb { namespace { -unsigned int random(unsigned int max) { - return std::rand() % max; -} +// Creates a random number in the range of [0, max). +int GenerateRandomNumber(int max) { return std::rand() % max; } -string newString(int32_t index) { - const unsigned int len = 1024; +std::string CreateRandomString(int32_t index) { + static const size_t len = 1024; char bytes[len]; - unsigned int i = 0; + size_t i = 0; while (i < 8) { bytes[i] = 'a' + ((index >> (4 * i)) & 0xf); ++i; } while (i < sizeof(bytes)) { - bytes[i] = 'a' + random(26); + bytes[i] = 'a' + GenerateRandomNumber(26); ++i; } - return string(bytes, sizeof(bytes)); + return std::string(bytes, sizeof(bytes)); } } // namespace -class Issue320 { }; +class Issue320 {}; TEST(Issue320, Test) { std::srand(0); @@ -49,8 +45,8 @@ TEST(Issue320, Test) { bool delete_before_put = false; bool keep_snapshots = true; - vector*> test_map(10000, nullptr); - vector snapshots(100, nullptr); + std::vector*> test_map(10000, nullptr); + std::vector snapshots(100, nullptr); DB* db; Options options; @@ -59,11 +55,11 @@ TEST(Issue320, Test) { std::string dbpath = test::TmpDir() + "/leveldb_issue320_test"; ASSERT_OK(DB::Open(options, dbpath, &db)); - unsigned int target_size = 10000; - unsigned int num_items = 0; - unsigned long count = 0; - string key; - string value, old_value; + uint32_t target_size = 10000; + uint32_t num_items = 0; + uint32_t count = 0; + std::string key; + std::string value, old_value; WriteOptions writeOptions; ReadOptions readOptions; @@ -72,13 +68,13 @@ TEST(Issue320, Test) { cout << "count: " << count << endl; } - unsigned int index = random(test_map.size()); + int index = GenerateRandomNumber(test_map.size()); WriteBatch batch; if (test_map[index] == nullptr) { num_items++; - test_map[index] = - new pair(newString(index), newString(index)); + test_map[index] = new std::pair( + CreateRandomString(index), CreateRandomString(index)); batch.Put(test_map[index]->first, test_map[index]->second); } else { ASSERT_OK(db->Get(readOptions, test_map[index]->first, &old_value)); @@ -92,13 +88,13 @@ TEST(Issue320, Test) { ASSERT_EQ(old_value, test_map[index]->second); } - if (num_items >= target_size && random(100) > 30) { + if (num_items >= target_size && GenerateRandomNumber(100) > 30) { batch.Delete(test_map[index]->first); delete test_map[index]; test_map[index] = nullptr; --num_items; } else { - test_map[index]->second = newString(index); + test_map[index]->second = CreateRandomString(index); if (delete_before_put) batch.Delete(test_map[index]->first); batch.Put(test_map[index]->first, test_map[index]->second); } @@ -106,8 +102,8 @@ TEST(Issue320, Test) { ASSERT_OK(db->Write(writeOptions, &batch)); - if (keep_snapshots && random(10) == 0) { - unsigned int i = random(snapshots.size()); + if (keep_snapshots && GenerateRandomNumber(10) == 0) { + int i = GenerateRandomNumber(snapshots.size()); if (snapshots[i] != nullptr) { db->ReleaseSnapshot(snapshots[i]); } @@ -134,6 +130,4 @@ TEST(Issue320, Test) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } From 08e771901f454ac32643bd8e8cb2bcfa08026c0c Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 11 Apr 2019 19:47:16 -0700 Subject: [PATCH 150/174] Simplify issue320_test. Use std::unique_ptr to simplify issue320_test. PiperOrigin-RevId: 243190799 --- issues/issue320_test.cc | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/issues/issue320_test.cc b/issues/issue320_test.cc index 28ef1b813b..51458571d7 100644 --- a/issues/issue320_test.cc +++ b/issues/issue320_test.cc @@ -45,7 +45,8 @@ TEST(Issue320, Test) { bool delete_before_put = false; bool keep_snapshots = true; - std::vector*> test_map(10000, nullptr); + std::vector>> test_map( + 10000); std::vector snapshots(100, nullptr); DB* db; @@ -73,7 +74,7 @@ TEST(Issue320, Test) { if (test_map[index] == nullptr) { num_items++; - test_map[index] = new std::pair( + test_map[index] = std::make_unique>( CreateRandomString(index), CreateRandomString(index)); batch.Put(test_map[index]->first, test_map[index]->second); } else { @@ -90,7 +91,6 @@ TEST(Issue320, Test) { if (num_items >= target_size && GenerateRandomNumber(100) > 30) { batch.Delete(test_map[index]->first); - delete test_map[index]; test_map[index] = nullptr; --num_items; } else { @@ -117,13 +117,6 @@ TEST(Issue320, Test) { } } - for (size_t i = 0; i < test_map.size(); ++i) { - if (test_map[i] != nullptr) { - delete test_map[i]; - test_map[i] = nullptr; - } - } - delete db; DestroyDB(dbpath, options); } From 5a2a472741f36ecf5b994439da5a64c6ab90c47f Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Fri, 12 Apr 2019 01:09:39 -0700 Subject: [PATCH 151/174] Fixed missing std namespaces and make_unique. cout/endl were missing the std namespace. Also std::make_unique was used inadvertently which is part of C++14 and only C++11 is currently supported. PiperOrigin-RevId: 243221310 --- issues/issue320_test.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/issues/issue320_test.cc b/issues/issue320_test.cc index 51458571d7..c5fcbfc6e7 100644 --- a/issues/issue320_test.cc +++ b/issues/issue320_test.cc @@ -66,7 +66,7 @@ TEST(Issue320, Test) { ReadOptions readOptions; while (count < 200000) { if ((++count % 1000) == 0) { - cout << "count: " << count << endl; + std::cout << "count: " << count << std::endl; } int index = GenerateRandomNumber(test_map.size()); @@ -74,18 +74,20 @@ TEST(Issue320, Test) { if (test_map[index] == nullptr) { num_items++; - test_map[index] = std::make_unique>( - CreateRandomString(index), CreateRandomString(index)); + test_map[index].reset(new std::pair( + CreateRandomString(index), CreateRandomString(index))); batch.Put(test_map[index]->first, test_map[index]->second); } else { ASSERT_OK(db->Get(readOptions, test_map[index]->first, &old_value)); if (old_value != test_map[index]->second) { - cout << "ERROR incorrect value returned by Get" << endl; - cout << " count=" << count << endl; - cout << " old value=" << old_value << endl; - cout << " test_map[index]->second=" << test_map[index]->second << endl; - cout << " test_map[index]->first=" << test_map[index]->first << endl; - cout << " index=" << index << endl; + std::cout << "ERROR incorrect value returned by Get" << std::endl; + std::cout << " count=" << count << std::endl; + std::cout << " old value=" << old_value << std::endl; + std::cout << " test_map[index]->second=" << test_map[index]->second + << std::endl; + std::cout << " test_map[index]->first=" << test_map[index]->first + << std::endl; + std::cout << " index=" << index << std::endl; ASSERT_EQ(old_value, test_map[index]->second); } From 2f008ac19ec783e4d0ba2161320241c99e9897e1 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Fri, 12 Apr 2019 18:34:19 -0700 Subject: [PATCH 152/174] Initialize class members to default values in constructors. There were a few members which were identified to have been left uninitialized in some constructors. These were very likely to have been set before being used, otherwise the ASan tests would have caught them, but still good practice to have them initialized. This addresses some items reported in issue #668. PiperOrigin-RevId: 243370145 --- db/db_bench.cc | 5 +---- db/db_impl.cc | 7 ++++--- util/cache.cc | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/db/db_bench.cc b/db/db_bench.cc index f9403f497a..41e903b7a5 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -304,10 +304,7 @@ struct ThreadState { Stats stats; SharedState* shared; - ThreadState(int index) - : tid(index), - rand(1000 + index) { - } + ThreadState(int index) : tid(index), rand(1000 + index), shared(nullptr) {} }; } // namespace diff --git a/db/db_impl.cc b/db/db_impl.cc index 34688622c4..caef2b1bde 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -48,7 +48,8 @@ struct DBImpl::Writer { bool done; port::CondVar cv; - explicit Writer(port::Mutex* mu) : cv(mu) { } + explicit Writer(port::Mutex* mu) + : batch(nullptr), sync(false), done(false), cv(mu) {} }; struct DBImpl::CompactionState { @@ -78,10 +79,10 @@ struct DBImpl::CompactionState { explicit CompactionState(Compaction* c) : compaction(c), + smallest_snapshot(0), outfile(nullptr), builder(nullptr), - total_bytes(0) { - } + total_bytes(0) {} }; // Fix user-supplied options to be reasonable diff --git a/util/cache.cc b/util/cache.cc index 7cc2cea9ce..25b51b535e 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -196,8 +196,7 @@ class LRUCache { HandleTable table_ GUARDED_BY(mutex_); }; -LRUCache::LRUCache() - : usage_(0) { +LRUCache::LRUCache() : capacity_(0), usage_(0) { // Make empty circular linked lists. lru_.next = &lru_; lru_.prev = &lru_; From 7b1174519044339f07a023dc445b0d36425bd6db Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Sat, 13 Apr 2019 09:19:55 -0700 Subject: [PATCH 153/174] Changed Windows specific highlighting from bash to cmd. This makes the syntax highlighting a little nicer on GitHub. PiperOrigin-RevId: 243426806 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0121dd8b63..537cab16e4 100644 --- a/README.md +++ b/README.md @@ -44,20 +44,20 @@ cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . First generate the Visual Studio 2017 project/solution files: -```bash +```cmd mkdir build cd build cmake -G "Visual Studio 15" .. ``` The default default will build for x86. For 64-bit run: -```bash +```cmd cmake -G "Visual Studio 15 Win64" .. ``` To compile the Windows solution from the command-line: -```bash +```cmd devenv /build Debug leveldb.sln ``` From 2ccb45c33aecd8b15000c0c622f45eb119b6b478 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Mon, 15 Apr 2019 15:11:03 -0700 Subject: [PATCH 154/174] Check for possibly invalid offset in test. Fix a possible array bounds offset issue flagged in issue #668. Not the source of any known bug, but will silence any static analyzers. PiperOrigin-RevId: 243697659 --- table/table_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/table/table_test.cc b/table/table_test.cc index e47db3d0e7..5573be6e1e 100644 --- a/table/table_test.cc +++ b/table/table_test.cc @@ -119,7 +119,7 @@ class StringSource: public RandomAccessFile { virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { - if (offset > contents_.size()) { + if (offset >= contents_.size()) { return Status::InvalidArgument("invalid Read offset"); } if (offset + n > contents_.size()) { From 3dc9202f78a3eb30ee8c0267e4e4be2e3f986e45 Mon Sep 17 00:00:00 2001 From: leveldb Team Date: Tue, 23 Apr 2019 11:00:28 -0700 Subject: [PATCH 155/174] [leveldb] Specifically export the WriteBatch::Handler inner class for Windows link Windows linking visibility in shared libraries requires that inner classes are specifically exported as visible, even if the containing class is exported. PiperOrigin-RevId: 244886019 --- include/leveldb/write_batch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 9b319f0587..5380c537cc 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -62,7 +62,7 @@ class LEVELDB_EXPORT WriteBatch { void Append(const WriteBatch& source); // Support for iterating over the contents of a batch. - class Handler { + class LEVELDB_EXPORT Handler { public: virtual ~Handler(); virtual void Put(const Slice& key, const Slice& value) = 0; From d3d1c8a0f40a7eaa12a5bb702fa01786b7c3a646 Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Thu, 25 Apr 2019 09:44:07 +0800 Subject: [PATCH 156/174] don't check current key in DBIter::Next() When iter_ is pointing to current key, we can safely move to the next key to avoid checking current key, which is of course not necessary. Benchmark shows that 'readseq' has about 8% performance improvement. Without patch: >./db_bench --benchmarks=readseq --num=$((4<<20)) --db=/tmp/db --use_existing_db=1 LevelDB: version 1.21 Date: Thu Apr 25 09:37:21 2019 CPU: 32 * Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz CPUCache: 20480 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 4194304 RawSize: 464.0 MB (estimated) FileSize: 264.0 MB (estimated) ------------------------------------------------ readseq : 0.196 micros/op; 565.7 MB/s With patch: >./db_bench --benchmarks=readseq --num=$((4<<20)) --db=/tmp/db --use_existing_db=1 LevelDB: version 1.21 Date: Thu Apr 25 09:38:20 2019 CPU: 32 * Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz CPUCache: 20480 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 4194304 RawSize: 464.0 MB (estimated) FileSize: 264.0 MB (estimated) ------------------------------------------------ readseq : 0.181 micros/op; 612.3 MB/s Signed-off-by: Kyle Zhang --- db/db_iter.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/db/db_iter.cc b/db/db_iter.cc index 4d0f42e424..48ca4a552a 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -168,6 +168,15 @@ void DBIter::Next() { } else { // Store in saved_key_ the current key so we skip it below. SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + + // iter_ is pointing to current key. We can now safely move to the next to + // avoid checking current key. + iter_->Next(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } } FindNextUserEntry(true, &saved_key_); From 3724030179716fd8d95cf79339884c49afade8f9 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Mon, 29 Apr 2019 15:03:07 -0700 Subject: [PATCH 157/174] Update Travis CI configuration. The Travis configuration: 1) Installs recent versions of clang and GCC. 2) Sets up the environment so that CMake picks up the installed compilers. Previously, the pre-installed clang compiler was used instead. 3) Requests a modern macOS image that has all the headers needed by GCC. The CL also removes now-unnecessary old workarounds from the Travis configuration. PiperOrigin-RevId: 245831188 --- .travis.yml | 55 ++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e1ad6a744..436e03755c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,29 @@ -# Build matrix / environment variable are explained on: +# Build matrix / environment variables are explained on: # http://about.travis-ci.org/docs/user/build-configuration/ # This file can be validated on: http://lint.travis-ci.org/ -dist: xenial language: cpp +dist: xenial +osx_image: xcode10.2 compiler: - - gcc - - clang +- gcc +- clang os: - - linux - - osx +- linux +- osx env: - - BUILD_TYPE=Debug - - BUILD_TYPE=RelWithDebInfo - -matrix: - exclude: - # GCC fails on recent Travis OSX images. - # https://github.com/travis-ci/travis-ci/issues/9640 - - compiler: gcc - os: osx +- BUILD_TYPE=Debug +- BUILD_TYPE=RelWithDebInfo addons: apt: sources: - - llvm-toolchain-xenial-7 + - llvm-toolchain-xenial-8 - ubuntu-toolchain-r-test packages: - - clang-7 + - clang-8 - cmake - gcc-8 - g++-8 @@ -40,24 +34,33 @@ addons: - ninja-build homebrew: packages: + - cmake - crc32c + - gcc@8 - gperftools - kyotocabinet - - gcc@7 + - llvm@8 - ninja - snappy - sqlite3 - -before_install: -# The Travis VM image for Mac already has a link at /usr/local/include/c++, -# causing Homebrew's gcc installation to error out. This was reported to -# Homebrew maintainers at https://github.com/Homebrew/brew/issues/1742 and -# removing the link emerged as a workaround. -- if [ "$TRAVIS_OS_NAME" == "osx" ]; then rm -f /usr/local/include/c++ ; fi + update: true install: -# /usr/bin/gcc is stuck to old versions on both Linux and OSX. +# The following Homebrew packages aren't linked by default, and need to be +# prepended to the path explicitly. +- if [ "$TRAVIS_OS_NAME" == "osx" ]; then + export PATH="$(brew --prefix llvm)/bin:$PATH"; + fi +# /usr/bin/gcc points to an older compiler on both Linux and macOS. - if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi +# /usr/bin/clang points to an older compiler on both Linux and macOS. +# +# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values +# below don't work on macOS. Fortunately, the path change above makes the +# default values (clang and clang++) resolve to the correct compiler on macOS. +- if [ "$TRAVIS_OS_NAME" == "linux" ]; then + if [ "$CXX" = "clang++" ]; then export CXX="clang++-8" CC="clang-8"; fi; + fi - echo ${CC} - echo ${CXX} - ${CXX} --version From 297e66afc1dda3f3d7a7cc2022030164c302cb7a Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 2 May 2019 11:01:00 -0700 Subject: [PATCH 158/174] Format all files IAW the Google C++ Style Guide. Use clang-format to correct formatting to be in agreement with the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). Doing this simplifies the process of accepting changes. Also fixed a few warnings flagged by clang-tidy. PiperOrigin-RevId: 246350737 --- .clang-format | 18 ++ README.md | 8 + db/autocompact_test.cc | 25 +-- db/builder.cc | 13 +- db/builder.h | 8 +- db/c.cc | 325 ++++++++++++++------------------ db/corruption_test.cc | 32 ++-- db/db_bench.cc | 124 ++++++------ db/db_impl.cc | 179 ++++++++---------- db/db_impl.h | 11 +- db/db_iter.cc | 39 ++-- db/db_iter.h | 9 +- db/db_test.cc | 241 +++++++++++------------ db/dbformat.cc | 22 +-- db/dbformat.h | 33 ++-- db/dbformat_test.cc | 75 ++++---- db/dumpfile.cc | 10 +- db/fault_injection_test.cc | 43 ++--- db/filename.cc | 15 +- db/filename.h | 5 +- db/filename_test.cc | 74 ++++---- db/leveldbutil.cc | 11 +- db/log_reader.cc | 15 +- db/log_reader.h | 2 +- db/log_test.cc | 142 ++++++-------- db/log_writer.cc | 29 ++- db/log_writer.h | 3 +- db/memtable.cc | 39 ++-- db/memtable.h | 8 +- db/recovery_test.cc | 44 ++--- db/repair.cc | 47 ++--- db/skiplist.h | 48 ++--- db/skiplist_test.cc | 38 ++-- db/snapshot.h | 10 +- db/table_cache.cc | 25 +-- db/table_cache.h | 17 +- db/version_edit.cc | 34 ++-- db/version_edit.h | 25 ++- db/version_edit_test.cc | 6 +- db/version_set.cc | 200 +++++++++----------- db/version_set.h | 56 +++--- db/version_set_test.cc | 39 ++-- db/write_batch.cc | 19 +- db/write_batch_internal.h | 9 +- db/write_batch_test.cc | 48 ++--- doc/bench/db_bench_sqlite3.cc | 141 +++++++------- doc/bench/db_bench_tree_db.cc | 108 +++++------ helpers/memenv/memenv.cc | 53 ++---- helpers/memenv/memenv_test.cc | 35 ++-- include/leveldb/c.h | 42 ++--- include/leveldb/cache.h | 3 +- include/leveldb/comparator.h | 6 +- include/leveldb/db.h | 19 +- include/leveldb/dumpfile.h | 1 + include/leveldb/env.h | 18 +- include/leveldb/filter_policy.h | 5 +- include/leveldb/iterator.h | 5 +- include/leveldb/options.h | 3 +- include/leveldb/slice.h | 29 +-- include/leveldb/status.h | 3 +- include/leveldb/table.h | 15 +- include/leveldb/table_builder.h | 1 + include/leveldb/write_batch.h | 3 +- issues/issue178_test.cc | 12 +- issues/issue200_test.cc | 10 +- port/port.h | 4 +- port/port_stdcxx.h | 26 ++- port/thread_annotations.h | 13 +- table/block.cc | 28 ++- table/block.h | 5 +- table/block_builder.cc | 21 +-- table/block_builder.h | 19 +- table/filter_block.cc | 19 +- table/filter_block.h | 14 +- table/filter_block_test.cc | 34 ++-- table/format.cc | 11 +- table/format.h | 28 +-- table/iterator.cc | 24 ++- table/iterator_wrapper.h | 54 ++++-- table/merger.cc | 22 +-- table/merger.h | 4 +- table/table.cc | 35 ++-- table/table_builder.cc | 25 +-- table/table_test.cc | 214 +++++++++------------ table/two_level_iterator.cc | 41 ++-- table/two_level_iterator.h | 9 +- util/arena.cc | 7 +- util/arena_test.cc | 17 +- util/bloom.cc | 15 +- util/bloom_test.cc | 46 ++--- util/cache.cc | 48 +++-- util/cache_test.cc | 44 ++--- util/coding.cc | 66 ++++--- util/coding.h | 14 +- util/coding_test.cc | 32 ++-- util/comparator.cc | 15 +- util/crc32c.cc | 12 +- util/crc32c.h | 4 +- util/crc32c_test.cc | 29 +-- util/env.cc | 24 +-- util/env_posix.cc | 59 +++--- util/env_posix_test.cc | 6 +- util/env_test.cc | 16 +- util/env_windows.cc | 7 +- util/filter_policy.cc | 2 +- util/hash.cc | 9 +- util/hash_test.cc | 22 +-- util/histogram.cc | 207 ++++++++++++++++---- util/histogram.h | 4 +- util/logging.cc | 7 +- util/logging.h | 4 +- util/logging_test.cc | 6 +- util/mutexlock.h | 6 +- util/no_destructor.h | 11 +- util/no_destructor_test.cc | 6 +- util/options.cc | 5 +- util/posix_logger.h | 27 +-- util/random.h | 9 +- util/status.cc | 8 +- util/status_test.cc | 4 +- util/testharness.cc | 2 +- util/testharness.h | 62 +++--- util/testutil.cc | 12 +- util/testutil.h | 15 +- util/windows_logger.h | 25 +-- 125 files changed, 1970 insertions(+), 2329 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..75f34012ff --- /dev/null +++ b/.clang-format @@ -0,0 +1,18 @@ +# Run manually to reformat a file: +# clang-format -i --style=file +# find . -iname '*.cc' -o -iname '*.h' -o -iname '*.h.in' | xargs clang-format -i --style=file +BasedOnStyle: Google +DerivePointerAlignment: false + +# Public headers are in a different location in the internal Google repository. +# Order them so that when imported to the authoritative repository they will be +# in correct alphabetical order. +IncludeCategories: + - Regex: '^(<|"(db|helpers)/)' + Priority: 1 + - Regex: '^"(leveldb)/' + Priority: 2 + - Regex: '^(<|"(issues|port|table|third_party|util)/)' + Priority: 3 + - Regex: '.*' + Priority: 4 diff --git a/README.md b/README.md index 537cab16e4..0b660aeea7 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,14 @@ Contribution requirements: 3. **Tests**: All changes must be accompanied by a new (or changed) test, or a sufficient explanation as to why a new (or changed) test is not required. +4. **Consistent Style**: This project conforms to the + [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). + To ensure your changes are properly formatted please run: + + ``` + clang-format -i --style=file + ``` + ## Submitting a Pull Request Before any pull request will be accepted the author must first sign a diff --git a/db/autocompact_test.cc b/db/autocompact_test.cc index d20a2362c3..00e3672801 100644 --- a/db/autocompact_test.cc +++ b/db/autocompact_test.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "leveldb/db.h" #include "db/db_impl.h" #include "leveldb/cache.h" +#include "leveldb/db.h" #include "util/testharness.h" #include "util/testutil.h" @@ -81,17 +81,16 @@ void AutoCompactTest::DoReads(int n) { ASSERT_LT(read, 100) << "Taking too long to compact"; Iterator* iter = db_->NewIterator(ReadOptions()); for (iter->SeekToFirst(); - iter->Valid() && iter->key().ToString() < limit_key; - iter->Next()) { + iter->Valid() && iter->key().ToString() < limit_key; iter->Next()) { // Drop data } delete iter; // Wait a little bit to allow any triggered compactions to complete. Env::Default()->SleepForMicroseconds(1000000); uint64_t size = Size(Key(0), Key(n)); - fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", - read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); - if (size <= initial_size/10) { + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", read + 1, + size / 1048576.0, Size(Key(n), Key(kCount)) / 1048576.0); + if (size <= initial_size / 10) { break; } } @@ -100,19 +99,13 @@ void AutoCompactTest::DoReads(int n) { // is pretty much unchanged. const int64_t final_other_size = Size(Key(n), Key(kCount)); ASSERT_LE(final_other_size, initial_other_size + 1048576); - ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); + ASSERT_GE(final_other_size, initial_other_size / 5 - 1048576); } -TEST(AutoCompactTest, ReadAll) { - DoReads(kCount); -} +TEST(AutoCompactTest, ReadAll) { DoReads(kCount); } -TEST(AutoCompactTest, ReadHalf) { - DoReads(kCount/2); -} +TEST(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/builder.cc b/db/builder.cc index 5fa405d31d..9520ee4535 100644 --- a/db/builder.cc +++ b/db/builder.cc @@ -4,8 +4,8 @@ #include "db/builder.h" -#include "db/filename.h" #include "db/dbformat.h" +#include "db/filename.h" #include "db/table_cache.h" #include "db/version_edit.h" #include "leveldb/db.h" @@ -14,12 +14,8 @@ namespace leveldb { -Status BuildTable(const std::string& dbname, - Env* env, - const Options& options, - TableCache* table_cache, - Iterator* iter, - FileMetaData* meta) { +Status BuildTable(const std::string& dbname, Env* env, const Options& options, + TableCache* table_cache, Iterator* iter, FileMetaData* meta) { Status s; meta->file_size = 0; iter->SeekToFirst(); @@ -60,8 +56,7 @@ Status BuildTable(const std::string& dbname, if (s.ok()) { // Verify that the table is usable - Iterator* it = table_cache->NewIterator(ReadOptions(), - meta->number, + Iterator* it = table_cache->NewIterator(ReadOptions(), meta->number, meta->file_size); s = it->status(); delete it; diff --git a/db/builder.h b/db/builder.h index 02897309a2..7bd0b8049b 100644 --- a/db/builder.h +++ b/db/builder.h @@ -22,12 +22,8 @@ class VersionEdit; // *meta will be filled with metadata about the generated table. // If no data is present in *iter, meta->file_size will be set to // zero, and no Table file will be produced. -Status BuildTable(const std::string& dbname, - Env* env, - const Options& options, - TableCache* table_cache, - Iterator* iter, - FileMetaData* meta); +Status BuildTable(const std::string& dbname, Env* env, const Options& options, + TableCache* table_cache, Iterator* iter, FileMetaData* meta); } // namespace leveldb diff --git a/db/c.cc b/db/c.cc index 7756ea3f62..72f6daabb1 100644 --- a/db/c.cc +++ b/db/c.cc @@ -5,6 +5,7 @@ #include "leveldb/c.h" #include + #include "leveldb/cache.h" #include "leveldb/comparator.h" #include "leveldb/db.h" @@ -42,67 +43,79 @@ using leveldb::WriteOptions; extern "C" { -struct leveldb_t { DB* rep; }; -struct leveldb_iterator_t { Iterator* rep; }; -struct leveldb_writebatch_t { WriteBatch rep; }; -struct leveldb_snapshot_t { const Snapshot* rep; }; -struct leveldb_readoptions_t { ReadOptions rep; }; -struct leveldb_writeoptions_t { WriteOptions rep; }; -struct leveldb_options_t { Options rep; }; -struct leveldb_cache_t { Cache* rep; }; -struct leveldb_seqfile_t { SequentialFile* rep; }; -struct leveldb_randomfile_t { RandomAccessFile* rep; }; -struct leveldb_writablefile_t { WritableFile* rep; }; -struct leveldb_logger_t { Logger* rep; }; -struct leveldb_filelock_t { FileLock* rep; }; +struct leveldb_t { + DB* rep; +}; +struct leveldb_iterator_t { + Iterator* rep; +}; +struct leveldb_writebatch_t { + WriteBatch rep; +}; +struct leveldb_snapshot_t { + const Snapshot* rep; +}; +struct leveldb_readoptions_t { + ReadOptions rep; +}; +struct leveldb_writeoptions_t { + WriteOptions rep; +}; +struct leveldb_options_t { + Options rep; +}; +struct leveldb_cache_t { + Cache* rep; +}; +struct leveldb_seqfile_t { + SequentialFile* rep; +}; +struct leveldb_randomfile_t { + RandomAccessFile* rep; +}; +struct leveldb_writablefile_t { + WritableFile* rep; +}; +struct leveldb_logger_t { + Logger* rep; +}; +struct leveldb_filelock_t { + FileLock* rep; +}; struct leveldb_comparator_t : public Comparator { void* state_; void (*destructor_)(void*); - int (*compare_)( - void*, - const char* a, size_t alen, - const char* b, size_t blen); + int (*compare_)(void*, const char* a, size_t alen, const char* b, + size_t blen); const char* (*name_)(void*); - virtual ~leveldb_comparator_t() { - (*destructor_)(state_); - } + virtual ~leveldb_comparator_t() { (*destructor_)(state_); } virtual int Compare(const Slice& a, const Slice& b) const { return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); } - virtual const char* Name() const { - return (*name_)(state_); - } + virtual const char* Name() const { return (*name_)(state_); } // No-ops since the C binding does not support key shortening methods. - virtual void FindShortestSeparator(std::string*, const Slice&) const { } - virtual void FindShortSuccessor(std::string* key) const { } + virtual void FindShortestSeparator(std::string*, const Slice&) const {} + virtual void FindShortSuccessor(std::string* key) const {} }; struct leveldb_filterpolicy_t : public FilterPolicy { void* state_; void (*destructor_)(void*); const char* (*name_)(void*); - char* (*create_)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length); - unsigned char (*key_match_)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length); - - virtual ~leveldb_filterpolicy_t() { - (*destructor_)(state_); - } + char* (*create_)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length); + unsigned char (*key_match_)(void*, const char* key, size_t length, + const char* filter, size_t filter_length); - virtual const char* Name() const { - return (*name_)(state_); - } + virtual ~leveldb_filterpolicy_t() { (*destructor_)(state_); } + + virtual const char* Name() const { return (*name_)(state_); } virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { std::vector key_pointers(n); @@ -118,8 +131,8 @@ struct leveldb_filterpolicy_t : public FilterPolicy { } virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { - return (*key_match_)(state_, key.data(), key.size(), - filter.data(), filter.size()); + return (*key_match_)(state_, key.data(), key.size(), filter.data(), + filter.size()); } }; @@ -148,10 +161,8 @@ static char* CopyString(const std::string& str) { return result; } -leveldb_t* leveldb_open( - const leveldb_options_t* options, - const char* name, - char** errptr) { +leveldb_t* leveldb_open(const leveldb_options_t* options, const char* name, + char** errptr) { DB* db; if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { return nullptr; @@ -166,39 +177,26 @@ void leveldb_close(leveldb_t* db) { delete db; } -void leveldb_put( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - const char* val, size_t vallen, - char** errptr) { +void leveldb_put(leveldb_t* db, const leveldb_writeoptions_t* options, + const char* key, size_t keylen, const char* val, size_t vallen, + char** errptr) { SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); } -void leveldb_delete( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - char** errptr) { +void leveldb_delete(leveldb_t* db, const leveldb_writeoptions_t* options, + const char* key, size_t keylen, char** errptr) { SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); } - -void leveldb_write( - leveldb_t* db, - const leveldb_writeoptions_t* options, - leveldb_writebatch_t* batch, - char** errptr) { +void leveldb_write(leveldb_t* db, const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, char** errptr) { SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); } -char* leveldb_get( - leveldb_t* db, - const leveldb_readoptions_t* options, - const char* key, size_t keylen, - size_t* vallen, - char** errptr) { +char* leveldb_get(leveldb_t* db, const leveldb_readoptions_t* options, + const char* key, size_t keylen, size_t* vallen, + char** errptr) { char* result = nullptr; std::string tmp; Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); @@ -215,30 +213,25 @@ char* leveldb_get( } leveldb_iterator_t* leveldb_create_iterator( - leveldb_t* db, - const leveldb_readoptions_t* options) { + leveldb_t* db, const leveldb_readoptions_t* options) { leveldb_iterator_t* result = new leveldb_iterator_t; result->rep = db->rep->NewIterator(options->rep); return result; } -const leveldb_snapshot_t* leveldb_create_snapshot( - leveldb_t* db) { +const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db) { leveldb_snapshot_t* result = new leveldb_snapshot_t; result->rep = db->rep->GetSnapshot(); return result; } -void leveldb_release_snapshot( - leveldb_t* db, - const leveldb_snapshot_t* snapshot) { +void leveldb_release_snapshot(leveldb_t* db, + const leveldb_snapshot_t* snapshot) { db->rep->ReleaseSnapshot(snapshot->rep); delete snapshot; } -char* leveldb_property_value( - leveldb_t* db, - const char* propname) { +char* leveldb_property_value(leveldb_t* db, const char* propname) { std::string tmp; if (db->rep->GetProperty(Slice(propname), &tmp)) { // We use strdup() since we expect human readable output. @@ -248,12 +241,12 @@ char* leveldb_property_value( } } -void leveldb_approximate_sizes( - leveldb_t* db, - int num_ranges, - const char* const* range_start_key, const size_t* range_start_key_len, - const char* const* range_limit_key, const size_t* range_limit_key_len, - uint64_t* sizes) { +void leveldb_approximate_sizes(leveldb_t* db, int num_ranges, + const char* const* range_start_key, + const size_t* range_start_key_len, + const char* const* range_limit_key, + const size_t* range_limit_key_len, + uint64_t* sizes) { Range* ranges = new Range[num_ranges]; for (int i = 0; i < num_ranges; i++) { ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); @@ -263,10 +256,9 @@ void leveldb_approximate_sizes( delete[] ranges; } -void leveldb_compact_range( - leveldb_t* db, - const char* start_key, size_t start_key_len, - const char* limit_key, size_t limit_key_len) { +void leveldb_compact_range(leveldb_t* db, const char* start_key, + size_t start_key_len, const char* limit_key, + size_t limit_key_len) { Slice a, b; db->rep->CompactRange( // Pass null Slice if corresponding "const char*" is null @@ -274,17 +266,13 @@ void leveldb_compact_range( (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr)); } -void leveldb_destroy_db( - const leveldb_options_t* options, - const char* name, - char** errptr) { +void leveldb_destroy_db(const leveldb_options_t* options, const char* name, + char** errptr) { SaveError(errptr, DestroyDB(name, options->rep)); } -void leveldb_repair_db( - const leveldb_options_t* options, - const char* name, - char** errptr) { +void leveldb_repair_db(const leveldb_options_t* options, const char* name, + char** errptr) { SaveError(errptr, RepairDB(name, options->rep)); } @@ -309,13 +297,9 @@ void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { iter->rep->Seek(Slice(k, klen)); } -void leveldb_iter_next(leveldb_iterator_t* iter) { - iter->rep->Next(); -} +void leveldb_iter_next(leveldb_iterator_t* iter) { iter->rep->Next(); } -void leveldb_iter_prev(leveldb_iterator_t* iter) { - iter->rep->Prev(); -} +void leveldb_iter_prev(leveldb_iterator_t* iter) { iter->rep->Prev(); } const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { Slice s = iter->rep->key(); @@ -337,32 +321,25 @@ leveldb_writebatch_t* leveldb_writebatch_create() { return new leveldb_writebatch_t; } -void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { - delete b; -} +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { delete b; } -void leveldb_writebatch_clear(leveldb_writebatch_t* b) { - b->rep.Clear(); -} +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { b->rep.Clear(); } -void leveldb_writebatch_put( - leveldb_writebatch_t* b, - const char* key, size_t klen, - const char* val, size_t vlen) { +void leveldb_writebatch_put(leveldb_writebatch_t* b, const char* key, + size_t klen, const char* val, size_t vlen) { b->rep.Put(Slice(key, klen), Slice(val, vlen)); } -void leveldb_writebatch_delete( - leveldb_writebatch_t* b, - const char* key, size_t klen) { +void leveldb_writebatch_delete(leveldb_writebatch_t* b, const char* key, + size_t klen) { b->rep.Delete(Slice(key, klen)); } -void leveldb_writebatch_iterate( - const leveldb_writebatch_t* b, - void* state, - void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), - void (*deleted)(void*, const char* k, size_t klen)) { +void leveldb_writebatch_iterate(const leveldb_writebatch_t* b, void* state, + void (*put)(void*, const char* k, size_t klen, + const char* v, size_t vlen), + void (*deleted)(void*, const char* k, + size_t klen)) { class H : public WriteBatch::Handler { public: void* state_; @@ -382,43 +359,37 @@ void leveldb_writebatch_iterate( b->rep.Iterate(&handler); } -void leveldb_writebatch_append(leveldb_writebatch_t *destination, - const leveldb_writebatch_t *source) { +void leveldb_writebatch_append(leveldb_writebatch_t* destination, + const leveldb_writebatch_t* source) { destination->rep.Append(source->rep); } -leveldb_options_t* leveldb_options_create() { - return new leveldb_options_t; -} +leveldb_options_t* leveldb_options_create() { return new leveldb_options_t; } -void leveldb_options_destroy(leveldb_options_t* options) { - delete options; -} +void leveldb_options_destroy(leveldb_options_t* options) { delete options; } -void leveldb_options_set_comparator( - leveldb_options_t* opt, - leveldb_comparator_t* cmp) { +void leveldb_options_set_comparator(leveldb_options_t* opt, + leveldb_comparator_t* cmp) { opt->rep.comparator = cmp; } -void leveldb_options_set_filter_policy( - leveldb_options_t* opt, - leveldb_filterpolicy_t* policy) { +void leveldb_options_set_filter_policy(leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { opt->rep.filter_policy = policy; } -void leveldb_options_set_create_if_missing( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_create_if_missing(leveldb_options_t* opt, + unsigned char v) { opt->rep.create_if_missing = v; } -void leveldb_options_set_error_if_exists( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_error_if_exists(leveldb_options_t* opt, + unsigned char v) { opt->rep.error_if_exists = v; } -void leveldb_options_set_paranoid_checks( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_paranoid_checks(leveldb_options_t* opt, + unsigned char v) { opt->rep.paranoid_checks = v; } @@ -459,12 +430,9 @@ void leveldb_options_set_compression(leveldb_options_t* opt, int t) { } leveldb_comparator_t* leveldb_comparator_create( - void* state, - void (*destructor)(void*), - int (*compare)( - void*, - const char* a, size_t alen, - const char* b, size_t blen), + void* state, void (*destructor)(void*), + int (*compare)(void*, const char* a, size_t alen, const char* b, + size_t blen), const char* (*name)(void*)) { leveldb_comparator_t* result = new leveldb_comparator_t; result->state_ = state; @@ -474,22 +442,15 @@ leveldb_comparator_t* leveldb_comparator_create( return result; } -void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { - delete cmp; -} +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { delete cmp; } leveldb_filterpolicy_t* leveldb_filterpolicy_create( - void* state, - void (*destructor)(void*), - char* (*create_filter)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length), - unsigned char (*key_may_match)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length), + void* state, void (*destructor)(void*), + char* (*create_filter)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length), + unsigned char (*key_may_match)(void*, const char* key, size_t length, + const char* filter, size_t filter_length), const char* (*name)(void*)) { leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; result->state_ = state; @@ -518,7 +479,7 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { bool KeyMayMatch(const Slice& key, const Slice& filter) const { return rep_->KeyMayMatch(key, filter); } - static void DoNothing(void*) { } + static void DoNothing(void*) {} }; Wrapper* wrapper = new Wrapper; wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); @@ -531,24 +492,20 @@ leveldb_readoptions_t* leveldb_readoptions_create() { return new leveldb_readoptions_t; } -void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { - delete opt; -} +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { delete opt; } -void leveldb_readoptions_set_verify_checksums( - leveldb_readoptions_t* opt, - unsigned char v) { +void leveldb_readoptions_set_verify_checksums(leveldb_readoptions_t* opt, + unsigned char v) { opt->rep.verify_checksums = v; } -void leveldb_readoptions_set_fill_cache( - leveldb_readoptions_t* opt, unsigned char v) { +void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t* opt, + unsigned char v) { opt->rep.fill_cache = v; } -void leveldb_readoptions_set_snapshot( - leveldb_readoptions_t* opt, - const leveldb_snapshot_t* snap) { +void leveldb_readoptions_set_snapshot(leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { opt->rep.snapshot = (snap ? snap->rep : nullptr); } @@ -556,12 +513,10 @@ leveldb_writeoptions_t* leveldb_writeoptions_create() { return new leveldb_writeoptions_t; } -void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { - delete opt; -} +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { delete opt; } -void leveldb_writeoptions_set_sync( - leveldb_writeoptions_t* opt, unsigned char v) { +void leveldb_writeoptions_set_sync(leveldb_writeoptions_t* opt, + unsigned char v) { opt->rep.sync = v; } @@ -600,16 +555,10 @@ char* leveldb_env_get_test_directory(leveldb_env_t* env) { return buffer; } -void leveldb_free(void* ptr) { - free(ptr); -} +void leveldb_free(void* ptr) { free(ptr); } -int leveldb_major_version() { - return kMajorVersion; -} +int leveldb_major_version() { return kMajorVersion; } -int leveldb_minor_version() { - return kMinorVersion; -} +int leveldb_minor_version() { return kMinorVersion; } } // end extern "C" diff --git a/db/corruption_test.cc b/db/corruption_test.cc index d50785a9f1..e6f64ee89e 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "leveldb/db.h" - #include -#include "leveldb/cache.h" -#include "leveldb/table.h" -#include "leveldb/write_batch.h" + #include "db/db_impl.h" #include "db/filename.h" #include "db/log_format.h" #include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" #include "util/logging.h" #include "util/testharness.h" #include "util/testutil.h" @@ -42,8 +42,8 @@ class CorruptionTest { } ~CorruptionTest() { - delete db_; - delete tiny_cache_; + delete db_; + delete tiny_cache_; } Status TryReopen() { @@ -52,9 +52,7 @@ class CorruptionTest { return DB::Open(options_, dbname_, &db_); } - void Reopen() { - ASSERT_OK(TryReopen()); - } + void Reopen() { ASSERT_OK(TryReopen()); } void RepairDB() { delete db_; @@ -66,7 +64,7 @@ class CorruptionTest { std::string key_space, value_space; WriteBatch batch; for (int i = 0; i < n; i++) { - //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + // if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); Slice key = Key(i, &key_space); batch.Clear(); batch.Put(key, Value(i, &value_space)); @@ -95,8 +93,7 @@ class CorruptionTest { // Ignore boundary keys. continue; } - if (!ConsumeDecimalNumber(&in, &key) || - !in.empty() || + if (!ConsumeDecimalNumber(&in, &key) || !in.empty() || key < next_expected) { bad_keys++; continue; @@ -127,8 +124,7 @@ class CorruptionTest { std::string fname; int picked_number = -1; for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type) && - type == filetype && + if (ParseFileName(filenames[i], &number, &type) && type == filetype && int(number) > picked_number) { // Pick latest file fname = dbname_ + "/" + filenames[i]; picked_number = number; @@ -194,7 +190,7 @@ class CorruptionTest { TEST(CorruptionTest, Recovery) { Build(100); Check(100, 100); - Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block Reopen(); @@ -361,6 +357,4 @@ TEST(CorruptionTest, UnrelatedKeys) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/db_bench.cc b/db/db_bench.cc index 41e903b7a5..3090b43b9e 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include #include #include +#include + #include "leveldb/cache.h" #include "leveldb/db.h" #include "leveldb/env.h" @@ -55,8 +56,7 @@ static const char* FLAGS_benchmarks = "fill100K," "crc32c," "snappycomp," - "snappyuncomp," - ; + "snappyuncomp,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -155,7 +155,7 @@ static Slice TrimSpace(Slice s) { start++; } size_t limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); @@ -214,9 +214,7 @@ class Stats { seconds_ = (finish_ - start_) * 1e-6; } - void AddMessage(Slice msg) { - AppendWithSpace(&message_, msg); - } + void AddMessage(Slice msg) { AppendWithSpace(&message_, msg); } void FinishedSingleOp() { if (FLAGS_histogram) { @@ -232,21 +230,26 @@ class Stats { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } } - void AddBytes(int64_t n) { - bytes_ += n; - } + void AddBytes(int64_t n) { bytes_ += n; } void Report(const Slice& name) { // Pretend at least one op was done in case we are running a benchmark @@ -265,11 +268,8 @@ class Stats { } AppendWithSpace(&extra, message_); - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - seconds_ * 1e6 / done_, - (extra.empty() ? "" : " "), - extra.c_str()); + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + seconds_ * 1e6 / done_, (extra.empty() ? "" : " "), extra.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); } @@ -294,13 +294,13 @@ struct SharedState { bool start GUARDED_BY(mu); SharedState(int total) - : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) { } + : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) {} }; // Per-thread state for concurrent executions of the same benchmark. struct ThreadState { - int tid; // 0..n-1 when running in n threads - Random rand; // Has different seeds for different threads + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads Stats stats; SharedState* shared; @@ -330,20 +330,20 @@ class Benchmark { static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); fprintf(stdout, "FileSize: %.1f MB (estimated)\n", - (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) - / 1048576.0)); + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -361,8 +361,8 @@ class Benchmark { } void PrintEnvironment() { - fprintf(stderr, "LevelDB: version %d.%d\n", - kMajorVersion, kMinorVersion); + fprintf(stderr, "LevelDB: version %d.%d\n", kMajorVersion, + kMinorVersion); #if defined(__linux) time_t now = time(nullptr); @@ -397,16 +397,16 @@ class Benchmark { public: Benchmark() - : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr), - filter_policy_(FLAGS_bloom_bits >= 0 - ? NewBloomFilterPolicy(FLAGS_bloom_bits) - : nullptr), - db_(nullptr), - num_(FLAGS_num), - value_size_(FLAGS_value_size), - entries_per_batch_(1), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - heap_counter_(0) { + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : nullptr), + db_(nullptr), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { std::vector files; g_env->GetChildren(FLAGS_db, &files); for (size_t i = 0; i < files.size(); i++) { @@ -516,7 +516,7 @@ class Benchmark { } else if (name == Slice("sstables")) { PrintStats("leveldb.sstables"); } else { - if (name != Slice()) { // No error message for empty name + if (!name.empty()) { // No error message for empty name fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); } } @@ -665,8 +665,8 @@ class Benchmark { int64_t bytes = 0; char* uncompressed = new char[input.size()]; while (ok && bytes < 1024 * 1048576) { // Compress 1G - ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), - uncompressed); + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); bytes += input.size(); thread->stats.FinishedSingleOp(); } @@ -706,13 +706,9 @@ class Benchmark { } } - void WriteSeq(ThreadState* thread) { - DoWrite(thread, true); - } + void WriteSeq(ThreadState* thread) { DoWrite(thread, true); } - void WriteRandom(ThreadState* thread) { - DoWrite(thread, false); - } + void WriteRandom(ThreadState* thread) { DoWrite(thread, false); } void DoWrite(ThreadState* thread, bool seq) { if (num_ != FLAGS_num) { @@ -728,7 +724,7 @@ class Benchmark { for (int i = 0; i < num_; i += entries_per_batch_) { batch.Clear(); for (int j = 0; j < entries_per_batch_; j++) { - const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num); char key[100]; snprintf(key, sizeof(key), "%016d", k); batch.Put(key, gen.Generate(value_size_)); @@ -838,7 +834,7 @@ class Benchmark { for (int i = 0; i < num_; i += entries_per_batch_) { batch.Clear(); for (int j = 0; j < entries_per_batch_; j++) { - const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num); char key[100]; snprintf(key, sizeof(key), "%016d", k); batch.Delete(key); @@ -852,13 +848,9 @@ class Benchmark { } } - void DeleteSeq(ThreadState* thread) { - DoDelete(thread, true); - } + void DeleteSeq(ThreadState* thread) { DoDelete(thread, true); } - void DeleteRandom(ThreadState* thread) { - DoDelete(thread, false); - } + void DeleteRandom(ThreadState* thread) { DoDelete(thread, false); } void ReadWhileWriting(ThreadState* thread) { if (thread->tid > 0) { @@ -890,9 +882,7 @@ class Benchmark { } } - void Compact(ThreadState* thread) { - db_->CompactRange(nullptr, nullptr); - } + void Compact(ThreadState* thread) { db_->CompactRange(nullptr, nullptr); } void PrintStats(const char* key) { std::string stats; @@ -982,9 +972,9 @@ int main(int argc, char** argv) { // Choose a location for the test database if none given with --db= if (FLAGS_db == nullptr) { - leveldb::g_env->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + leveldb::g_env->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/db/db_impl.cc b/db/db_impl.cc index caef2b1bde..bff2d62148 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -75,7 +75,7 @@ struct DBImpl::CompactionState { uint64_t total_bytes; - Output* current_output() { return &outputs[outputs.size()-1]; } + Output* current_output() { return &outputs[outputs.size() - 1]; } explicit CompactionState(Compaction* c) : compaction(c), @@ -98,10 +98,10 @@ Options SanitizeOptions(const std::string& dbname, Options result = src; result.comparator = icmp; result.filter_policy = (src.filter_policy != nullptr) ? ipolicy : nullptr; - ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); - ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); - ClipToRange(&result.max_file_size, 1<<20, 1<<30); - ClipToRange(&result.block_size, 1<<10, 4<<20); + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64 << 10, 1 << 30); + ClipToRange(&result.max_file_size, 1 << 20, 1 << 30); + ClipToRange(&result.block_size, 1 << 10, 4 << 20); if (result.info_log == nullptr) { // Open a log file in the same directory as the db src.env->CreateDir(dbname); // In case it does not exist @@ -268,8 +268,7 @@ void DBImpl::DeleteObsoleteFiles() { if (type == kTableFile) { table_cache_->Evict(number); } - Log(options_.info_log, "Delete type=%d #%lld\n", - static_cast(type), + Log(options_.info_log, "Delete type=%d #%lld\n", static_cast(type), static_cast(number)); env_->DeleteFile(dbname_ + "/" + filenames[i]); } @@ -277,7 +276,7 @@ void DBImpl::DeleteObsoleteFiles() { } } -Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { +Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) { mutex_.AssertHeld(); // Ignore error from CreateDir since the creation of the DB is @@ -302,8 +301,8 @@ Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { } } else { if (options_.error_if_exists) { - return Status::InvalidArgument( - dbname_, "exists (error_if_exists is true)"); + return Status::InvalidArgument(dbname_, + "exists (error_if_exists is true)"); } } @@ -378,8 +377,8 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, Status* status; // null if options_.paranoid_checks==false virtual void Corruption(size_t bytes, const Status& s) { Log(info_log, "%s%s: dropping %d bytes; %s", - (this->status == nullptr ? "(ignoring error) " : ""), - fname, static_cast(bytes), s.ToString().c_str()); + (this->status == nullptr ? "(ignoring error) " : ""), fname, + static_cast(bytes), s.ToString().c_str()); if (this->status != nullptr && this->status->ok()) *this->status = s; } }; @@ -405,10 +404,9 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, // paranoid_checks==false so that corruptions cause entire commits // to be skipped instead of propagating bad information (like overly // large sequence numbers). - log::Reader reader(file, &reporter, true/*checksum*/, - 0/*initial_offset*/); + log::Reader reader(file, &reporter, true /*checksum*/, 0 /*initial_offset*/); Log(options_.info_log, "Recovering log #%llu", - (unsigned long long) log_number); + (unsigned long long)log_number); // Read all the records and add to a memtable std::string scratch; @@ -416,11 +414,10 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, WriteBatch batch; int compactions = 0; MemTable* mem = nullptr; - while (reader.ReadRecord(&record, &scratch) && - status.ok()) { + while (reader.ReadRecord(&record, &scratch) && status.ok()) { if (record.size() < 12) { - reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + reporter.Corruption(record.size(), + Status::Corruption("log record too small")); continue; } WriteBatchInternal::SetContents(&batch, record); @@ -434,9 +431,8 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, if (!status.ok()) { break; } - const SequenceNumber last_seq = - WriteBatchInternal::Sequence(&batch) + - WriteBatchInternal::Count(&batch) - 1; + const SequenceNumber last_seq = WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; if (last_seq > *max_sequence) { *max_sequence = last_seq; } @@ -500,7 +496,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, pending_outputs_.insert(meta.number); Iterator* iter = mem->NewIterator(); Log(options_.info_log, "Level-0 table #%llu: started", - (unsigned long long) meta.number); + (unsigned long long)meta.number); Status s; { @@ -510,13 +506,11 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, } Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", - (unsigned long long) meta.number, - (unsigned long long) meta.file_size, + (unsigned long long)meta.number, (unsigned long long)meta.file_size, s.ToString().c_str()); delete iter; pending_outputs_.erase(meta.number); - // Note that if file_size is zero, the file has been deleted and // should not be added to the manifest. int level = 0; @@ -526,8 +520,8 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, if (base != nullptr) { level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); } - edit->AddFile(level, meta.number, meta.file_size, - meta.smallest, meta.largest); + edit->AddFile(level, meta.number, meta.file_size, meta.smallest, + meta.largest); } CompactionStats stats; @@ -658,8 +652,7 @@ void DBImpl::MaybeScheduleCompaction() { // DB is being deleted; no more background compactions } else if (!bg_error_.ok()) { // Already got an error; no more changes - } else if (imm_ == nullptr && - manual_compaction_ == nullptr && + } else if (imm_ == nullptr && manual_compaction_ == nullptr && !versions_->NeedsCompaction()) { // No work to be done } else { @@ -711,8 +704,7 @@ void DBImpl::BackgroundCompaction() { } Log(options_.info_log, "Manual compaction at level-%d from %s .. %s; will stop at %s\n", - m->level, - (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + m->level, (m->begin ? m->begin->DebugString().c_str() : "(begin)"), (m->end ? m->end->DebugString().c_str() : "(end)"), (m->done ? "(end)" : manual_end.DebugString().c_str())); } else { @@ -727,19 +719,17 @@ void DBImpl::BackgroundCompaction() { assert(c->num_input_files(0) == 1); FileMetaData* f = c->input(0, 0); c->edit()->DeleteFile(c->level(), f->number); - c->edit()->AddFile(c->level() + 1, f->number, f->file_size, - f->smallest, f->largest); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, f->smallest, + f->largest); status = versions_->LogAndApply(c->edit(), &mutex_); if (!status.ok()) { RecordBackgroundError(status); } VersionSet::LevelSummaryStorage tmp; Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", - static_cast(f->number), - c->level() + 1, + static_cast(f->number), c->level() + 1, static_cast(f->file_size), - status.ToString().c_str(), - versions_->LevelSummary(&tmp)); + status.ToString().c_str(), versions_->LevelSummary(&tmp)); } else { CompactionState* compact = new CompactionState(c); status = DoCompactionWork(compact); @@ -757,8 +747,7 @@ void DBImpl::BackgroundCompaction() { } else if (shutting_down_.load(std::memory_order_acquire)) { // Ignore compaction errors found during shutting down } else { - Log(options_.info_log, - "Compaction error: %s", status.ToString().c_str()); + Log(options_.info_log, "Compaction error: %s", status.ToString().c_str()); } if (is_manual) { @@ -853,31 +842,25 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, if (s.ok() && current_entries > 0) { // Verify that the table is usable - Iterator* iter = table_cache_->NewIterator(ReadOptions(), - output_number, - current_bytes); + Iterator* iter = + table_cache_->NewIterator(ReadOptions(), output_number, current_bytes); s = iter->status(); delete iter; if (s.ok()) { - Log(options_.info_log, - "Generated table #%llu@%d: %lld keys, %lld bytes", - (unsigned long long) output_number, - compact->compaction->level(), - (unsigned long long) current_entries, - (unsigned long long) current_bytes); + Log(options_.info_log, "Generated table #%llu@%d: %lld keys, %lld bytes", + (unsigned long long)output_number, compact->compaction->level(), + (unsigned long long)current_entries, + (unsigned long long)current_bytes); } } return s; } - Status DBImpl::InstallCompactionResults(CompactionState* compact) { mutex_.AssertHeld(); - Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", - compact->compaction->num_input_files(0), - compact->compaction->level(), - compact->compaction->num_input_files(1), - compact->compaction->level() + 1, + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), compact->compaction->level(), + compact->compaction->num_input_files(1), compact->compaction->level() + 1, static_cast(compact->total_bytes)); // Add compaction outputs @@ -885,9 +868,8 @@ Status DBImpl::InstallCompactionResults(CompactionState* compact) { const int level = compact->compaction->level(); for (size_t i = 0; i < compact->outputs.size(); i++) { const CompactionState::Output& out = compact->outputs[i]; - compact->compaction->edit()->AddFile( - level + 1, - out.number, out.file_size, out.smallest, out.largest); + compact->compaction->edit()->AddFile(level + 1, out.number, out.file_size, + out.smallest, out.largest); } return versions_->LogAndApply(compact->compaction->edit(), &mutex_); } @@ -896,9 +878,8 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { const uint64_t start_micros = env_->NowMicros(); int64_t imm_micros = 0; // Micros spent doing imm_ compactions - Log(options_.info_log, "Compacting %d@%d + %d@%d files", - compact->compaction->num_input_files(0), - compact->compaction->level(), + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), compact->compaction->level(), compact->compaction->num_input_files(1), compact->compaction->level() + 1); @@ -921,7 +902,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { std::string current_user_key; bool has_current_user_key = false; SequenceNumber last_sequence_for_key = kMaxSequenceNumber; - for (; input->Valid() && !shutting_down_.load(std::memory_order_acquire); ) { + for (; input->Valid() && !shutting_down_.load(std::memory_order_acquire);) { // Prioritize immutable compaction work if (has_imm_.load(std::memory_order_relaxed)) { const uint64_t imm_start = env_->NowMicros(); @@ -953,8 +934,8 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { last_sequence_for_key = kMaxSequenceNumber; } else { if (!has_current_user_key || - user_comparator()->Compare(ikey.user_key, - Slice(current_user_key)) != 0) { + user_comparator()->Compare(ikey.user_key, Slice(current_user_key)) != + 0) { // First occurrence of this user key current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); has_current_user_key = true; @@ -963,7 +944,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (last_sequence_for_key <= compact->smallest_snapshot) { // Hidden by an newer entry for same user key - drop = true; // (A) + drop = true; // (A) } else if (ikey.type == kTypeDeletion && ikey.sequence <= compact->smallest_snapshot && compact->compaction->IsBaseLevelForKey(ikey.user_key)) { @@ -1049,8 +1030,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { RecordBackgroundError(status); } VersionSet::LevelSummaryStorage tmp; - Log(options_.info_log, - "compacted to: %s", versions_->LevelSummary(&tmp)); + Log(options_.info_log, "compacted to: %s", versions_->LevelSummary(&tmp)); return status; } @@ -1063,7 +1043,7 @@ struct IterState { MemTable* const imm GUARDED_BY(mu); IterState(port::Mutex* mutex, MemTable* mem, MemTable* imm, Version* version) - : mu(mutex), version(version), mem(mem), imm(imm) { } + : mu(mutex), version(version), mem(mem), imm(imm) {} }; static void CleanupIteratorState(void* arg1, void* arg2) { @@ -1116,8 +1096,7 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { return versions_->MaxNextLevelOverlappingBytes(); } -Status DBImpl::Get(const ReadOptions& options, - const Slice& key, +Status DBImpl::Get(const ReadOptions& options, const Slice& key, std::string* value) { Status s; MutexLock l(&mutex_); @@ -1168,12 +1147,12 @@ Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; uint32_t seed; Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); - return NewDBIterator( - this, user_comparator(), iter, - (options.snapshot != nullptr - ? static_cast(options.snapshot)->sequence_number() - : latest_snapshot), - seed); + return NewDBIterator(this, user_comparator(), iter, + (options.snapshot != nullptr + ? static_cast(options.snapshot) + ->sequence_number() + : latest_snapshot), + seed); } void DBImpl::RecordReadSample(Slice key) { @@ -1202,9 +1181,9 @@ Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { return DB::Delete(options, key); } -Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { +Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) { Writer w(&mutex_); - w.batch = my_batch; + w.batch = updates; w.sync = options.sync; w.done = false; @@ -1218,10 +1197,10 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } // May temporarily unlock and wait. - Status status = MakeRoomForWrite(my_batch == nullptr); + Status status = MakeRoomForWrite(updates == nullptr); uint64_t last_sequence = versions_->LastSequence(); Writer* last_writer = &w; - if (status.ok() && my_batch != nullptr) { // nullptr batch is for compactions + if (status.ok() && updates != nullptr) { // nullptr batch is for compactions WriteBatch* updates = BuildBatchGroup(&last_writer); WriteBatchInternal::SetSequence(updates, last_sequence + 1); last_sequence += WriteBatchInternal::Count(updates); @@ -1290,8 +1269,8 @@ WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { // original write is small, limit the growth so we do not slow // down the small write too much. size_t max_size = 1 << 20; - if (size <= (128<<10)) { - max_size = size + (128<<10); + if (size <= (128 << 10)) { + max_size = size + (128 << 10); } *last_writer = first; @@ -1337,9 +1316,8 @@ Status DBImpl::MakeRoomForWrite(bool force) { // Yield previous error s = bg_error_; break; - } else if ( - allow_delay && - versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + } else if (allow_delay && versions_->NumLevelFiles(0) >= + config::kL0_SlowdownWritesTrigger) { // We are getting close to hitting a hard limit on the number of // L0 files. Rather than delaying a single write by several // seconds when we hit the hard limit, start delaying each @@ -1383,7 +1361,7 @@ Status DBImpl::MakeRoomForWrite(bool force) { has_imm_.store(true, std::memory_order_release); mem_ = new MemTable(internal_comparator_); mem_->Ref(); - force = false; // Do not force another compaction if have room + force = false; // Do not force another compaction if have room MaybeScheduleCompaction(); } } @@ -1417,21 +1395,16 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { snprintf(buf, sizeof(buf), " Compactions\n" "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" - "--------------------------------------------------\n" - ); + "--------------------------------------------------\n"); value->append(buf); for (int level = 0; level < config::kNumLevels; level++) { int files = versions_->NumLevelFiles(level); if (stats_[level].micros > 0 || files > 0) { - snprintf( - buf, sizeof(buf), - "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", - level, - files, - versions_->NumLevelBytes(level) / 1048576.0, - stats_[level].micros / 1e6, - stats_[level].bytes_read / 1048576.0, - stats_[level].bytes_written / 1048576.0); + snprintf(buf, sizeof(buf), "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", level, + files, versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); value->append(buf); } } @@ -1457,9 +1430,7 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { return false; } -void DBImpl::GetApproximateSizes( - const Range* range, int n, - uint64_t* sizes) { +void DBImpl::GetApproximateSizes(const Range* range, int n, uint64_t* sizes) { // TODO(opt): better implementation Version* v; { @@ -1497,10 +1468,9 @@ Status DB::Delete(const WriteOptions& opt, const Slice& key) { return Write(opt, &batch); } -DB::~DB() { } +DB::~DB() {} -Status DB::Open(const Options& options, const std::string& dbname, - DB** dbptr) { +Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) { *dbptr = nullptr; DBImpl* impl = new DBImpl(options, dbname); @@ -1543,8 +1513,7 @@ Status DB::Open(const Options& options, const std::string& dbname, return s; } -Snapshot::~Snapshot() { -} +Snapshot::~Snapshot() {} Status DestroyDB(const std::string& dbname, const Options& options) { Env* env = options.env; diff --git a/db/db_impl.h b/db/db_impl.h index ca00d4209a..c895952313 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -35,8 +35,7 @@ class DBImpl : public DB { virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); virtual Status Delete(const WriteOptions&, const Slice& key); virtual Status Write(const WriteOptions& options, WriteBatch* updates); - virtual Status Get(const ReadOptions& options, - const Slice& key, + virtual Status Get(const ReadOptions& options, const Slice& key, std::string* value); virtual Iterator* NewIterator(const ReadOptions&); virtual const Snapshot* GetSnapshot(); @@ -166,9 +165,9 @@ class DBImpl : public DB { struct ManualCompaction { int level; bool done; - const InternalKey* begin; // null means beginning of key range - const InternalKey* end; // null means end of key range - InternalKey tmp_storage; // Used to keep track of compaction progress + const InternalKey* begin; // null means beginning of key range + const InternalKey* end; // null means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress }; ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); @@ -184,7 +183,7 @@ class DBImpl : public DB { int64_t bytes_read; int64_t bytes_written; - CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) {} void Add(const CompactionStats& c) { this->micros += c.micros; diff --git a/db/db_iter.cc b/db/db_iter.cc index 4d0f42e424..1e5b5e2d31 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -4,9 +4,9 @@ #include "db/db_iter.h" -#include "db/filename.h" #include "db/db_impl.h" #include "db/dbformat.h" +#include "db/filename.h" #include "leveldb/env.h" #include "leveldb/iterator.h" #include "port/port.h" @@ -36,17 +36,14 @@ namespace { // combines multiple entries for the same userkey found in the DB // representation into a single entry while accounting for sequence // numbers, deletion markers, overwrites, etc. -class DBIter: public Iterator { +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 - }; + enum Direction { kForward, kReverse }; DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, uint32_t seed) @@ -57,11 +54,8 @@ class DBIter: public Iterator { direction_(kForward), valid_(false), rnd_(seed), - bytes_until_read_sampling_(RandomCompactionPeriod()) { - } - virtual ~DBIter() { - delete iter_; - } + bytes_until_read_sampling_(RandomCompactionPeriod()) {} + virtual ~DBIter() { delete iter_; } virtual bool Valid() const { return valid_; } virtual Slice key() const { assert(valid_); @@ -105,7 +99,7 @@ class DBIter: public Iterator { // Picks the number of bytes that can be read until a compaction is scheduled. size_t RandomCompactionPeriod() { - return rnd_.Uniform(2*config::kReadBytesPeriod); + return rnd_.Uniform(2 * config::kReadBytesPeriod); } DBImpl* db_; @@ -114,8 +108,8 @@ class DBIter: public Iterator { SequenceNumber const sequence_; Status status_; - std::string saved_key_; // == current key when direction_==kReverse - std::string saved_value_; // == current raw value when direction_==kReverse + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse Direction direction_; bool valid_; @@ -221,8 +215,8 @@ void DBIter::Prev() { ClearSavedValue(); return; } - if (user_comparator_->Compare(ExtractUserKey(iter_->key()), - saved_key_) < 0) { + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), saved_key_) < + 0) { break; } } @@ -278,8 +272,8 @@ void DBIter::Seek(const Slice& target) { direction_ = kForward; ClearSavedValue(); saved_key_.clear(); - AppendInternalKey( - &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + AppendInternalKey(&saved_key_, + ParsedInternalKey(target, sequence_, kValueTypeForSeek)); iter_->Seek(saved_key_); if (iter_->Valid()) { FindNextUserEntry(false, &saved_key_ /* temporary storage */); @@ -308,12 +302,9 @@ void DBIter::SeekToLast() { } // anonymous namespace -Iterator* NewDBIterator( - DBImpl* db, - const Comparator* user_key_comparator, - Iterator* internal_iter, - SequenceNumber sequence, - uint32_t seed) { +Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator, + Iterator* internal_iter, SequenceNumber sequence, + uint32_t seed) { return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); } diff --git a/db/db_iter.h b/db/db_iter.h index 262840edd3..fd93e912a0 100644 --- a/db/db_iter.h +++ b/db/db_iter.h @@ -6,8 +6,9 @@ #define STORAGE_LEVELDB_DB_DB_ITER_H_ #include -#include "leveldb/db.h" + #include "db/dbformat.h" +#include "leveldb/db.h" namespace leveldb { @@ -16,10 +17,8 @@ class DBImpl; // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. -Iterator* NewDBIterator(DBImpl* db, - const Comparator* user_key_comparator, - Iterator* internal_iter, - SequenceNumber sequence, +Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator, + Iterator* internal_iter, SequenceNumber sequence, uint32_t seed); } // namespace leveldb diff --git a/db/db_test.cc b/db/db_test.cc index 3ab4aee851..43432163d1 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/db.h" + #include #include -#include "leveldb/db.h" -#include "leveldb/filter_policy.h" #include "db/db_impl.h" #include "db/filename.h" #include "db/version_set.h" #include "db/write_batch_internal.h" #include "leveldb/cache.h" #include "leveldb/env.h" +#include "leveldb/filter_policy.h" #include "leveldb/table.h" #include "port/port.h" #include "port/thread_annotations.h" @@ -31,9 +32,9 @@ static std::string RandomString(Random* rnd, int len) { } static std::string RandomKey(Random* rnd) { - int len = (rnd->OneIn(3) - ? 1 // Short sometimes to encourage collisions - : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + int len = + (rnd->OneIn(3) ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); return test::RandomKey(rnd, len); } @@ -42,11 +43,10 @@ class AtomicCounter { private: port::Mutex mu_; int count_ GUARDED_BY(mu_); + public: - AtomicCounter() : count_(0) { } - void Increment() { - IncrementBy(1); - } + AtomicCounter() : count_(0) {} + void Increment() { IncrementBy(1); } void IncrementBy(int count) LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); count_ += count; @@ -120,15 +120,15 @@ class SpecialEnv : public EnvWrapper { bool count_random_reads_; AtomicCounter random_read_counter_; - explicit SpecialEnv(Env* base) : EnvWrapper(base), - delay_data_sync_(false), - data_sync_error_(false), - no_space_(false), - non_writable_(false), - manifest_sync_error_(false), - manifest_write_error_(false), - count_random_reads_(false) { - } + explicit SpecialEnv(Env* base) + : EnvWrapper(base), + delay_data_sync_(false), + data_sync_error_(false), + no_space_(false), + non_writable_(false), + manifest_sync_error_(false), + manifest_write_error_(false), + count_random_reads_(false) {} Status NewWritableFile(const std::string& f, WritableFile** r) { class DataFile : public WritableFile { @@ -137,10 +137,7 @@ class SpecialEnv : public EnvWrapper { WritableFile* const base_; public: - DataFile(SpecialEnv* env, WritableFile* base) - : env_(env), - base_(base) { - } + DataFile(SpecialEnv* env, WritableFile* base) : env_(env), base_(base) {} ~DataFile() { delete base_; } Status Append(const Slice& data) { if (env_->no_space_.load(std::memory_order_acquire)) { @@ -166,8 +163,9 @@ class SpecialEnv : public EnvWrapper { private: SpecialEnv* env_; WritableFile* base_; + public: - ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) {} ~ManifestFile() { delete base_; } Status Append(const Slice& data) { if (env_->manifest_write_error_.load(std::memory_order_acquire)) { @@ -208,10 +206,10 @@ class SpecialEnv : public EnvWrapper { private: RandomAccessFile* target_; AtomicCounter* counter_; + public: CountingFile(RandomAccessFile* target, AtomicCounter* counter) - : target_(target), counter_(counter) { - } + : target_(target), counter_(counter) {} virtual ~CountingFile() { delete target_; } virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { @@ -233,13 +231,7 @@ class DBTest { const FilterPolicy* filter_policy_; // Sequence of option configurations to try - enum OptionConfig { - kDefault, - kReuse, - kFilter, - kUncompressed, - kEnd - }; + enum OptionConfig { kDefault, kReuse, kFilter, kUncompressed, kEnd }; int option_config_; public: @@ -249,8 +241,7 @@ class DBTest { Options last_options_; - DBTest() : option_config_(kDefault), - env_(new SpecialEnv(Env::Default())) { + DBTest() : option_config_(kDefault), env_(new SpecialEnv(Env::Default())) { filter_policy_ = NewBloomFilterPolicy(10); dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, Options()); @@ -297,13 +288,9 @@ class DBTest { return options; } - DBImpl* dbfull() { - return reinterpret_cast(db_); - } + DBImpl* dbfull() { return reinterpret_cast(db_); } - void Reopen(Options* options = nullptr) { - ASSERT_OK(TryReopen(options)); - } + void Reopen(Options* options = nullptr) { ASSERT_OK(TryReopen(options)); } void Close() { delete db_; @@ -336,9 +323,7 @@ class DBTest { return db_->Put(WriteOptions(), k, v); } - Status Delete(const std::string& k) { - return db_->Delete(WriteOptions(), k); - } + Status Delete(const std::string& k) { return db_->Delete(WriteOptions(), k); } std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { ReadOptions options; @@ -424,9 +409,8 @@ class DBTest { int NumTableFilesAtLevel(int level) { std::string property; - ASSERT_TRUE( - db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), - &property)); + ASSERT_TRUE(db_->GetProperty( + "leveldb.num-files-at-level" + NumberToString(level), &property)); return std::stoi(property); } @@ -491,9 +475,9 @@ class DBTest { void DumpFileCounts(const char* label) { fprintf(stderr, "---\n%s:\n", label); - fprintf(stderr, "maxoverlap: %lld\n", - static_cast( - dbfull()->TEST_MaxNextLevelOverlappingBytes())); + fprintf( + stderr, "maxoverlap: %lld\n", + static_cast(dbfull()->TEST_MaxNextLevelOverlappingBytes())); for (int level = 0; level < config::kNumLevels; level++) { int num = NumTableFilesAtLevel(level); if (num > 0) { @@ -612,8 +596,8 @@ TEST(DBTest, GetFromImmutableLayer) { // Block sync calls. env_->delay_data_sync_.store(true, std::memory_order_release); - Put("k1", std::string(100000, 'x')); // Fill memtable. - Put("k2", std::string(100000, 'y')); // Trigger compaction. + Put("k1", std::string(100000, 'x')); // Fill memtable. + Put("k2", std::string(100000, 'y')); // Trigger compaction. ASSERT_EQ("v1", Get("foo")); // Release sync calls. env_->delay_data_sync_.store(false, std::memory_order_release); @@ -635,7 +619,7 @@ TEST(DBTest, GetMemUsage) { ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val)); int mem_usage = std::stoi(val); ASSERT_GT(mem_usage, 0); - ASSERT_LT(mem_usage, 5*1024*1024); + ASSERT_LT(mem_usage, 5 * 1024 * 1024); } while (ChangeOptions()); } @@ -760,8 +744,7 @@ TEST(DBTest, GetEncountersEmptyLevel) { // Step 1: First place sstables in levels 0 and 2 int compaction_count = 0; - while (NumTableFilesAtLevel(0) == 0 || - NumTableFilesAtLevel(2) == 0) { + while (NumTableFilesAtLevel(0) == 0 || NumTableFilesAtLevel(2) == 0) { ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; compaction_count++; Put("a", "begin"); @@ -898,10 +881,10 @@ TEST(DBTest, IterMulti) { ASSERT_EQ(IterStatus(iter), "b->vb"); // Make sure iter stays at snapshot - ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a", "va2")); ASSERT_OK(Put("a2", "va3")); - ASSERT_OK(Put("b", "vb2")); - ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); ASSERT_OK(Delete("b")); iter->SeekToFirst(); ASSERT_EQ(IterStatus(iter), "a->va"); @@ -1092,7 +1075,7 @@ TEST(DBTest, RecoverWithLargeLog) { TEST(DBTest, CompactionsGenerateMultipleFiles) { Options options = CurrentOptions(); - options.write_buffer_size = 100000000; // Large write buffer + options.write_buffer_size = 100000000; // Large write buffer Reopen(&options); Random rnd(301); @@ -1161,26 +1144,25 @@ TEST(DBTest, SparseMerge) { dbfull()->TEST_CompactRange(0, nullptr, nullptr); // Make sparse update - Put("A", "va2"); + Put("A", "va2"); Put("B100", "bvalue2"); - Put("C", "vc2"); + Put("C", "vc2"); dbfull()->TEST_CompactMemTable(); // Compactions should not cause us to create a situation where // a file overlaps too much data at the next level. - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); dbfull()->TEST_CompactRange(0, nullptr, nullptr); - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); dbfull()->TEST_CompactRange(1, nullptr, nullptr); - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); } static bool Between(uint64_t val, uint64_t low, uint64_t high) { bool result = (val >= low) && (val <= high); if (!result) { fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", - (unsigned long long)(val), - (unsigned long long)(low), + (unsigned long long)(val), (unsigned long long)(low), (unsigned long long)(high)); } return result; @@ -1189,7 +1171,7 @@ static bool Between(uint64_t val, uint64_t low, uint64_t high) { TEST(DBTest, ApproximateSizes) { do { Options options = CurrentOptions(); - options.write_buffer_size = 100000000; // Large write buffer + options.write_buffer_size = 100000000; // Large write buffer options.compression = kNoCompression; DestroyAndReopen(); @@ -1224,12 +1206,13 @@ TEST(DBTest, ApproximateSizes) { for (int compact_start = 0; compact_start < N; compact_start += 10) { for (int i = 0; i < N; i += 10) { - ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); - ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); - ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + ASSERT_TRUE(Between(Size("", Key(i)), S1 * i, S2 * i)); + ASSERT_TRUE(Between(Size("", Key(i) + ".suffix"), S1 * (i + 1), + S2 * (i + 1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i + 10)), S1 * 10, S2 * 10)); } - ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); - ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)), S1 * 50, S2 * 50)); + ASSERT_TRUE(Between(Size("", Key(50) + ".suffix"), S1 * 50, S2 * 50)); std::string cstart_str = Key(compact_start); std::string cend_str = Key(compact_start + 9); @@ -1348,7 +1331,7 @@ TEST(DBTest, HiddenValuesAreRemoved) { Put("pastfoo", "v"); const Snapshot* snapshot = db_->GetSnapshot(); Put("foo", "tiny"); - Put("pastfoo2", "v2"); // Advance sequence number one more + Put("pastfoo2", "v2"); // Advance sequence number one more ASSERT_OK(dbfull()->TEST_CompactMemTable()); ASSERT_GT(NumTableFilesAtLevel(0), 0); @@ -1373,14 +1356,14 @@ TEST(DBTest, DeletionMarkers1) { Put("foo", "v1"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level // Place a table at level last-1 to prevent merging with preceding mutation Put("a", "begin"); Put("z", "end"); dbfull()->TEST_CompactMemTable(); ASSERT_EQ(NumTableFilesAtLevel(last), 1); - ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + ASSERT_EQ(NumTableFilesAtLevel(last - 1), 1); Delete("foo"); Put("foo", "v2"); @@ -1388,11 +1371,11 @@ TEST(DBTest, DeletionMarkers1) { ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); Slice z("z"); - dbfull()->TEST_CompactRange(last-2, nullptr, &z); + dbfull()->TEST_CompactRange(last - 2, nullptr, &z); // DEL eliminated, but v1 remains because we aren't compacting that level // (DEL can be eliminated because v2 hides v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); - dbfull()->TEST_CompactRange(last-1, nullptr, nullptr); + dbfull()->TEST_CompactRange(last - 1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); @@ -1402,23 +1385,23 @@ TEST(DBTest, DeletionMarkers2) { Put("foo", "v1"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level // Place a table at level last-1 to prevent merging with preceding mutation Put("a", "begin"); Put("z", "end"); dbfull()->TEST_CompactMemTable(); ASSERT_EQ(NumTableFilesAtLevel(last), 1); - ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + ASSERT_EQ(NumTableFilesAtLevel(last - 1), 1); Delete("foo"); ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-2, nullptr, nullptr); + dbfull()->TEST_CompactRange(last - 2, nullptr, nullptr); // DEL kept: "last" file overlaps ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-1, nullptr, nullptr); + dbfull()->TEST_CompactRange(last - 1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); @@ -1428,7 +1411,8 @@ TEST(DBTest, OverlapInLevel0) { do { ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; - // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > + // 0. ASSERT_OK(Put("100", "v100")); ASSERT_OK(Put("999", "v999")); dbfull()->TEST_CompactMemTable(); @@ -1548,16 +1532,17 @@ TEST(DBTest, CustomComparator) { return ToNumber(a) - ToNumber(b); } virtual void FindShortestSeparator(std::string* s, const Slice& l) const { - ToNumber(*s); // Check format - ToNumber(l); // Check format + ToNumber(*s); // Check format + ToNumber(l); // Check format } virtual void FindShortSuccessor(std::string* key) const { - ToNumber(*key); // Check format + ToNumber(*key); // Check format } + private: static int ToNumber(const Slice& x) { // Check that there are no extra characters. - ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size() - 1] == ']') << EscapeString(x); int val; char ignored; @@ -1570,7 +1555,7 @@ TEST(DBTest, CustomComparator) { Options new_options = CurrentOptions(); new_options.create_if_missing = true; new_options.comparator = &cmp; - new_options.filter_policy = nullptr; // Cannot use bloom filters + new_options.filter_policy = nullptr; // Cannot use bloom filters new_options.write_buffer_size = 1000; // Compact more often DestroyAndReopen(&new_options); ASSERT_OK(Put("[10]", "ten")); @@ -1588,7 +1573,7 @@ TEST(DBTest, CustomComparator) { for (int run = 0; run < 2; run++) { for (int i = 0; i < 1000; i++) { char buf[100]; - snprintf(buf, sizeof(buf), "[%d]", i*10); + snprintf(buf, sizeof(buf), "[%d]", i * 10); ASSERT_OK(Put(buf, buf)); } Compact("[0]", "[1000000]"); @@ -1739,7 +1724,7 @@ TEST(DBTest, NoSpace) { // Force out-of-space errors. env_->no_space_.store(true, std::memory_order_release); for (int i = 0; i < 10; i++) { - for (int level = 0; level < config::kNumLevels-1; level++) { + for (int level = 0; level < config::kNumLevels - 1; level++) { dbfull()->TEST_CompactRange(level, nullptr, nullptr); } } @@ -1809,9 +1794,8 @@ TEST(DBTest, ManifestWriteError) { // We iterate twice. In the second iteration, everything is the // same except the log record never makes it to the MANIFEST file. for (int iter = 0; iter < 2; iter++) { - std::atomic* error_type = (iter == 0) - ? &env_->manifest_sync_error_ - : &env_->manifest_write_error_; + std::atomic* error_type = (iter == 0) ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; // Insert foo=>bar mapping Options options = CurrentOptions(); @@ -1826,7 +1810,7 @@ TEST(DBTest, ManifestWriteError) { dbfull()->TEST_CompactMemTable(); ASSERT_EQ("bar", Get("foo")); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level // Merging compaction (will fail) error_type->store(true, std::memory_order_release); @@ -1854,8 +1838,7 @@ TEST(DBTest, MissingSSTFile) { options.paranoid_checks = true; Status s = TryReopen(&options); ASSERT_TRUE(!s.ok()); - ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) - << s.ToString(); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) << s.ToString(); } TEST(DBTest, StillReadSST) { @@ -1915,7 +1898,7 @@ TEST(DBTest, BloomFilter) { int reads = env_->random_read_counter_.Read(); fprintf(stderr, "%d present => %d reads\n", N, reads); ASSERT_GE(reads, N); - ASSERT_LE(reads, N + 2*N/100); + ASSERT_LE(reads, N + 2 * N / 100); // Lookup present keys. Should rarely read from either sstable. env_->random_read_counter_.Reset(); @@ -1924,7 +1907,7 @@ TEST(DBTest, BloomFilter) { } reads = env_->random_read_counter_.Read(); fprintf(stderr, "%d missing => %d reads\n", N, reads); - ASSERT_LE(reads, 3*N/100); + ASSERT_LE(reads, 3 * N / 100); env_->delay_data_sync_.store(false, std::memory_order_release); Close(); @@ -1970,8 +1953,8 @@ static void MTThreadBody(void* arg) { if (rnd.OneIn(2)) { // Write values of the form . // We add some padding for force compactions. - snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", - key, id, static_cast(counter)); + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", key, id, + static_cast(counter)); ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); } else { // Read a value and verify that it matches the pattern written above. @@ -2033,24 +2016,24 @@ namespace { typedef std::map KVMap; } -class ModelDB: public DB { +class ModelDB : public DB { public: class ModelSnapshot : public Snapshot { public: KVMap map_; }; - explicit ModelDB(const Options& options): options_(options) { } - ~ModelDB() { } + explicit ModelDB(const Options& options) : options_(options) {} + ~ModelDB() {} virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { return DB::Put(o, k, v); } virtual Status Delete(const WriteOptions& o, const Slice& key) { return DB::Delete(o, key); } - virtual Status Get(const ReadOptions& options, - const Slice& key, std::string* value) { - assert(false); // Not implemented + virtual Status Get(const ReadOptions& options, const Slice& key, + std::string* value) { + assert(false); // Not implemented return Status::NotFound(key); } virtual Iterator* NewIterator(const ReadOptions& options) { @@ -2080,9 +2063,7 @@ class ModelDB: public DB { virtual void Put(const Slice& key, const Slice& value) { (*map_)[key.ToString()] = value.ToString(); } - virtual void Delete(const Slice& key) { - map_->erase(key.ToString()); - } + virtual void Delete(const Slice& key) { map_->erase(key.ToString()); } }; Handler handler; handler.map_ = &map_; @@ -2097,15 +2078,13 @@ class ModelDB: public DB { sizes[i] = 0; } } - virtual void CompactRange(const Slice* start, const Slice* end) { - } + virtual void CompactRange(const Slice* start, const Slice* end) {} private: - class ModelIter: public Iterator { + class ModelIter : public Iterator { public: ModelIter(const KVMap* map, bool owned) - : map_(map), owned_(owned), iter_(map_->end()) { - } + : map_(map), owned_(owned), iter_(map_->end()) {} ~ModelIter() { if (owned_) delete map_; } @@ -2136,9 +2115,7 @@ class ModelDB: public DB { KVMap map_; }; -static bool CompareIterators(int step, - DB* model, - DB* db, +static bool CompareIterators(int step, DB* model, DB* db, const Snapshot* model_snap, const Snapshot* db_snap) { ReadOptions options; @@ -2149,12 +2126,10 @@ static bool CompareIterators(int step, bool ok = true; int count = 0; for (miter->SeekToFirst(), dbiter->SeekToFirst(); - ok && miter->Valid() && dbiter->Valid(); - miter->Next(), dbiter->Next()) { + ok && miter->Valid() && dbiter->Valid(); miter->Next(), dbiter->Next()) { count++; if (miter->key().compare(dbiter->key()) != 0) { - fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", - step, + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", step, EscapeString(miter->key()).c_str(), EscapeString(dbiter->key()).c_str()); ok = false; @@ -2163,8 +2138,7 @@ static bool CompareIterators(int step, if (miter->value().compare(dbiter->value()) != 0) { fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", - step, - EscapeString(miter->key()).c_str(), + step, EscapeString(miter->key()).c_str(), EscapeString(miter->value()).c_str(), EscapeString(miter->value()).c_str()); ok = false; @@ -2198,22 +2172,19 @@ TEST(DBTest, Randomized) { } // TODO(sanjay): Test Get() works int p = rnd.Uniform(100); - if (p < 45) { // Put + if (p < 45) { // Put k = RandomKey(&rnd); - v = RandomString(&rnd, - rnd.OneIn(20) - ? 100 + rnd.Uniform(100) - : rnd.Uniform(8)); + v = RandomString( + &rnd, rnd.OneIn(20) ? 100 + rnd.Uniform(100) : rnd.Uniform(8)); ASSERT_OK(model.Put(WriteOptions(), k, v)); ASSERT_OK(db_->Put(WriteOptions(), k, v)); - } else if (p < 90) { // Delete + } else if (p < 90) { // Delete k = RandomKey(&rnd); ASSERT_OK(model.Delete(WriteOptions(), k)); ASSERT_OK(db_->Delete(WriteOptions(), k)); - - } else { // Multi-element batch + } else { // Multi-element batch WriteBatch b; const int num = rnd.Uniform(8); for (int i = 0; i < num; i++) { @@ -2288,8 +2259,8 @@ void BM_LogAndApply(int iters, int num_base_files) { VersionEdit vbase; uint64_t fnum = 1; for (int i = 0; i < num_base_files; i++) { - InternalKey start(MakeKey(2*fnum), 1, kTypeValue); - InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + InternalKey start(MakeKey(2 * fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion); vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); } ASSERT_OK(vset.LogAndApply(&vbase, &mu)); @@ -2299,8 +2270,8 @@ void BM_LogAndApply(int iters, int num_base_files) { for (int i = 0; i < iters; i++) { VersionEdit vedit; vedit.DeleteFile(2, fnum); - InternalKey start(MakeKey(2*fnum), 1, kTypeValue); - InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + InternalKey start(MakeKey(2 * fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion); vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); vset.LogAndApply(&vedit, &mu); } @@ -2309,8 +2280,8 @@ void BM_LogAndApply(int iters, int num_base_files) { char buf[16]; snprintf(buf, sizeof(buf), "%d", num_base_files); fprintf(stderr, - "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", - buf, iters, us, ((float)us) / iters); + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", buf, + iters, us, ((float)us) / iters); } } // namespace leveldb diff --git a/db/dbformat.cc b/db/dbformat.cc index 20a7ca4462..69e8dc6aa8 100644 --- a/db/dbformat.cc +++ b/db/dbformat.cc @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include #include "db/dbformat.h" + +#include + #include "port/port.h" #include "util/coding.h" @@ -22,8 +24,7 @@ void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { std::string ParsedInternalKey::DebugString() const { char buf[50]; - snprintf(buf, sizeof(buf), "' @ %llu : %d", - (unsigned long long) sequence, + snprintf(buf, sizeof(buf), "' @ %llu : %d", (unsigned long long)sequence, int(type)); std::string result = "'"; result += EscapeString(user_key.ToString()); @@ -65,9 +66,8 @@ int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { return r; } -void InternalKeyComparator::FindShortestSeparator( - std::string* start, - const Slice& limit) const { +void InternalKeyComparator::FindShortestSeparator(std::string* start, + const Slice& limit) const { // Attempt to shorten the user portion of the key Slice user_start = ExtractUserKey(*start); Slice user_limit = ExtractUserKey(limit); @@ -77,7 +77,8 @@ void InternalKeyComparator::FindShortestSeparator( user_comparator_->Compare(user_start, tmp) < 0) { // User key has become shorter physically, but larger logically. // Tack on the earliest possible number to the shortened user key. - PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + PutFixed64(&tmp, + PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*start, tmp) < 0); assert(this->Compare(tmp, limit) < 0); start->swap(tmp); @@ -92,15 +93,14 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const { user_comparator_->Compare(user_key, tmp) < 0) { // User key has become shorter physically, but larger logically. // Tack on the earliest possible number to the shortened user key. - PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + PutFixed64(&tmp, + PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*key, tmp) < 0); key->swap(tmp); } } -const char* InternalFilterPolicy::Name() const { - return user_policy_->Name(); -} +const char* InternalFilterPolicy::Name() const { return user_policy_->Name(); } void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, std::string* dst) const { diff --git a/db/dbformat.h b/db/dbformat.h index c4d9575f2c..bdc23b8bd8 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_DB_DBFORMAT_H_ #include + #include "leveldb/comparator.h" #include "leveldb/db.h" #include "leveldb/filter_policy.h" @@ -48,10 +49,7 @@ class InternalKey; // Value types encoded as the last component of internal keys. // DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk // data structures. -enum ValueType { - kTypeDeletion = 0x0, - kTypeValue = 0x1 -}; +enum ValueType { kTypeDeletion = 0x0, kTypeValue = 0x1 }; // kValueTypeForSeek defines the ValueType that should be passed when // constructing a ParsedInternalKey object for seeking to a particular // sequence number (since we sort sequence numbers in decreasing order @@ -64,17 +62,16 @@ typedef uint64_t SequenceNumber; // We leave eight bits empty at the bottom so a type and sequence# // can be packed together into 64-bits. -static const SequenceNumber kMaxSequenceNumber = - ((0x1ull << 56) - 1); +static const SequenceNumber kMaxSequenceNumber = ((0x1ull << 56) - 1); struct ParsedInternalKey { Slice user_key; SequenceNumber sequence; ValueType type; - ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey() {} // Intentionally left uninitialized (for speed) ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) - : user_key(u), sequence(seq), type(t) { } + : user_key(u), sequence(seq), type(t) {} std::string DebugString() const; }; @@ -103,13 +100,13 @@ inline Slice ExtractUserKey(const Slice& internal_key) { class InternalKeyComparator : public Comparator { private: const Comparator* user_comparator_; + public: - explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) {} virtual const char* Name() const; virtual int Compare(const Slice& a, const Slice& b) const; - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const; + virtual void FindShortestSeparator(std::string* start, + const Slice& limit) const; virtual void FindShortSuccessor(std::string* key) const; const Comparator* user_comparator() const { return user_comparator_; } @@ -121,8 +118,9 @@ class InternalKeyComparator : public Comparator { class InternalFilterPolicy : public FilterPolicy { private: const FilterPolicy* const user_policy_; + public: - explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) {} virtual const char* Name() const; virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; @@ -134,8 +132,9 @@ class InternalFilterPolicy : public FilterPolicy { class InternalKey { private: std::string rep_; + public: - InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey() {} // Leave rep_ as empty to indicate it is invalid InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); } @@ -158,8 +157,8 @@ class InternalKey { std::string DebugString() const; }; -inline int InternalKeyComparator::Compare( - const InternalKey& a, const InternalKey& b) const { +inline int InternalKeyComparator::Compare(const InternalKey& a, + const InternalKey& b) const { return Compare(a.Encode(), b.Encode()); } @@ -204,7 +203,7 @@ class LookupKey { const char* start_; const char* kstart_; const char* end_; - char space_[200]; // Avoid allocation for short keys + char space_[200]; // Avoid allocation for short keys // No copying allowed LookupKey(const LookupKey&); diff --git a/db/dbformat_test.cc b/db/dbformat_test.cc index 5d82f5d313..87e6aae50d 100644 --- a/db/dbformat_test.cc +++ b/db/dbformat_test.cc @@ -8,8 +8,7 @@ namespace leveldb { -static std::string IKey(const std::string& user_key, - uint64_t seq, +static std::string IKey(const std::string& user_key, uint64_t seq, ValueType vt) { std::string encoded; AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); @@ -28,9 +27,7 @@ static std::string ShortSuccessor(const std::string& s) { return result; } -static void TestKey(const std::string& key, - uint64_t seq, - ValueType vt) { +static void TestKey(const std::string& key, uint64_t seq, ValueType vt) { std::string encoded = IKey(key, seq, vt); Slice in(encoded); @@ -44,16 +41,22 @@ static void TestKey(const std::string& key, ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); } -class FormatTest { }; +class FormatTest {}; TEST(FormatTest, InternalKey_EncodeDecode) { - const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; - const uint64_t seq[] = { - 1, 2, 3, - (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, - (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, - (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 - }; + const char* keys[] = {"", "k", "hello", "longggggggggggggggggggggg"}; + const uint64_t seq[] = {1, + 2, + 3, + (1ull << 8) - 1, + 1ull << 8, + (1ull << 8) + 1, + (1ull << 16) - 1, + 1ull << 16, + (1ull << 16) + 1, + (1ull << 32) - 1, + 1ull << 32, + (1ull << 32) + 1}; for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { TestKey(keys[k], seq[s], kTypeValue); @@ -65,37 +68,35 @@ TEST(FormatTest, InternalKey_EncodeDecode) { TEST(FormatTest, InternalKeyShortSeparator) { // When user keys are same ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 99, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 101, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 100, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 100, kTypeDeletion))); + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 99, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 101, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeDeletion))); // When user keys are misordered ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("bar", 99, kTypeValue))); + Shorten(IKey("foo", 100, kTypeValue), IKey("bar", 99, kTypeValue))); // When user keys are different, but correctly ordered - ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), - Shorten(IKey("foo", 100, kTypeValue), - IKey("hello", 200, kTypeValue))); + ASSERT_EQ( + IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), IKey("hello", 200, kTypeValue))); // When start user key is prefix of limit user key - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foobar", 200, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foobar", 200, kTypeValue))); // When limit user key is prefix of start user key - ASSERT_EQ(IKey("foobar", 100, kTypeValue), - Shorten(IKey("foobar", 100, kTypeValue), - IKey("foo", 200, kTypeValue))); + ASSERT_EQ( + IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), IKey("foo", 200, kTypeValue))); } TEST(FormatTest, InternalKeyShortestSuccessor) { @@ -107,6 +108,4 @@ TEST(FormatTest, InternalKeyShortestSuccessor) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/dumpfile.cc b/db/dumpfile.cc index 941988b872..1dbff5e657 100644 --- a/db/dumpfile.cc +++ b/db/dumpfile.cc @@ -90,7 +90,6 @@ class WriteBatchItemPrinter : public WriteBatch::Handler { } }; - // Called on every log record (each one of which is a WriteBatch) // found in a kLogFile. static void WriteBatchPrinter(uint64_t pos, Slice record, WritableFile* dst) { @@ -216,9 +215,12 @@ Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) { return Status::InvalidArgument(fname + ": unknown file type"); } switch (ftype) { - case kLogFile: return DumpLog(env, fname, dst); - case kDescriptorFile: return DumpDescriptor(env, fname, dst); - case kTableFile: return DumpTable(env, fname, dst); + case kLogFile: + return DumpLog(env, fname, dst); + case kDescriptorFile: + return DumpDescriptor(env, fname, dst); + case kTableFile: + return DumpTable(env, fname, dst); default: break; } diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index 1f72984b70..7088ea707c 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -9,12 +9,12 @@ #include #include -#include "leveldb/db.h" #include "db/db_impl.h" #include "db/filename.h" #include "db/log_format.h" #include "db/version_set.h" #include "leveldb/cache.h" +#include "leveldb/db.h" #include "leveldb/env.h" #include "leveldb/table.h" #include "leveldb/write_batch.h" @@ -56,8 +56,7 @@ Status Truncate(const std::string& filename, uint64_t length) { SequentialFile* orig_file; Status s = env->NewSequentialFile(filename, &orig_file); - if (!s.ok()) - return s; + if (!s.ok()) return s; char* scratch = new char[length]; leveldb::Slice result; @@ -93,7 +92,7 @@ struct FileState { : filename_(filename), pos_(-1), pos_at_last_sync_(-1), - pos_at_last_flush_(-1) { } + pos_at_last_flush_(-1) {} FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} @@ -108,8 +107,7 @@ struct FileState { // is written to or sync'ed. class TestWritableFile : public WritableFile { public: - TestWritableFile(const FileState& state, - WritableFile* f, + TestWritableFile(const FileState& state, WritableFile* f, FaultInjectionTestEnv* env); virtual ~TestWritableFile(); virtual Status Append(const Slice& data); @@ -130,7 +128,7 @@ class FaultInjectionTestEnv : public EnvWrapper { public: FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {} - virtual ~FaultInjectionTestEnv() { } + virtual ~FaultInjectionTestEnv() {} virtual Status NewWritableFile(const std::string& fname, WritableFile** result); virtual Status NewAppendableFile(const std::string& fname, @@ -165,13 +163,9 @@ class FaultInjectionTestEnv : public EnvWrapper { bool filesystem_active_ GUARDED_BY(mutex_); // Record flushes, syncs, writes }; -TestWritableFile::TestWritableFile(const FileState& state, - WritableFile* f, +TestWritableFile::TestWritableFile(const FileState& state, WritableFile* f, FaultInjectionTestEnv* env) - : state_(state), - target_(f), - writable_file_opened_(true), - env_(env) { + : state_(state), target_(f), writable_file_opened_(true), env_(env) { assert(f != nullptr); } @@ -395,9 +389,7 @@ class FaultInjectionTest { delete env_; } - void ReuseLogs(bool reuse) { - options_.reuse_logs = reuse; - } + void ReuseLogs(bool reuse) { options_.reuse_logs = reuse; } void Build(int start_idx, int num_vals) { std::string key_space, value_space; @@ -497,18 +489,17 @@ class FaultInjectionTest { } void PartialCompactTestReopenWithFault(ResetMethod reset_method, - int num_pre_sync, - int num_post_sync) { + int num_pre_sync, int num_post_sync) { env_->SetFilesystemActive(false); CloseDB(); ResetDBState(reset_method); ASSERT_OK(OpenDB()); ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR)); - ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR)); + ASSERT_OK(Verify(num_pre_sync, num_post_sync, + FaultInjectionTest::VAL_EXPECT_ERROR)); } - void NoWriteTestPreFault() { - } + void NoWriteTestPreFault() {} void NoWriteTestReopenWithFault(ResetMethod reset_method) { CloseDB(); @@ -524,8 +515,7 @@ class FaultInjectionTest { int num_post_sync = rnd.Uniform(kMaxNumValues); PartialCompactTestPreFault(num_pre_sync, num_post_sync); - PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, - num_pre_sync, + PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, num_pre_sync, num_post_sync); NoWriteTestPreFault(); @@ -535,8 +525,7 @@ class FaultInjectionTest { // No new files created so we expect all values since no files will be // dropped. PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, - num_pre_sync + num_post_sync, - 0); + num_pre_sync + num_post_sync, 0); NoWriteTestPreFault(); NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); @@ -556,6 +545,4 @@ TEST(FaultInjectionTest, FaultTestWithLogReuse) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/filename.cc b/db/filename.cc index 6539bbe75a..85de45c507 100644 --- a/db/filename.cc +++ b/db/filename.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "db/filename.h" + #include #include -#include "db/filename.h" + #include "db/dbformat.h" #include "leveldb/env.h" #include "util/logging.h" @@ -19,8 +21,7 @@ static std::string MakeFileName(const std::string& dbname, uint64_t number, const char* suffix) { char buf[100]; snprintf(buf, sizeof(buf), "/%06llu.%s", - static_cast(number), - suffix); + static_cast(number), suffix); return dbname + buf; } @@ -51,9 +52,7 @@ std::string CurrentFileName(const std::string& dbname) { return dbname + "/CURRENT"; } -std::string LockFileName(const std::string& dbname) { - return dbname + "/LOCK"; -} +std::string LockFileName(const std::string& dbname) { return dbname + "/LOCK"; } std::string TempFileName(const std::string& dbname, uint64_t number) { assert(number > 0); @@ -69,7 +68,6 @@ std::string OldInfoLogFileName(const std::string& dbname) { return dbname + "/LOG.old"; } - // Owned filenames have the form: // dbname/CURRENT // dbname/LOCK @@ -77,8 +75,7 @@ std::string OldInfoLogFileName(const std::string& dbname) { // dbname/LOG.old // dbname/MANIFEST-[0-9]+ // dbname/[0-9]+.(log|sst|ldb) -bool ParseFileName(const std::string& filename, - uint64_t* number, +bool ParseFileName(const std::string& filename, uint64_t* number, FileType* type) { Slice rest(filename); if (rest == "CURRENT") { diff --git a/db/filename.h b/db/filename.h index 62cb3ef92d..524e813c06 100644 --- a/db/filename.h +++ b/db/filename.h @@ -8,7 +8,9 @@ #define STORAGE_LEVELDB_DB_FILENAME_H_ #include + #include + #include "leveldb/slice.h" #include "leveldb/status.h" #include "port/port.h" @@ -69,8 +71,7 @@ std::string OldInfoLogFileName(const std::string& dbname); // If filename is a leveldb file, store the type of the file in *type. // The number encoded in the filename is stored in *number. If the // filename was successfully parsed, returns true. Else return false. -bool ParseFileName(const std::string& filename, - uint64_t* number, +bool ParseFileName(const std::string& filename, uint64_t* number, FileType* type); // Make the CURRENT file point to the descriptor file with the diff --git a/db/filename_test.cc b/db/filename_test.cc index 0bde538e27..952f32008e 100644 --- a/db/filename_test.cc +++ b/db/filename_test.cc @@ -11,7 +11,7 @@ namespace leveldb { -class FileNameTest { }; +class FileNameTest {}; TEST(FileNameTest, Parse) { Slice db; @@ -24,17 +24,17 @@ TEST(FileNameTest, Parse) { uint64_t number; FileType type; } cases[] = { - { "100.log", 100, kLogFile }, - { "0.log", 0, kLogFile }, - { "0.sst", 0, kTableFile }, - { "0.ldb", 0, kTableFile }, - { "CURRENT", 0, kCurrentFile }, - { "LOCK", 0, kDBLockFile }, - { "MANIFEST-2", 2, kDescriptorFile }, - { "MANIFEST-7", 7, kDescriptorFile }, - { "LOG", 0, kInfoLogFile }, - { "LOG.old", 0, kInfoLogFile }, - { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + {"100.log", 100, kLogFile}, + {"0.log", 0, kLogFile}, + {"0.sst", 0, kTableFile}, + {"0.ldb", 0, kTableFile}, + {"CURRENT", 0, kCurrentFile}, + {"LOCK", 0, kDBLockFile}, + {"MANIFEST-2", 2, kDescriptorFile}, + {"MANIFEST-7", 7, kDescriptorFile}, + {"LOG", 0, kInfoLogFile}, + {"LOG.old", 0, kInfoLogFile}, + {"18446744073709551615.log", 18446744073709551615ull, kLogFile}, }; for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { std::string f = cases[i].fname; @@ -44,30 +44,28 @@ TEST(FileNameTest, Parse) { } // Errors - static const char* errors[] = { - "", - "foo", - "foo-dx-100.log", - ".log", - "", - "manifest", - "CURREN", - "CURRENTX", - "MANIFES", - "MANIFEST", - "MANIFEST-", - "XMANIFEST-3", - "MANIFEST-3x", - "LOC", - "LOCKx", - "LO", - "LOGx", - "18446744073709551616.log", - "184467440737095516150.log", - "100", - "100.", - "100.lop" - }; + static const char* errors[] = {"", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop"}; for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { std::string f = errors[i]; ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; @@ -130,6 +128,4 @@ TEST(FileNameTest, Construction) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/leveldbutil.cc b/db/leveldbutil.cc index 9f4b7dd70c..b21cf8efa4 100644 --- a/db/leveldbutil.cc +++ b/db/leveldbutil.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include + #include "leveldb/dumpfile.h" #include "leveldb/env.h" #include "leveldb/status.h" @@ -38,11 +39,9 @@ bool HandleDumpCommand(Env* env, char** files, int num) { } // namespace leveldb static void Usage() { - fprintf( - stderr, - "Usage: leveldbutil command...\n" - " dump files... -- dump contents of specified files\n" - ); + fprintf(stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n"); } int main(int argc, char** argv) { @@ -54,7 +53,7 @@ int main(int argc, char** argv) { } else { std::string command = argv[1]; if (command == "dump") { - ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + ok = leveldb::HandleDumpCommand(env, argv + 2, argc - 2); } else { Usage(); ok = false; diff --git a/db/log_reader.cc b/db/log_reader.cc index 19c4df6fdb..f47272349c 100644 --- a/db/log_reader.cc +++ b/db/log_reader.cc @@ -5,6 +5,7 @@ #include "db/log_reader.h" #include + #include "leveldb/env.h" #include "util/coding.h" #include "util/crc32c.h" @@ -12,8 +13,7 @@ namespace leveldb { namespace log { -Reader::Reporter::~Reporter() { -} +Reader::Reporter::~Reporter() {} Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, uint64_t initial_offset) @@ -26,12 +26,9 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, last_record_offset_(0), end_of_buffer_offset_(0), initial_offset_(initial_offset), - resyncing_(initial_offset > 0) { -} + resyncing_(initial_offset > 0) {} -Reader::~Reader() { - delete[] backing_store_; -} +Reader::~Reader() { delete[] backing_store_; } bool Reader::SkipToInitialBlock() { const size_t offset_in_block = initial_offset_ % kBlockSize; @@ -176,9 +173,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { return false; } -uint64_t Reader::LastRecordOffset() { - return last_record_offset_; -} +uint64_t Reader::LastRecordOffset() { return last_record_offset_; } void Reader::ReportCorruption(uint64_t bytes, const char* reason) { ReportDrop(bytes, Status::Corruption(reason)); diff --git a/db/log_reader.h b/db/log_reader.h index 7dcce8e844..b27c1645ee 100644 --- a/db/log_reader.h +++ b/db/log_reader.h @@ -63,7 +63,7 @@ class Reader { bool const checksum_; char* const backing_store_; Slice buffer_; - bool eof_; // Last Read() indicated EOF by returning < kBlockSize + bool eof_; // Last Read() indicated EOF by returning < kBlockSize // Offset of the last record returned by ReadRecord. uint64_t last_record_offset_; diff --git a/db/log_test.cc b/db/log_test.cc index 48a5928657..3acaa33cc5 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -56,7 +56,7 @@ class LogTest { Slice contents_; bool force_error_; bool returned_partial_; - StringSource() : force_error_(false), returned_partial_(false) { } + StringSource() : force_error_(false), returned_partial_(false) {} virtual Status Read(size_t n, Slice* result, char* scratch) { ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; @@ -93,7 +93,7 @@ class LogTest { size_t dropped_bytes_; std::string message_; - ReportCollector() : dropped_bytes_(0) { } + ReportCollector() : dropped_bytes_(0) {} virtual void Corruption(size_t bytes, const Status& status) { dropped_bytes_ += bytes; message_.append(status.ToString()); @@ -113,11 +113,11 @@ class LogTest { static int num_initial_offset_records_; public: - LogTest() : reading_(false), - writer_(new Writer(&dest_)), - reader_(new Reader(&source_, &report_, true/*checksum*/, - 0/*initial_offset*/)) { - } + LogTest() + : reading_(false), + writer_(new Writer(&dest_)), + reader_(new Reader(&source_, &report_, true /*checksum*/, + 0 /*initial_offset*/)) {} ~LogTest() { delete writer_; @@ -134,9 +134,7 @@ class LogTest { writer_->AddRecord(Slice(msg)); } - size_t WrittenBytes() const { - return dest_.contents_.size(); - } + size_t WrittenBytes() const { return dest_.contents_.size(); } std::string Read() { if (!reading_) { @@ -166,22 +164,16 @@ class LogTest { void FixChecksum(int header_offset, int len) { // Compute crc of type/len/data - uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset + 6], 1 + len); crc = crc32c::Mask(crc); EncodeFixed32(&dest_.contents_[header_offset], crc); } - void ForceError() { - source_.force_error_ = true; - } + void ForceError() { source_.force_error_ = true; } - size_t DroppedBytes() const { - return report_.dropped_bytes_; - } + size_t DroppedBytes() const { return report_.dropped_bytes_; } - std::string ReportMessage() const { - return report_.message_; - } + std::string ReportMessage() const { return report_.message_; } // Returns OK iff recorded error message contains "msg" std::string MatchError(const std::string& msg) const { @@ -202,14 +194,14 @@ class LogTest { void StartReadingAt(uint64_t initial_offset) { delete reader_; - reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset); + reader_ = new Reader(&source_, &report_, true /*checksum*/, initial_offset); } void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { WriteInitialOffsetLog(); reading_ = true; source_.contents_ = Slice(dest_.contents_); - Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + Reader* offset_reader = new Reader(&source_, &report_, true /*checksum*/, WrittenBytes() + offset_past_end); Slice record; std::string scratch; @@ -222,8 +214,8 @@ class LogTest { WriteInitialOffsetLog(); reading_ = true; source_.contents_ = Slice(dest_.contents_); - Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, - initial_offset); + Reader* offset_reader = + new Reader(&source_, &report_, true /*checksum*/, initial_offset); // Read all records from expected_record_offset through the last one. ASSERT_LT(expected_record_offset, num_initial_offset_records_); @@ -242,34 +234,30 @@ class LogTest { } }; -size_t LogTest::initial_offset_record_sizes_[] = - {10000, // Two sizable records in first block - 10000, - 2 * log::kBlockSize - 1000, // Span three blocks - 1, - 13716, // Consume all but two bytes of block 3. - log::kBlockSize - kHeaderSize, // Consume the entirety of block 4. - }; - -uint64_t LogTest::initial_offset_last_record_offsets_[] = - {0, - kHeaderSize + 10000, - 2 * (kHeaderSize + 10000), - 2 * (kHeaderSize + 10000) + - (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, - 2 * (kHeaderSize + 10000) + - (2 * log::kBlockSize - 1000) + 3 * kHeaderSize - + kHeaderSize + 1, - 3 * log::kBlockSize, - }; +size_t LogTest::initial_offset_record_sizes_[] = { + 10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1, + 13716, // Consume all but two bytes of block 3. + log::kBlockSize - kHeaderSize, // Consume the entirety of block 4. +}; + +uint64_t LogTest::initial_offset_last_record_offsets_[] = { + 0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize + + kHeaderSize + 1, + 3 * log::kBlockSize, +}; // LogTest::initial_offset_last_record_offsets_ must be defined before this. int LogTest::num_initial_offset_records_ = - sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t); + sizeof(LogTest::initial_offset_last_record_offsets_) / sizeof(uint64_t); -TEST(LogTest, Empty) { - ASSERT_EQ("EOF", Read()); -} +TEST(LogTest, Empty) { ASSERT_EQ("EOF", Read()); } TEST(LogTest, ReadWrite) { Write("foo"); @@ -306,7 +294,7 @@ TEST(LogTest, Fragmentation) { TEST(LogTest, MarginalTrailer) { // Make a trailer that is exactly the same length as an empty record. - const int n = kBlockSize - 2*kHeaderSize; + const int n = kBlockSize - 2 * kHeaderSize; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); Write(""); @@ -319,7 +307,7 @@ TEST(LogTest, MarginalTrailer) { TEST(LogTest, MarginalTrailer2) { // Make a trailer that is exactly the same length as an empty record. - const int n = kBlockSize - 2*kHeaderSize; + const int n = kBlockSize - 2 * kHeaderSize; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); Write("bar"); @@ -331,7 +319,7 @@ TEST(LogTest, MarginalTrailer2) { } TEST(LogTest, ShortTrailer) { - const int n = kBlockSize - 2*kHeaderSize + 4; + const int n = kBlockSize - 2 * kHeaderSize + 4; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); Write(""); @@ -343,7 +331,7 @@ TEST(LogTest, ShortTrailer) { } TEST(LogTest, AlignedEof) { - const int n = kBlockSize - 2*kHeaderSize + 4; + const int n = kBlockSize - 2 * kHeaderSize + 4; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); ASSERT_EQ(BigString("foo", n), Read()); @@ -394,7 +382,7 @@ TEST(LogTest, BadRecordType) { TEST(LogTest, TruncatedTrailingRecordIsIgnored) { Write("foo"); - ShrinkSize(4); // Drop all payload as well as a header byte + ShrinkSize(4); // Drop all payload as well as a header byte ASSERT_EQ("EOF", Read()); // Truncated last record is ignored, not treated as an error. ASSERT_EQ(0, DroppedBytes()); @@ -492,7 +480,7 @@ TEST(LogTest, SkipIntoMultiRecord) { // If initial_offset points to a record after first(R1) but before first(R2) // incomplete fragment errors are not actual errors, and must be suppressed // until a new first or full record is encountered. - Write(BigString("foo", 3*kBlockSize)); + Write(BigString("foo", 3 * kBlockSize)); Write("correct"); StartReadingAt(kBlockSize); @@ -514,44 +502,30 @@ TEST(LogTest, ErrorJoinsRecords) { Write("correct"); // Wipe the middle block - for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + for (int offset = kBlockSize; offset < 2 * kBlockSize; offset++) { SetByte(offset, 'x'); } ASSERT_EQ("correct", Read()); ASSERT_EQ("EOF", Read()); const size_t dropped = DroppedBytes(); - ASSERT_LE(dropped, 2*kBlockSize + 100); - ASSERT_GE(dropped, 2*kBlockSize); + ASSERT_LE(dropped, 2 * kBlockSize + 100); + ASSERT_GE(dropped, 2 * kBlockSize); } -TEST(LogTest, ReadStart) { - CheckInitialOffsetRecord(0, 0); -} +TEST(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); } -TEST(LogTest, ReadSecondOneOff) { - CheckInitialOffsetRecord(1, 1); -} +TEST(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); } -TEST(LogTest, ReadSecondTenThousand) { - CheckInitialOffsetRecord(10000, 1); -} +TEST(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); } -TEST(LogTest, ReadSecondStart) { - CheckInitialOffsetRecord(10007, 1); -} +TEST(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); } -TEST(LogTest, ReadThirdOneOff) { - CheckInitialOffsetRecord(10008, 2); -} +TEST(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); } -TEST(LogTest, ReadThirdStart) { - CheckInitialOffsetRecord(20014, 2); -} +TEST(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); } -TEST(LogTest, ReadFourthOneOff) { - CheckInitialOffsetRecord(20015, 3); -} +TEST(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); } TEST(LogTest, ReadFourthFirstBlockTrailer) { CheckInitialOffsetRecord(log::kBlockSize - 4, 3); @@ -575,17 +549,11 @@ TEST(LogTest, ReadInitialOffsetIntoBlockPadding) { CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5); } -TEST(LogTest, ReadEnd) { - CheckOffsetPastEndReturnsNoRecords(0); -} +TEST(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); } -TEST(LogTest, ReadPastEnd) { - CheckOffsetPastEndReturnsNoRecords(5); -} +TEST(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); } } // namespace log } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/log_writer.cc b/db/log_writer.cc index 74a03270da..5e83f6a261 100644 --- a/db/log_writer.cc +++ b/db/log_writer.cc @@ -5,6 +5,7 @@ #include "db/log_writer.h" #include + #include "leveldb/env.h" #include "util/coding.h" #include "util/crc32c.h" @@ -19,9 +20,7 @@ static void InitTypeCrc(uint32_t* type_crc) { } } -Writer::Writer(WritableFile* dest) - : dest_(dest), - block_offset_(0) { +Writer::Writer(WritableFile* dest) : dest_(dest), block_offset_(0) { InitTypeCrc(type_crc_); } @@ -30,8 +29,7 @@ Writer::Writer(WritableFile* dest, uint64_t dest_length) InitTypeCrc(type_crc_); } -Writer::~Writer() { -} +Writer::~Writer() {} Status Writer::AddRecord(const Slice& slice) { const char* ptr = slice.data(); @@ -49,7 +47,7 @@ Status Writer::AddRecord(const Slice& slice) { // Switch to a new block if (leftover > 0) { // Fill the trailer (literal below relies on kHeaderSize being 7) - assert(kHeaderSize == 7); + static_assert(kHeaderSize == 7, ""); dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); } block_offset_ = 0; @@ -81,30 +79,31 @@ Status Writer::AddRecord(const Slice& slice) { return s; } -Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { - assert(n <= 0xffff); // Must fit in two bytes - assert(block_offset_ + kHeaderSize + n <= kBlockSize); +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, + size_t length) { + assert(length <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + length <= kBlockSize); // Format the header char buf[kHeaderSize]; - buf[4] = static_cast(n & 0xff); - buf[5] = static_cast(n >> 8); + buf[4] = static_cast(length & 0xff); + buf[5] = static_cast(length >> 8); buf[6] = static_cast(t); // Compute the crc of the record type and the payload. - uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); - crc = crc32c::Mask(crc); // Adjust for storage + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length); + crc = crc32c::Mask(crc); // Adjust for storage EncodeFixed32(buf, crc); // Write the header and the payload Status s = dest_->Append(Slice(buf, kHeaderSize)); if (s.ok()) { - s = dest_->Append(Slice(ptr, n)); + s = dest_->Append(Slice(ptr, length)); if (s.ok()) { s = dest_->Flush(); } } - block_offset_ += kHeaderSize + n; + block_offset_ += kHeaderSize + length; return s; } diff --git a/db/log_writer.h b/db/log_writer.h index 9e7cc4705b..840809d4be 100644 --- a/db/log_writer.h +++ b/db/log_writer.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_DB_LOG_WRITER_H_ #include + #include "db/log_format.h" #include "leveldb/slice.h" #include "leveldb/status.h" @@ -34,7 +35,7 @@ class Writer { private: WritableFile* dest_; - int block_offset_; // Current offset in block + int block_offset_; // Current offset in block // crc32c values for all supported record types. These are // pre-computed to reduce the overhead of computing the crc of the diff --git a/db/memtable.cc b/db/memtable.cc index 287afdbdcb..c91405c238 100644 --- a/db/memtable.cc +++ b/db/memtable.cc @@ -18,20 +18,15 @@ static Slice GetLengthPrefixedSlice(const char* data) { return Slice(p, len); } -MemTable::MemTable(const InternalKeyComparator& cmp) - : comparator_(cmp), - refs_(0), - table_(comparator_, &arena_) { -} +MemTable::MemTable(const InternalKeyComparator& comparator) + : comparator_(comparator), refs_(0), table_(comparator_, &arena_) {} -MemTable::~MemTable() { - assert(refs_ == 0); -} +MemTable::~MemTable() { assert(refs_ == 0); } size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } -int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) - const { +int MemTable::KeyComparator::operator()(const char* aptr, + const char* bptr) const { // Internal keys are encoded as length-prefixed strings. Slice a = GetLengthPrefixedSlice(aptr); Slice b = GetLengthPrefixedSlice(bptr); @@ -48,9 +43,9 @@ static const char* EncodeKey(std::string* scratch, const Slice& target) { return scratch->data(); } -class MemTableIterator: public Iterator { +class MemTableIterator : public Iterator { public: - explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } + explicit MemTableIterator(MemTable::Table* table) : iter_(table) {} virtual bool Valid() const { return iter_.Valid(); } virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } @@ -68,19 +63,16 @@ class MemTableIterator: public Iterator { private: MemTable::Table::Iterator iter_; - std::string tmp_; // For passing to EncodeKey + std::string tmp_; // For passing to EncodeKey // No copying allowed MemTableIterator(const MemTableIterator&); void operator=(const MemTableIterator&); }; -Iterator* MemTable::NewIterator() { - return new MemTableIterator(&table_); -} +Iterator* MemTable::NewIterator() { return new MemTableIterator(&table_); } -void MemTable::Add(SequenceNumber s, ValueType type, - const Slice& key, +void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key, const Slice& value) { // Format of an entry is concatenation of: // key_size : varint32 of internal_key.size() @@ -90,9 +82,9 @@ void MemTable::Add(SequenceNumber s, ValueType type, size_t key_size = key.size(); size_t val_size = value.size(); size_t internal_key_size = key_size + 8; - const size_t encoded_len = - VarintLength(internal_key_size) + internal_key_size + - VarintLength(val_size) + val_size; + const size_t encoded_len = VarintLength(internal_key_size) + + internal_key_size + VarintLength(val_size) + + val_size; char* buf = arena_.Allocate(encoded_len); char* p = EncodeVarint32(buf, internal_key_size); memcpy(p, key.data(), key_size); @@ -121,10 +113,9 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { // all entries with overly large sequence numbers. const char* entry = iter.key(); uint32_t key_length; - const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length); if (comparator_.comparator.user_comparator()->Compare( - Slice(key_ptr, key_length - 8), - key.user_key()) == 0) { + Slice(key_ptr, key_length - 8), key.user_key()) == 0) { // Correct user key const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); switch (static_cast(tag & 0xff)) { diff --git a/db/memtable.h b/db/memtable.h index f2a6736cf9..ef18bb53f3 100644 --- a/db/memtable.h +++ b/db/memtable.h @@ -6,9 +6,10 @@ #define STORAGE_LEVELDB_DB_MEMTABLE_H_ #include -#include "leveldb/db.h" + #include "db/dbformat.h" #include "db/skiplist.h" +#include "leveldb/db.h" #include "util/arena.h" namespace leveldb { @@ -49,8 +50,7 @@ class MemTable { // Add an entry into memtable that maps key to value at the // specified sequence number and with the specified type. // Typically value will be empty if type==kTypeDeletion. - void Add(SequenceNumber seq, ValueType type, - const Slice& key, + void Add(SequenceNumber seq, ValueType type, const Slice& key, const Slice& value); // If memtable contains a value for key, store it in *value and return true. @@ -64,7 +64,7 @@ class MemTable { struct KeyComparator { const InternalKeyComparator comparator; - explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {} int operator()(const char* a, const char* b) const; }; friend class MemTableIterator; diff --git a/db/recovery_test.cc b/db/recovery_test.cc index 87bd53cab2..547a9591ea 100644 --- a/db/recovery_test.cc +++ b/db/recovery_test.cc @@ -86,15 +86,13 @@ class RecoveryTest { std::string current; ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t)); size_t len = current.size(); - if (len > 0 && current[len-1] == '\n') { + if (len > 0 && current[len - 1] == '\n') { current.resize(len - 1); } return dbname_ + "/" + current; } - std::string LogName(uint64_t number) { - return LogFileName(dbname_, number); - } + std::string LogName(uint64_t number) { return LogFileName(dbname_, number); } size_t DeleteLogFiles() { // Linux allows unlinking open files, but Windows does not. @@ -107,13 +105,9 @@ class RecoveryTest { return logs.size(); } - void DeleteManifestFile() { - ASSERT_OK(env_->DeleteFile(ManifestFileName())); - } + void DeleteManifestFile() { ASSERT_OK(env_->DeleteFile(ManifestFileName())); } - uint64_t FirstLogFile() { - return GetFiles(kLogFile)[0]; - } + uint64_t FirstLogFile() { return GetFiles(kLogFile)[0]; } std::vector GetFiles(FileType t) { std::vector filenames; @@ -129,13 +123,9 @@ class RecoveryTest { return result; } - int NumLogs() { - return GetFiles(kLogFile).size(); - } + int NumLogs() { return GetFiles(kLogFile).size(); } - int NumTables() { - return GetFiles(kTableFile).size(); - } + int NumTables() { return GetFiles(kTableFile).size(); } uint64_t FileSize(const std::string& fname) { uint64_t result; @@ -143,9 +133,7 @@ class RecoveryTest { return result; } - void CompactMemTable() { - dbfull()->TEST_CompactMemTable(); - } + void CompactMemTable() { dbfull()->TEST_CompactMemTable(); } // Directly construct a log file that sets key to val. void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) { @@ -197,7 +185,7 @@ TEST(RecoveryTest, LargeManifestCompacted) { uint64_t len = FileSize(old_manifest); WritableFile* file; ASSERT_OK(env()->NewAppendableFile(old_manifest, &file)); - std::string zeroes(3*1048576 - static_cast(len), 0); + std::string zeroes(3 * 1048576 - static_cast(len), 0); ASSERT_OK(file->Append(zeroes)); ASSERT_OK(file->Flush()); delete file; @@ -270,7 +258,7 @@ TEST(RecoveryTest, MultipleMemTables) { // Force creation of multiple memtables by reducing the write buffer size. Options opt; opt.reuse_logs = true; - opt.write_buffer_size = (kNum*100) / 2; + opt.write_buffer_size = (kNum * 100) / 2; Open(&opt); ASSERT_LE(2, NumTables()); ASSERT_EQ(1, NumLogs()); @@ -289,16 +277,16 @@ TEST(RecoveryTest, MultipleLogFiles) { // Make a bunch of uncompacted log files. uint64_t old_log = FirstLogFile(); - MakeLogFile(old_log+1, 1000, "hello", "world"); - MakeLogFile(old_log+2, 1001, "hi", "there"); - MakeLogFile(old_log+3, 1002, "foo", "bar2"); + MakeLogFile(old_log + 1, 1000, "hello", "world"); + MakeLogFile(old_log + 2, 1001, "hi", "there"); + MakeLogFile(old_log + 3, 1002, "foo", "bar2"); // Recover and check that all log files were processed. Open(); ASSERT_LE(1, NumTables()); ASSERT_EQ(1, NumLogs()); uint64_t new_log = FirstLogFile(); - ASSERT_LE(old_log+3, new_log); + ASSERT_LE(old_log + 3, new_log); ASSERT_EQ("bar2", Get("foo")); ASSERT_EQ("world", Get("hello")); ASSERT_EQ("there", Get("hi")); @@ -316,7 +304,7 @@ TEST(RecoveryTest, MultipleLogFiles) { // Check that introducing an older log file does not cause it to be re-read. Close(); - MakeLogFile(old_log+1, 2000, "hello", "stale write"); + MakeLogFile(old_log + 1, 2000, "hello", "stale write"); Open(); ASSERT_LE(1, NumTables()); ASSERT_EQ(1, NumLogs()); @@ -339,6 +327,4 @@ TEST(RecoveryTest, ManifestMissing) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/repair.cc b/db/repair.cc index df8dcd2c75..d5ecc45e66 100644 --- a/db/repair.cc +++ b/db/repair.cc @@ -84,9 +84,7 @@ class Repairer { "recovered %d files; %llu bytes. " "Some data may have been lost. " "****", - dbname_.c_str(), - static_cast(tables_.size()), - bytes); + dbname_.c_str(), static_cast(tables_.size()), bytes); } return status; } @@ -152,8 +150,7 @@ class Repairer { Status status = ConvertLogToTable(logs_[i]); if (!status.ok()) { Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", - (unsigned long long) logs_[i], - status.ToString().c_str()); + (unsigned long long)logs_[i], status.ToString().c_str()); } ArchiveFile(logname); } @@ -167,8 +164,7 @@ class Repairer { virtual void Corruption(size_t bytes, const Status& s) { // We print error messages for corruption, but continue repairing. Log(info_log, "Log #%llu: dropping %d bytes; %s", - (unsigned long long) lognum, - static_cast(bytes), + (unsigned long long)lognum, static_cast(bytes), s.ToString().c_str()); } }; @@ -190,8 +186,8 @@ class Repairer { // corruptions cause entire commits to be skipped instead of // propagating bad information (like overly large sequence // numbers). - log::Reader reader(lfile, &reporter, false/*do not checksum*/, - 0/*initial_offset*/); + log::Reader reader(lfile, &reporter, false /*do not checksum*/, + 0 /*initial_offset*/); // Read all the records and add to a memtable std::string scratch; @@ -202,8 +198,8 @@ class Repairer { int counter = 0; while (reader.ReadRecord(&record, &scratch)) { if (record.size() < 12) { - reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + reporter.Corruption(record.size(), + Status::Corruption("log record too small")); continue; } WriteBatchInternal::SetContents(&batch, record); @@ -212,8 +208,7 @@ class Repairer { counter += WriteBatchInternal::Count(&batch); } else { Log(options_.info_log, "Log #%llu: ignoring %s", - (unsigned long long) log, - status.ToString().c_str()); + (unsigned long long)log, status.ToString().c_str()); status = Status::OK(); // Keep going with rest of file } } @@ -234,9 +229,7 @@ class Repairer { } } Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", - (unsigned long long) log, - counter, - (unsigned long long) meta.number, + (unsigned long long)log, counter, (unsigned long long)meta.number, status.ToString().c_str()); return status; } @@ -272,8 +265,7 @@ class Repairer { ArchiveFile(TableFileName(dbname_, number)); ArchiveFile(SSTTableFileName(dbname_, number)); Log(options_.info_log, "Table #%llu: dropped: %s", - (unsigned long long) t.meta.number, - status.ToString().c_str()); + (unsigned long long)t.meta.number, status.ToString().c_str()); return; } @@ -287,8 +279,7 @@ class Repairer { Slice key = iter->key(); if (!ParseInternalKey(key, &parsed)) { Log(options_.info_log, "Table #%llu: unparsable key %s", - (unsigned long long) t.meta.number, - EscapeString(key).c_str()); + (unsigned long long)t.meta.number, EscapeString(key).c_str()); continue; } @@ -307,9 +298,7 @@ class Repairer { } delete iter; Log(options_.info_log, "Table #%llu: %d entries %s", - (unsigned long long) t.meta.number, - counter, - status.ToString().c_str()); + (unsigned long long)t.meta.number, counter, status.ToString().c_str()); if (status.ok()) { tables_.push_back(t); @@ -363,7 +352,7 @@ class Repairer { s = env_->RenameFile(copy, orig); if (s.ok()) { Log(options_.info_log, "Table #%llu: %d entries repaired", - (unsigned long long) t.meta.number, counter); + (unsigned long long)t.meta.number, counter); tables_.push_back(t); } } @@ -395,11 +384,11 @@ class Repairer { for (size_t i = 0; i < tables_.size(); i++) { // TODO(opt): separate out into multiple levels const TableInfo& t = tables_[i]; - edit_.AddFile(0, t.meta.number, t.meta.file_size, - t.meta.smallest, t.meta.largest); + edit_.AddFile(0, t.meta.number, t.meta.file_size, t.meta.smallest, + t.meta.largest); } - //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + // fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); { log::Writer log(file); std::string record; @@ -447,8 +436,8 @@ class Repairer { new_file.append("/"); new_file.append((slash == nullptr) ? fname.c_str() : slash + 1); Status s = env_->RenameFile(fname, new_file); - Log(options_.info_log, "Archiving %s: %s\n", - fname.c_str(), s.ToString().c_str()); + Log(options_.info_log, "Archiving %s: %s\n", fname.c_str(), + s.ToString().c_str()); } }; } // namespace diff --git a/db/skiplist.h b/db/skiplist.h index 7ac914bc76..05e57331a9 100644 --- a/db/skiplist.h +++ b/db/skiplist.h @@ -38,7 +38,7 @@ namespace leveldb { class Arena; -template +template class SkipList { private: struct Node; @@ -100,13 +100,13 @@ class SkipList { // Immutable after construction Comparator const compare_; - Arena* const arena_; // Arena used for allocations of nodes + Arena* const arena_; // Arena used for allocations of nodes Node* const head_; // Modified only by Insert(). Read racily by readers, but stale // values are ok. - std::atomic max_height_; // Height of the entire list + std::atomic max_height_; // Height of the entire list inline int GetMaxHeight() const { return max_height_.load(std::memory_order_relaxed); @@ -143,9 +143,9 @@ class SkipList { }; // Implementation details follow -template +template struct SkipList::Node { - explicit Node(const Key& k) : key(k) { } + explicit Node(const Key& k) : key(k) {} Key const key; @@ -179,38 +179,38 @@ struct SkipList::Node { std::atomic next_[1]; }; -template -typename SkipList::Node* -SkipList::NewNode(const Key& key, int height) { +template +typename SkipList::Node* SkipList::NewNode( + const Key& key, int height) { char* const node_memory = arena_->AllocateAligned( sizeof(Node) + sizeof(std::atomic) * (height - 1)); return new (node_memory) Node(key); } -template +template inline SkipList::Iterator::Iterator(const SkipList* list) { list_ = list; node_ = nullptr; } -template +template inline bool SkipList::Iterator::Valid() const { return node_ != nullptr; } -template +template inline const Key& SkipList::Iterator::key() const { assert(Valid()); return node_->key; } -template +template inline void SkipList::Iterator::Next() { assert(Valid()); node_ = node_->Next(0); } -template +template inline void SkipList::Iterator::Prev() { // Instead of using explicit "prev" links, we just search for the // last node that falls before key. @@ -221,17 +221,17 @@ inline void SkipList::Iterator::Prev() { } } -template +template inline void SkipList::Iterator::Seek(const Key& target) { node_ = list_->FindGreaterOrEqual(target, nullptr); } -template +template inline void SkipList::Iterator::SeekToFirst() { node_ = list_->head_->Next(0); } -template +template inline void SkipList::Iterator::SeekToLast() { node_ = list_->FindLast(); if (node_ == list_->head_) { @@ -239,7 +239,7 @@ inline void SkipList::Iterator::SeekToLast() { } } -template +template int SkipList::RandomHeight() { // Increase height with probability 1 in kBranching static const unsigned int kBranching = 4; @@ -252,13 +252,13 @@ int SkipList::RandomHeight() { return height; } -template +template bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { // null n is considered infinite return (n != nullptr) && (compare_(n->key, key) < 0); } -template +template typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) const { @@ -281,7 +281,7 @@ SkipList::FindGreaterOrEqual(const Key& key, } } -template +template typename SkipList::Node* SkipList::FindLessThan(const Key& key) const { Node* x = head_; @@ -302,7 +302,7 @@ SkipList::FindLessThan(const Key& key) const { } } -template +template typename SkipList::Node* SkipList::FindLast() const { Node* x = head_; @@ -322,7 +322,7 @@ typename SkipList::Node* SkipList::FindLast() } } -template +template SkipList::SkipList(Comparator cmp, Arena* arena) : compare_(cmp), arena_(arena), @@ -334,7 +334,7 @@ SkipList::SkipList(Comparator cmp, Arena* arena) } } -template +template void SkipList::Insert(const Key& key) { // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() // here since Insert() is externally synchronized. @@ -368,7 +368,7 @@ void SkipList::Insert(const Key& key) { } } -template +template bool SkipList::Contains(const Key& key) const { Node* x = FindGreaterOrEqual(key, nullptr); if (x != nullptr && Equal(key, x->key)) { diff --git a/db/skiplist_test.cc b/db/skiplist_test.cc index 38c1941dc9..9fa2d96829 100644 --- a/db/skiplist_test.cc +++ b/db/skiplist_test.cc @@ -31,7 +31,7 @@ struct Comparator { } }; -class SkipTest { }; +class SkipTest {}; TEST(SkipTest, Empty) { Arena arena; @@ -117,8 +117,7 @@ TEST(SkipTest, InsertAndLookup) { // Compare against model iterator for (std::set::reverse_iterator model_iter = keys.rbegin(); - model_iter != keys.rend(); - ++model_iter) { + model_iter != keys.rend(); ++model_iter) { ASSERT_TRUE(iter.Valid()); ASSERT_EQ(*model_iter, iter.key()); iter.Prev(); @@ -160,12 +159,12 @@ class ConcurrentTest { static uint64_t hash(Key key) { return key & 0xff; } static uint64_t HashNumbers(uint64_t k, uint64_t g) { - uint64_t data[2] = { k, g }; + uint64_t data[2] = {k, g}; return Hash(reinterpret_cast(data), sizeof(data), 0); } static Key MakeKey(uint64_t k, uint64_t g) { - assert(sizeof(Key) == sizeof(uint64_t)); + static_assert(sizeof(Key) == sizeof(uint64_t), ""); assert(k <= K); // We sometimes pass K to seek to the end of the skiplist assert(g <= 0xffffffffu); return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); @@ -195,9 +194,7 @@ class ConcurrentTest { void Set(int k, int v) { generation[k].store(v, std::memory_order_release); } - int Get(int k) { - return generation[k].load(std::memory_order_acquire); - } + int Get(int k) { return generation[k].load(std::memory_order_acquire); } State() { for (int k = 0; k < K; k++) { @@ -216,7 +213,7 @@ class ConcurrentTest { SkipList list_; public: - ConcurrentTest() : list_(Comparator(), &arena_) { } + ConcurrentTest() : list_(Comparator(), &arena_) {} // REQUIRES: External synchronization void WriteStep(Random* rnd) { @@ -255,11 +252,9 @@ class ConcurrentTest { // Note that generation 0 is never inserted, so it is ok if // <*,0,*> is missing. ASSERT_TRUE((gen(pos) == 0) || - (gen(pos) > static_cast(initial_state.Get(key(pos)))) - ) << "key: " << key(pos) - << "; gen: " << gen(pos) - << "; initgen: " - << initial_state.Get(key(pos)); + (gen(pos) > static_cast(initial_state.Get(key(pos))))) + << "key: " << key(pos) << "; gen: " << gen(pos) + << "; initgen: " << initial_state.Get(key(pos)); // Advance to next key in the valid key space if (key(pos) < key(current)) { @@ -305,17 +300,10 @@ class TestState { int seed_; std::atomic quit_flag_; - enum ReaderState { - STARTING, - RUNNING, - DONE - }; + enum ReaderState { STARTING, RUNNING, DONE }; explicit TestState(int s) - : seed_(s), - quit_flag_(false), - state_(STARTING), - state_cv_(&mu_) {} + : seed_(s), quit_flag_(false), state_(STARTING), state_cv_(&mu_) {} void Wait(ReaderState s) LOCKS_EXCLUDED(mu_) { mu_.Lock(); @@ -378,6 +366,4 @@ TEST(SkipTest, Concurrent5) { RunConcurrent(5); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/snapshot.h b/db/snapshot.h index c43d9f900b..9f1d66491d 100644 --- a/db/snapshot.h +++ b/db/snapshot.h @@ -44,8 +44,14 @@ class SnapshotList { } bool empty() const { return head_.next_ == &head_; } - SnapshotImpl* oldest() const { assert(!empty()); return head_.next_; } - SnapshotImpl* newest() const { assert(!empty()); return head_.prev_; } + SnapshotImpl* oldest() const { + assert(!empty()); + return head_.next_; + } + SnapshotImpl* newest() const { + assert(!empty()); + return head_.prev_; + } // Creates a SnapshotImpl and appends it to the end of the list. SnapshotImpl* New(SequenceNumber sequence_number) { diff --git a/db/table_cache.cc b/db/table_cache.cc index 7226d3b7a7..73f05fd7b1 100644 --- a/db/table_cache.cc +++ b/db/table_cache.cc @@ -29,18 +29,14 @@ static void UnrefEntry(void* arg1, void* arg2) { cache->Release(h); } -TableCache::TableCache(const std::string& dbname, - const Options& options, +TableCache::TableCache(const std::string& dbname, const Options& options, int entries) : env_(options.env), dbname_(dbname), options_(options), - cache_(NewLRUCache(entries)) { -} + cache_(NewLRUCache(entries)) {} -TableCache::~TableCache() { - delete cache_; -} +TableCache::~TableCache() { delete cache_; } Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle** handle) { @@ -80,8 +76,7 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, } Iterator* TableCache::NewIterator(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, + uint64_t file_number, uint64_t file_size, Table** tableptr) { if (tableptr != nullptr) { *tableptr = nullptr; @@ -102,17 +97,15 @@ Iterator* TableCache::NewIterator(const ReadOptions& options, return result; } -Status TableCache::Get(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - const Slice& k, - void* arg, - void (*saver)(void*, const Slice&, const Slice&)) { +Status TableCache::Get(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, const Slice& k, void* arg, + void (*handle_result)(void*, const Slice&, + const Slice&)) { Cache::Handle* handle = nullptr; Status s = FindTable(file_number, file_size, &handle); if (s.ok()) { Table* t = reinterpret_cast(cache_->Value(handle))->table; - s = t->InternalGet(options, k, arg, saver); + s = t->InternalGet(options, k, arg, handle_result); cache_->Release(handle); } return s; diff --git a/db/table_cache.h b/db/table_cache.h index ae8bee5e6d..21ae92d27b 100644 --- a/db/table_cache.h +++ b/db/table_cache.h @@ -7,8 +7,10 @@ #ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ #define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ -#include #include + +#include + #include "db/dbformat.h" #include "leveldb/cache.h" #include "leveldb/table.h" @@ -30,18 +32,13 @@ class TableCache { // underlies the returned iterator. The returned "*tableptr" object is owned // by the cache and should not be deleted, and is valid for as long as the // returned iterator is live. - Iterator* NewIterator(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - Table** tableptr = nullptr); + Iterator* NewIterator(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, Table** tableptr = nullptr); // If a seek to internal key "k" in specified file finds an entry, // call (*handle_result)(arg, found_key, found_value). - Status Get(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - const Slice& k, - void* arg, + Status Get(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, const Slice& k, void* arg, void (*handle_result)(void*, const Slice&, const Slice&)); // Evict any entry for the specified file number diff --git a/db/version_edit.cc b/db/version_edit.cc index b7a366d60e..44a4d02230 100644 --- a/db/version_edit.cc +++ b/db/version_edit.cc @@ -12,15 +12,15 @@ namespace leveldb { // Tag numbers for serialized VersionEdit. These numbers are written to // disk and should not be changed. enum Tag { - kComparator = 1, - kLogNumber = 2, - kNextFileNumber = 3, - kLastSequence = 4, - kCompactPointer = 5, - kDeletedFile = 6, - kNewFile = 7, + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, // 8 was used for large value refs - kPrevLogNumber = 9 + kPrevLogNumber = 9 }; void VersionEdit::Clear() { @@ -67,8 +67,7 @@ void VersionEdit::EncodeTo(std::string* dst) const { } for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); - ++iter) { + iter != deleted_files_.end(); ++iter) { PutVarint32(dst, kDeletedFile); PutVarint32(dst, iter->first); // level PutVarint64(dst, iter->second); // file number @@ -97,8 +96,7 @@ static bool GetInternalKey(Slice* input, InternalKey* dst) { static bool GetLevel(Slice* input, int* level) { uint32_t v; - if (GetVarint32(input, &v) && - v < config::kNumLevels) { + if (GetVarint32(input, &v) && v < config::kNumLevels) { *level = v; return true; } else { @@ -163,8 +161,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kCompactPointer: - if (GetLevel(&input, &level) && - GetInternalKey(&input, &key)) { + if (GetLevel(&input, &level) && GetInternalKey(&input, &key)) { compact_pointers_.push_back(std::make_pair(level, key)); } else { msg = "compaction pointer"; @@ -172,8 +169,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kDeletedFile: - if (GetLevel(&input, &level) && - GetVarint64(&input, &number)) { + if (GetLevel(&input, &level) && GetVarint64(&input, &number)) { deleted_files_.insert(std::make_pair(level, number)); } else { msg = "deleted file"; @@ -181,8 +177,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kNewFile: - if (GetLevel(&input, &level) && - GetVarint64(&input, &f.number) && + if (GetLevel(&input, &level) && GetVarint64(&input, &f.number) && GetVarint64(&input, &f.file_size) && GetInternalKey(&input, &f.smallest) && GetInternalKey(&input, &f.largest)) { @@ -239,8 +234,7 @@ std::string VersionEdit::DebugString() const { r.append(compact_pointers_[i].second.DebugString()); } for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); - ++iter) { + iter != deleted_files_.end(); ++iter) { r.append("\n DeleteFile: "); AppendNumberTo(&r, iter->first); r.append(" "); diff --git a/db/version_edit.h b/db/version_edit.h index eaef77b327..3daf4ef6f6 100644 --- a/db/version_edit.h +++ b/db/version_edit.h @@ -8,6 +8,7 @@ #include #include #include + #include "db/dbformat.h" namespace leveldb { @@ -16,19 +17,19 @@ class VersionSet; struct FileMetaData { int refs; - int allowed_seeks; // Seeks allowed until compaction + int allowed_seeks; // Seeks allowed until compaction uint64_t number; - uint64_t file_size; // File size in bytes - InternalKey smallest; // Smallest internal key served by table - InternalKey largest; // Largest internal key served by table + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table - FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {} }; class VersionEdit { public: VersionEdit() { Clear(); } - ~VersionEdit() { } + ~VersionEdit() {} void Clear(); @@ -59,10 +60,8 @@ class VersionEdit { // Add the specified file at the specified number. // REQUIRES: This version has not been saved (see VersionSet::SaveTo) // REQUIRES: "smallest" and "largest" are smallest and largest keys in file - void AddFile(int level, uint64_t file, - uint64_t file_size, - const InternalKey& smallest, - const InternalKey& largest) { + void AddFile(int level, uint64_t file, uint64_t file_size, + const InternalKey& smallest, const InternalKey& largest) { FileMetaData f; f.number = file; f.file_size = file_size; @@ -84,7 +83,7 @@ class VersionEdit { private: friend class VersionSet; - typedef std::set< std::pair > DeletedFileSet; + typedef std::set > DeletedFileSet; std::string comparator_; uint64_t log_number_; @@ -97,9 +96,9 @@ class VersionEdit { bool has_next_file_number_; bool has_last_sequence_; - std::vector< std::pair > compact_pointers_; + std::vector > compact_pointers_; DeletedFileSet deleted_files_; - std::vector< std::pair > new_files_; + std::vector > new_files_; }; } // namespace leveldb diff --git a/db/version_edit_test.cc b/db/version_edit_test.cc index 280310b49d..0b7cda8854 100644 --- a/db/version_edit_test.cc +++ b/db/version_edit_test.cc @@ -17,7 +17,7 @@ static void TestEncodeDecode(const VersionEdit& edit) { ASSERT_EQ(encoded, encoded2); } -class VersionEditTest { }; +class VersionEditTest {}; TEST(VersionEditTest, EncodeDecode) { static const uint64_t kBig = 1ull << 50; @@ -41,6 +41,4 @@ TEST(VersionEditTest, EncodeDecode) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/db/version_set.cc b/db/version_set.cc index 56493acd29..96a92cce2c 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -4,8 +4,10 @@ #include "db/version_set.h" -#include #include + +#include + #include "db/filename.h" #include "db/log_reader.h" #include "db/log_writer.h" @@ -84,8 +86,7 @@ Version::~Version() { } int FindFile(const InternalKeyComparator& icmp, - const std::vector& files, - const Slice& key) { + const std::vector& files, const Slice& key) { uint32_t left = 0; uint32_t right = files.size(); while (left < right) { @@ -104,26 +105,25 @@ int FindFile(const InternalKeyComparator& icmp, return right; } -static bool AfterFile(const Comparator* ucmp, - const Slice* user_key, const FileMetaData* f) { +static bool AfterFile(const Comparator* ucmp, const Slice* user_key, + const FileMetaData* f) { // null user_key occurs before all keys and is therefore never after *f return (user_key != nullptr && ucmp->Compare(*user_key, f->largest.user_key()) > 0); } -static bool BeforeFile(const Comparator* ucmp, - const Slice* user_key, const FileMetaData* f) { +static bool BeforeFile(const Comparator* ucmp, const Slice* user_key, + const FileMetaData* f) { // null user_key occurs after all keys and is therefore never before *f return (user_key != nullptr && ucmp->Compare(*user_key, f->smallest.user_key()) < 0); } -bool SomeFileOverlapsRange( - const InternalKeyComparator& icmp, - bool disjoint_sorted_files, - const std::vector& files, - const Slice* smallest_user_key, - const Slice* largest_user_key) { +bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { const Comparator* ucmp = icmp.user_comparator(); if (!disjoint_sorted_files) { // Need to check against all files @@ -143,7 +143,8 @@ bool SomeFileOverlapsRange( uint32_t index = 0; if (smallest_user_key != nullptr) { // Find the earliest possible internal key for smallest_user_key - InternalKey small_key(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); + InternalKey small_key(*smallest_user_key, kMaxSequenceNumber, + kValueTypeForSeek); index = FindFile(icmp, files, small_key.Encode()); } @@ -164,13 +165,9 @@ class Version::LevelFileNumIterator : public Iterator { public: LevelFileNumIterator(const InternalKeyComparator& icmp, const std::vector* flist) - : icmp_(icmp), - flist_(flist), - index_(flist->size()) { // Marks as invalid - } - virtual bool Valid() const { - return index_ < flist_->size(); + : icmp_(icmp), flist_(flist), index_(flist->size()) { // Marks as invalid } + virtual bool Valid() const { return index_ < flist_->size(); } virtual void Seek(const Slice& target) { index_ = FindFile(icmp_, *flist_, target); } @@ -197,10 +194,11 @@ class Version::LevelFileNumIterator : public Iterator { Slice value() const { assert(Valid()); EncodeFixed64(value_buf_, (*flist_)[index_]->number); - EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + EncodeFixed64(value_buf_ + 8, (*flist_)[index_]->file_size); return Slice(value_buf_, sizeof(value_buf_)); } virtual Status status() const { return Status::OK(); } + private: const InternalKeyComparator icmp_; const std::vector* const flist_; @@ -210,16 +208,14 @@ class Version::LevelFileNumIterator : public Iterator { mutable char value_buf_[16]; }; -static Iterator* GetFileIterator(void* arg, - const ReadOptions& options, +static Iterator* GetFileIterator(void* arg, const ReadOptions& options, const Slice& file_value) { TableCache* cache = reinterpret_cast(arg); if (file_value.size() != 16) { return NewErrorIterator( Status::Corruption("FileReader invoked with unexpected value")); } else { - return cache->NewIterator(options, - DecodeFixed64(file_value.data()), + return cache->NewIterator(options, DecodeFixed64(file_value.data()), DecodeFixed64(file_value.data() + 8)); } } @@ -227,17 +223,16 @@ static Iterator* GetFileIterator(void* arg, Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, int level) const { return NewTwoLevelIterator( - new LevelFileNumIterator(vset_->icmp_, &files_[level]), - &GetFileIterator, vset_->table_cache_, options); + new LevelFileNumIterator(vset_->icmp_, &files_[level]), &GetFileIterator, + vset_->table_cache_, options); } void Version::AddIterators(const ReadOptions& options, std::vector* iters) { // Merge all level zero files together since they may overlap for (size_t i = 0; i < files_[0].size(); i++) { - iters->push_back( - vset_->table_cache_->NewIterator( - options, files_[0][i]->number, files_[0][i]->file_size)); + iters->push_back(vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); } // For levels > 0, we can use a concatenating iterator that sequentially @@ -264,7 +259,7 @@ struct Saver { Slice user_key; std::string* value; }; -} +} // namespace static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { Saver* s = reinterpret_cast(arg); ParsedInternalKey parsed_key; @@ -284,8 +279,7 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) { return a->number > b->number; } -void Version::ForEachOverlapping(Slice user_key, Slice internal_key, - void* arg, +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg, bool (*func)(void*, int, FileMetaData*)) { // TODO(sanjay): Change Version::Get() to use this function. const Comparator* ucmp = vset_->icmp_.user_comparator(); @@ -329,10 +323,8 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key, } } -Status Version::Get(const ReadOptions& options, - const LookupKey& k, - std::string* value, - GetStats* stats) { +Status Version::Get(const ReadOptions& options, const LookupKey& k, + std::string* value, GetStats* stats) { Slice ikey = k.internal_key(); Slice user_key = k.user_key(); const Comparator* ucmp = vset_->icmp_.user_comparator(); @@ -405,14 +397,14 @@ Status Version::Get(const ReadOptions& options, saver.ucmp = ucmp; saver.user_key = user_key; saver.value = value; - s = vset_->table_cache_->Get(options, f->number, f->file_size, - ikey, &saver, SaveValue); + s = vset_->table_cache_->Get(options, f->number, f->file_size, ikey, + &saver, SaveValue); if (!s.ok()) { return s; } switch (saver.state) { case kNotFound: - break; // Keep searching in other files + break; // Keep searching in other files case kFound: return s; case kDeleted: @@ -479,9 +471,7 @@ bool Version::RecordReadSample(Slice internal_key) { return false; } -void Version::Ref() { - ++refs_; -} +void Version::Ref() { ++refs_; } void Version::Unref() { assert(this != &vset_->dummy_versions_); @@ -492,16 +482,14 @@ void Version::Unref() { } } -bool Version::OverlapInLevel(int level, - const Slice* smallest_user_key, +bool Version::OverlapInLevel(int level, const Slice* smallest_user_key, const Slice* largest_user_key) { return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], smallest_user_key, largest_user_key); } -int Version::PickLevelForMemTableOutput( - const Slice& smallest_user_key, - const Slice& largest_user_key) { +int Version::PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key) { int level = 0; if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { // Push to next level if there is no overlap in next level, @@ -528,11 +516,9 @@ int Version::PickLevelForMemTableOutput( } // Store in "*inputs" all files in "level" that overlap [begin,end] -void Version::GetOverlappingInputs( - int level, - const InternalKey* begin, - const InternalKey* end, - std::vector* inputs) { +void Version::GetOverlappingInputs(int level, const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { assert(level >= 0); assert(level < config::kNumLevels); inputs->clear(); @@ -544,7 +530,7 @@ void Version::GetOverlappingInputs( user_end = end->user_key(); } const Comparator* user_cmp = vset_->icmp_.user_comparator(); - for (size_t i = 0; i < files_[level].size(); ) { + for (size_t i = 0; i < files_[level].size();) { FileMetaData* f = files_[level][i++]; const Slice file_start = f->smallest.user_key(); const Slice file_limit = f->largest.user_key(); @@ -561,8 +547,8 @@ void Version::GetOverlappingInputs( user_begin = file_start; inputs->clear(); i = 0; - } else if (end != nullptr && user_cmp->Compare(file_limit, - user_end) > 0) { + } else if (end != nullptr && + user_cmp->Compare(file_limit, user_end) > 0) { user_end = file_limit; inputs->clear(); i = 0; @@ -630,9 +616,7 @@ class VersionSet::Builder { public: // Initialize a builder with the files from *base and other info from *vset - Builder(VersionSet* vset, Version* base) - : vset_(vset), - base_(base) { + Builder(VersionSet* vset, Version* base) : vset_(vset), base_(base) { base_->Ref(); BySmallestKey cmp; cmp.internal_comparator = &vset_->icmp_; @@ -646,8 +630,8 @@ class VersionSet::Builder { const FileSet* added = levels_[level].added_files; std::vector to_unref; to_unref.reserve(added->size()); - for (FileSet::const_iterator it = added->begin(); - it != added->end(); ++it) { + for (FileSet::const_iterator it = added->begin(); it != added->end(); + ++it) { to_unref.push_back(*it); } delete added; @@ -674,8 +658,7 @@ class VersionSet::Builder { // Delete files const VersionEdit::DeletedFileSet& del = edit->deleted_files_; for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); - iter != del.end(); - ++iter) { + iter != del.end(); ++iter) { const int level = iter->first; const uint64_t number = iter->second; levels_[level].deleted_files.insert(number); @@ -721,13 +704,11 @@ class VersionSet::Builder { const FileSet* added = levels_[level].added_files; v->files_[level].reserve(base_files.size() + added->size()); for (FileSet::const_iterator added_iter = added->begin(); - added_iter != added->end(); - ++added_iter) { + added_iter != added->end(); ++added_iter) { // Add all smaller files listed in base_ - for (std::vector::const_iterator bpos - = std::upper_bound(base_iter, base_end, *added_iter, cmp); - base_iter != bpos; - ++base_iter) { + for (std::vector::const_iterator bpos = + std::upper_bound(base_iter, base_end, *added_iter, cmp); + base_iter != bpos; ++base_iter) { MaybeAddFile(v, level, *base_iter); } @@ -743,7 +724,7 @@ class VersionSet::Builder { // Make sure there is no overlap in levels > 0 if (level > 0) { for (uint32_t i = 1; i < v->files_[level].size(); i++) { - const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& prev_end = v->files_[level][i - 1]->largest; const InternalKey& this_begin = v->files_[level][i]->smallest; if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", @@ -764,7 +745,7 @@ class VersionSet::Builder { std::vector* files = &v->files_[level]; if (level > 0 && !files->empty()) { // Must not overlap - assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + assert(vset_->icmp_.Compare((*files)[files->size() - 1]->largest, f->smallest) < 0); } f->refs++; @@ -773,8 +754,7 @@ class VersionSet::Builder { } }; -VersionSet::VersionSet(const std::string& dbname, - const Options* options, +VersionSet::VersionSet(const std::string& dbname, const Options* options, TableCache* table_cache, const InternalKeyComparator* cmp) : env_(options->env), @@ -903,7 +883,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { return s; } -Status VersionSet::Recover(bool *save_manifest) { +Status VersionSet::Recover(bool* save_manifest) { struct LogReporter : public log::Reader::Reporter { Status* status; virtual void Corruption(size_t bytes, const Status& s) { @@ -917,7 +897,7 @@ Status VersionSet::Recover(bool *save_manifest) { if (!s.ok()) { return s; } - if (current.empty() || current[current.size()-1] != '\n') { + if (current.empty() || current[current.size() - 1] != '\n') { return Status::Corruption("CURRENT file does not end with newline"); } current.resize(current.size() - 1); @@ -927,8 +907,8 @@ Status VersionSet::Recover(bool *save_manifest) { s = env_->NewSequentialFile(dscname, &file); if (!s.ok()) { if (s.IsNotFound()) { - return Status::Corruption( - "CURRENT points to a non-existent file", s.ToString()); + return Status::Corruption("CURRENT points to a non-existent file", + s.ToString()); } return s; } @@ -946,7 +926,8 @@ Status VersionSet::Recover(bool *save_manifest) { { LogReporter reporter; reporter.status = &s; - log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + log::Reader reader(file, &reporter, true /*checksum*/, + 0 /*initial_offset*/); Slice record; std::string scratch; while (reader.ReadRecord(&record, &scratch) && s.ok()) { @@ -1071,7 +1052,7 @@ void VersionSet::Finalize(Version* v) { int best_level = -1; double best_score = -1; - for (int level = 0; level < config::kNumLevels-1; level++) { + for (int level = 0; level < config::kNumLevels - 1; level++) { double score; if (level == 0) { // We treat level-0 specially by bounding the number of files @@ -1086,7 +1067,7 @@ void VersionSet::Finalize(Version* v) { // setting, or very high compression ratios, or lots of // overwrites/deletions). score = v->files_[level].size() / - static_cast(config::kL0_CompactionTrigger); + static_cast(config::kL0_CompactionTrigger); } else { // Compute the ratio of current size to size limit. const uint64_t level_bytes = TotalFileSize(v->files_[level]); @@ -1142,16 +1123,12 @@ int VersionSet::NumLevelFiles(int level) const { const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { // Update code if kNumLevels changes - assert(config::kNumLevels == 7); + static_assert(config::kNumLevels == 7, ""); snprintf(scratch->buffer, sizeof(scratch->buffer), - "files[ %d %d %d %d %d %d %d ]", - int(current_->files_[0].size()), - int(current_->files_[1].size()), - int(current_->files_[2].size()), - int(current_->files_[3].size()), - int(current_->files_[4].size()), - int(current_->files_[5].size()), - int(current_->files_[6].size())); + "files[ %d %d %d %d %d %d %d ]", int(current_->files_[0].size()), + int(current_->files_[1].size()), int(current_->files_[2].size()), + int(current_->files_[3].size()), int(current_->files_[4].size()), + int(current_->files_[5].size()), int(current_->files_[6].size())); return scratch->buffer; } @@ -1188,8 +1165,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { } void VersionSet::AddLiveFiles(std::set* live) { - for (Version* v = dummy_versions_.next_; - v != &dummy_versions_; + for (Version* v = dummy_versions_.next_; v != &dummy_versions_; v = v->next_) { for (int level = 0; level < config::kNumLevels; level++) { const std::vector& files = v->files_[level]; @@ -1212,7 +1188,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() { for (int level = 1; level < config::kNumLevels - 1; level++) { for (size_t i = 0; i < current_->files_[level].size(); i++) { const FileMetaData* f = current_->files_[level][i]; - current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + current_->GetOverlappingInputs(level + 1, &f->smallest, &f->largest, &overlaps); const int64_t sum = TotalFileSize(overlaps); if (sum > result) { @@ -1227,8 +1203,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() { // *smallest, *largest. // REQUIRES: inputs is not empty void VersionSet::GetRange(const std::vector& inputs, - InternalKey* smallest, - InternalKey* largest) { + InternalKey* smallest, InternalKey* largest) { assert(!inputs.empty()); smallest->Clear(); largest->Clear(); @@ -1253,8 +1228,7 @@ void VersionSet::GetRange(const std::vector& inputs, // REQUIRES: inputs is not empty void VersionSet::GetRange2(const std::vector& inputs1, const std::vector& inputs2, - InternalKey* smallest, - InternalKey* largest) { + InternalKey* smallest, InternalKey* largest) { std::vector all = inputs1; all.insert(all.end(), inputs2.begin(), inputs2.end()); GetRange(all, smallest, largest); @@ -1276,8 +1250,8 @@ Iterator* VersionSet::MakeInputIterator(Compaction* c) { if (c->level() + which == 0) { const std::vector& files = c->inputs_[which]; for (size_t i = 0; i < files.size(); i++) { - list[num++] = table_cache_->NewIterator( - options, files[i]->number, files[i]->file_size); + list[num++] = table_cache_->NewIterator(options, files[i]->number, + files[i]->file_size); } } else { // Create concatenating iterator for the files from this level @@ -1304,7 +1278,7 @@ Compaction* VersionSet::PickCompaction() { if (size_compaction) { level = current_->compaction_level_; assert(level >= 0); - assert(level+1 < config::kNumLevels); + assert(level + 1 < config::kNumLevels); c = new Compaction(options_, level); // Pick the first file that comes after compact_pointer_[level] @@ -1433,7 +1407,8 @@ void VersionSet::SetupOtherInputs(Compaction* c) { AddBoundaryInputs(icmp_, current_->files_[level], &c->inputs_[0]); GetRange(c->inputs_[0], &smallest, &largest); - current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + current_->GetOverlappingInputs(level + 1, &smallest, &largest, + &c->inputs_[1]); // Get entire range covered by compaction InternalKey all_start, all_limit; @@ -1454,18 +1429,14 @@ void VersionSet::SetupOtherInputs(Compaction* c) { InternalKey new_start, new_limit; GetRange(expanded0, &new_start, &new_limit); std::vector expanded1; - current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + current_->GetOverlappingInputs(level + 1, &new_start, &new_limit, &expanded1); if (expanded1.size() == c->inputs_[1].size()) { Log(options_->info_log, "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", - level, - int(c->inputs_[0].size()), - int(c->inputs_[1].size()), - long(inputs0_size), long(inputs1_size), - int(expanded0.size()), - int(expanded1.size()), - long(expanded0_size), long(inputs1_size)); + level, int(c->inputs_[0].size()), int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), int(expanded0.size()), + int(expanded1.size()), long(expanded0_size), long(inputs1_size)); smallest = new_start; largest = new_limit; c->inputs_[0] = expanded0; @@ -1490,10 +1461,8 @@ void VersionSet::SetupOtherInputs(Compaction* c) { c->edit_.SetCompactPointer(level, largest); } -Compaction* VersionSet::CompactRange( - int level, - const InternalKey* begin, - const InternalKey* end) { +Compaction* VersionSet::CompactRange(int level, const InternalKey* begin, + const InternalKey* end) { std::vector inputs; current_->GetOverlappingInputs(level, begin, end, &inputs); if (inputs.empty()) { @@ -1566,7 +1535,7 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) { const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { const std::vector& files = input_version_->files_[lvl]; - for (; level_ptrs_[lvl] < files.size(); ) { + for (; level_ptrs_[lvl] < files.size();) { FileMetaData* f = files[level_ptrs_[lvl]]; if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { // We've advanced far enough @@ -1587,8 +1556,9 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) { // Scan to find earliest grandparent file that contains key. const InternalKeyComparator* icmp = &vset->icmp_; while (grandparent_index_ < grandparents_.size() && - icmp->Compare(internal_key, - grandparents_[grandparent_index_]->largest.Encode()) > 0) { + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > + 0) { if (seen_key_) { overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; } diff --git a/db/version_set.h b/db/version_set.h index 0beae4dd00..334ebd9cc4 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -18,6 +18,7 @@ #include #include #include + #include "db/dbformat.h" #include "db/version_edit.h" #include "port/port.h" @@ -25,7 +26,9 @@ namespace leveldb { -namespace log { class Writer; } +namespace log { +class Writer; +} class Compaction; class Iterator; @@ -40,8 +43,7 @@ class WritableFile; // Return files.size() if there is no such file. // REQUIRES: "files" contains a sorted list of non-overlapping files. int FindFile(const InternalKeyComparator& icmp, - const std::vector& files, - const Slice& key); + const std::vector& files, const Slice& key); // Returns true iff some file in "files" overlaps the user key range // [*smallest,*largest]. @@ -90,16 +92,15 @@ class Version { void GetOverlappingInputs( int level, - const InternalKey* begin, // nullptr means before all keys - const InternalKey* end, // nullptr means after all keys + const InternalKey* begin, // nullptr means before all keys + const InternalKey* end, // nullptr means after all keys std::vector* inputs); // Returns true iff some file in the specified level overlaps // some part of [*smallest_user_key,*largest_user_key]. // smallest_user_key==nullptr represents a key smaller than all the DB's keys. // largest_user_key==nullptr represents a key largest than all the DB's keys. - bool OverlapInLevel(int level, - const Slice* smallest_user_key, + bool OverlapInLevel(int level, const Slice* smallest_user_key, const Slice* largest_user_key); // Return the level at which we should place a new memtable compaction @@ -124,14 +125,13 @@ class Version { // false, makes no more calls. // // REQUIRES: user portion of internal_key == user_key. - void ForEachOverlapping(Slice user_key, Slice internal_key, - void* arg, + void ForEachOverlapping(Slice user_key, Slice internal_key, void* arg, bool (*func)(void*, int, FileMetaData*)); - VersionSet* vset_; // VersionSet to which this Version belongs - Version* next_; // Next version in linked list - Version* prev_; // Previous version in linked list - int refs_; // Number of live refs to this version + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version // List of files per level std::vector files_[config::kNumLevels]; @@ -147,12 +147,14 @@ class Version { int compaction_level_; explicit Version(VersionSet* vset) - : vset_(vset), next_(this), prev_(this), refs_(0), + : vset_(vset), + next_(this), + prev_(this), + refs_(0), file_to_compact_(nullptr), file_to_compact_level_(-1), compaction_score_(-1), - compaction_level_(-1) { - } + compaction_level_(-1) {} ~Version(); @@ -163,10 +165,8 @@ class Version { class VersionSet { public: - VersionSet(const std::string& dbname, - const Options* options, - TableCache* table_cache, - const InternalKeyComparator*); + VersionSet(const std::string& dbname, const Options* options, + TableCache* table_cache, const InternalKeyComparator*); ~VersionSet(); // Apply *edit to the current version to form a new descriptor that @@ -178,7 +178,7 @@ class VersionSet { EXCLUSIVE_LOCKS_REQUIRED(mu); // Recover the last saved descriptor from persistent storage. - Status Recover(bool *save_manifest); + Status Recover(bool* save_manifest); // Return the current version. Version* current() const { return current_; } @@ -233,10 +233,8 @@ class VersionSet { // the specified level. Returns nullptr if there is nothing in that // level that overlaps the specified range. Caller should delete // the result. - Compaction* CompactRange( - int level, - const InternalKey* begin, - const InternalKey* end); + Compaction* CompactRange(int level, const InternalKey* begin, + const InternalKey* end); // Return the maximum overlapping data (in bytes) at next level for any // file at a level >= 1. @@ -277,14 +275,12 @@ class VersionSet { void Finalize(Version* v); - void GetRange(const std::vector& inputs, - InternalKey* smallest, + void GetRange(const std::vector& inputs, InternalKey* smallest, InternalKey* largest); void GetRange2(const std::vector& inputs1, const std::vector& inputs2, - InternalKey* smallest, - InternalKey* largest); + InternalKey* smallest, InternalKey* largest); void SetupOtherInputs(Compaction* c); @@ -373,7 +369,7 @@ class Compaction { VersionEdit edit_; // Each compaction reads inputs from "level_" and "level_+1" - std::vector inputs_[2]; // The two sets of inputs + std::vector inputs_[2]; // The two sets of inputs // State used to check for number of overlapping grandparent files // (parent == level_ + 1, grandparent == level_ + 2) diff --git a/db/version_set_test.cc b/db/version_set_test.cc index b32e2e5e20..43b51d8051 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -14,7 +14,7 @@ class FindFileTest { std::vector files_; bool disjoint_sorted_files_; - FindFileTest() : disjoint_sorted_files_(true) { } + FindFileTest() : disjoint_sorted_files_(true) {} ~FindFileTest() { for (int i = 0; i < files_.size(); i++) { @@ -50,10 +50,10 @@ class FindFileTest { TEST(FindFileTest, Empty) { ASSERT_EQ(0, Find("foo")); - ASSERT_TRUE(! Overlaps("a", "z")); - ASSERT_TRUE(! Overlaps(nullptr, "z")); - ASSERT_TRUE(! Overlaps("a", nullptr)); - ASSERT_TRUE(! Overlaps(nullptr, nullptr)); + ASSERT_TRUE(!Overlaps("a", "z")); + ASSERT_TRUE(!Overlaps(nullptr, "z")); + ASSERT_TRUE(!Overlaps("a", nullptr)); + ASSERT_TRUE(!Overlaps(nullptr, nullptr)); } TEST(FindFileTest, Single) { @@ -65,8 +65,8 @@ TEST(FindFileTest, Single) { ASSERT_EQ(1, Find("q1")); ASSERT_EQ(1, Find("z")); - ASSERT_TRUE(! Overlaps("a", "b")); - ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(!Overlaps("a", "b")); + ASSERT_TRUE(!Overlaps("z1", "z2")); ASSERT_TRUE(Overlaps("a", "p")); ASSERT_TRUE(Overlaps("a", "q")); ASSERT_TRUE(Overlaps("a", "z")); @@ -78,15 +78,14 @@ TEST(FindFileTest, Single) { ASSERT_TRUE(Overlaps("q", "q")); ASSERT_TRUE(Overlaps("q", "q1")); - ASSERT_TRUE(! Overlaps(nullptr, "j")); - ASSERT_TRUE(! Overlaps("r", nullptr)); + ASSERT_TRUE(!Overlaps(nullptr, "j")); + ASSERT_TRUE(!Overlaps("r", nullptr)); ASSERT_TRUE(Overlaps(nullptr, "p")); ASSERT_TRUE(Overlaps(nullptr, "p1")); ASSERT_TRUE(Overlaps("q", nullptr)); ASSERT_TRUE(Overlaps(nullptr, nullptr)); } - TEST(FindFileTest, Multiple) { Add("150", "200"); Add("200", "250"); @@ -110,10 +109,10 @@ TEST(FindFileTest, Multiple) { ASSERT_EQ(3, Find("450")); ASSERT_EQ(4, Find("451")); - ASSERT_TRUE(! Overlaps("100", "149")); - ASSERT_TRUE(! Overlaps("251", "299")); - ASSERT_TRUE(! Overlaps("451", "500")); - ASSERT_TRUE(! Overlaps("351", "399")); + ASSERT_TRUE(!Overlaps("100", "149")); + ASSERT_TRUE(!Overlaps("251", "299")); + ASSERT_TRUE(!Overlaps("451", "500")); + ASSERT_TRUE(!Overlaps("351", "399")); ASSERT_TRUE(Overlaps("100", "150")); ASSERT_TRUE(Overlaps("100", "200")); @@ -130,8 +129,8 @@ TEST(FindFileTest, MultipleNullBoundaries) { Add("200", "250"); Add("300", "350"); Add("400", "450"); - ASSERT_TRUE(! Overlaps(nullptr, "149")); - ASSERT_TRUE(! Overlaps("451", nullptr)); + ASSERT_TRUE(!Overlaps(nullptr, "149")); + ASSERT_TRUE(!Overlaps("451", nullptr)); ASSERT_TRUE(Overlaps(nullptr, nullptr)); ASSERT_TRUE(Overlaps(nullptr, "150")); ASSERT_TRUE(Overlaps(nullptr, "199")); @@ -147,8 +146,8 @@ TEST(FindFileTest, MultipleNullBoundaries) { TEST(FindFileTest, OverlapSequenceChecks) { Add("200", "200", 5000, 3000); - ASSERT_TRUE(! Overlaps("199", "199")); - ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(!Overlaps("199", "199")); + ASSERT_TRUE(!Overlaps("201", "300")); ASSERT_TRUE(Overlaps("200", "200")); ASSERT_TRUE(Overlaps("190", "200")); ASSERT_TRUE(Overlaps("200", "210")); @@ -158,8 +157,8 @@ TEST(FindFileTest, OverlappingFiles) { Add("150", "600"); Add("400", "500"); disjoint_sorted_files_ = false; - ASSERT_TRUE(! Overlaps("100", "149")); - ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(!Overlaps("100", "149")); + ASSERT_TRUE(!Overlaps("601", "700")); ASSERT_TRUE(Overlaps("100", "150")); ASSERT_TRUE(Overlaps("100", "200")); ASSERT_TRUE(Overlaps("100", "300")); diff --git a/db/write_batch.cc b/db/write_batch.cc index 23eb00fd42..2dec642483 100644 --- a/db/write_batch.cc +++ b/db/write_batch.cc @@ -15,10 +15,10 @@ #include "leveldb/write_batch.h" -#include "leveldb/db.h" #include "db/dbformat.h" #include "db/memtable.h" #include "db/write_batch_internal.h" +#include "leveldb/db.h" #include "util/coding.h" namespace leveldb { @@ -26,22 +26,18 @@ namespace leveldb { // WriteBatch header has an 8-byte sequence number followed by a 4-byte count. static const size_t kHeader = 12; -WriteBatch::WriteBatch() { - Clear(); -} +WriteBatch::WriteBatch() { Clear(); } -WriteBatch::~WriteBatch() { } +WriteBatch::~WriteBatch() {} -WriteBatch::Handler::~Handler() { } +WriteBatch::Handler::~Handler() {} void WriteBatch::Clear() { rep_.clear(); rep_.resize(kHeader); } -size_t WriteBatch::ApproximateSize() const { - return rep_.size(); -} +size_t WriteBatch::ApproximateSize() const { return rep_.size(); } Status WriteBatch::Iterate(Handler* handler) const { Slice input(rep_); @@ -112,7 +108,7 @@ void WriteBatch::Delete(const Slice& key) { PutLengthPrefixedSlice(&rep_, key); } -void WriteBatch::Append(const WriteBatch &source) { +void WriteBatch::Append(const WriteBatch& source) { WriteBatchInternal::Append(this, &source); } @@ -133,8 +129,7 @@ class MemTableInserter : public WriteBatch::Handler { }; } // namespace -Status WriteBatchInternal::InsertInto(const WriteBatch* b, - MemTable* memtable) { +Status WriteBatchInternal::InsertInto(const WriteBatch* b, MemTable* memtable) { MemTableInserter inserter; inserter.sequence_ = WriteBatchInternal::Sequence(b); inserter.mem_ = memtable; diff --git a/db/write_batch_internal.h b/db/write_batch_internal.h index 9448ef7b21..fce86e3f1f 100644 --- a/db/write_batch_internal.h +++ b/db/write_batch_internal.h @@ -29,13 +29,9 @@ class WriteBatchInternal { // this batch. static void SetSequence(WriteBatch* batch, SequenceNumber seq); - static Slice Contents(const WriteBatch* batch) { - return Slice(batch->rep_); - } + static Slice Contents(const WriteBatch* batch) { return Slice(batch->rep_); } - static size_t ByteSize(const WriteBatch* batch) { - return batch->rep_.size(); - } + static size_t ByteSize(const WriteBatch* batch) { return batch->rep_.size(); } static void SetContents(WriteBatch* batch, const Slice& contents); @@ -46,5 +42,4 @@ class WriteBatchInternal { } // namespace leveldb - #endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/db/write_batch_test.cc b/db/write_batch_test.cc index 49c178df6f..c32317fb5e 100644 --- a/db/write_batch_test.cc +++ b/db/write_batch_test.cc @@ -52,7 +52,7 @@ static std::string PrintContents(WriteBatch* b) { return state; } -class WriteBatchTest { }; +class WriteBatchTest {}; TEST(WriteBatchTest, Empty) { WriteBatch batch; @@ -68,10 +68,11 @@ TEST(WriteBatchTest, Multiple) { WriteBatchInternal::SetSequence(&batch, 100); ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); - ASSERT_EQ("Put(baz, boo)@102" - "Delete(box)@101" - "Put(foo, bar)@100", - PrintContents(&batch)); + ASSERT_EQ( + "Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); } TEST(WriteBatchTest, Corruption) { @@ -81,10 +82,11 @@ TEST(WriteBatchTest, Corruption) { WriteBatchInternal::SetSequence(&batch, 200); Slice contents = WriteBatchInternal::Contents(&batch); WriteBatchInternal::SetContents(&batch, - Slice(contents.data(),contents.size()-1)); - ASSERT_EQ("Put(foo, bar)@200" - "ParseError()", - PrintContents(&batch)); + Slice(contents.data(), contents.size() - 1)); + ASSERT_EQ( + "Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); } TEST(WriteBatchTest, Append) { @@ -92,25 +94,25 @@ TEST(WriteBatchTest, Append) { WriteBatchInternal::SetSequence(&b1, 200); WriteBatchInternal::SetSequence(&b2, 300); b1.Append(b2); - ASSERT_EQ("", - PrintContents(&b1)); + ASSERT_EQ("", PrintContents(&b1)); b2.Put("a", "va"); b1.Append(b2); - ASSERT_EQ("Put(a, va)@200", - PrintContents(&b1)); + ASSERT_EQ("Put(a, va)@200", PrintContents(&b1)); b2.Clear(); b2.Put("b", "vb"); b1.Append(b2); - ASSERT_EQ("Put(a, va)@200" - "Put(b, vb)@201", - PrintContents(&b1)); + ASSERT_EQ( + "Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); b2.Delete("foo"); b1.Append(b2); - ASSERT_EQ("Put(a, va)@200" - "Put(b, vb)@202" - "Put(b, vb)@201" - "Delete(foo)@203", - PrintContents(&b1)); + ASSERT_EQ( + "Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); } TEST(WriteBatchTest, ApproximateSize) { @@ -132,6 +134,4 @@ TEST(WriteBatchTest, ApproximateSize) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/doc/bench/db_bench_sqlite3.cc b/doc/bench/db_bench_sqlite3.cc index 7e05de2be4..f183f4fcfd 100644 --- a/doc/bench/db_bench_sqlite3.cc +++ b/doc/bench/db_bench_sqlite3.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include #include #include -#include + #include "util/histogram.h" #include "util/random.h" #include "util/testutil.h" @@ -38,8 +39,7 @@ static const char* FLAGS_benchmarks = "fillrand100K," "fillseq100K," "readseq," - "readrand100K," - ; + "readrand100K,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -78,8 +78,7 @@ static bool FLAGS_WAL_enabled = true; // Use the db with the following name. static const char* FLAGS_db = nullptr; -inline -static void ExecErrorCheck(int status, char *err_msg) { +inline static void ExecErrorCheck(int status, char* err_msg) { if (status != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", err_msg); sqlite3_free(err_msg); @@ -87,24 +86,21 @@ static void ExecErrorCheck(int status, char *err_msg) { } } -inline -static void StepErrorCheck(int status) { +inline static void StepErrorCheck(int status) { if (status != SQLITE_DONE) { fprintf(stderr, "SQL step error: status = %d\n", status); exit(1); } } -inline -static void ErrorCheck(int status) { +inline static void ErrorCheck(int status) { if (status != SQLITE_OK) { fprintf(stderr, "sqlite3 error: status = %d\n", status); exit(1); } } -inline -static void WalCheckpoint(sqlite3* db_) { +inline static void WalCheckpoint(sqlite3* db_) { // Flush all writes to disk if (FLAGS_WAL_enabled) { sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr, @@ -153,7 +149,7 @@ static Slice TrimSpace(Slice s) { start++; } int limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); @@ -177,7 +173,7 @@ class Benchmark { // State kept for progress messages int done_; - int next_report_; // When to report next + int next_report_; // When to report next void PrintHeader() { const int kKeySize = 16; @@ -186,17 +182,17 @@ class Benchmark { fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -262,13 +258,20 @@ class Benchmark { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } @@ -286,16 +289,14 @@ class Benchmark { snprintf(rate, sizeof(rate), "%6.1f MB/s", (bytes_ / 1048576.0) / (finish - start_)); if (!message_.empty()) { - message_ = std::string(rate) + " " + message_; + message_ = std::string(rate) + " " + message_; } else { message_ = rate; } } - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - (finish - start_) * 1e6 / done_, - (message_.empty() ? "" : " "), + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "), message_.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); @@ -304,22 +305,16 @@ class Benchmark { } public: - enum Order { - SEQUENTIAL, - RANDOM - }; - enum DBState { - FRESH, - EXISTING - }; + enum Order { SEQUENTIAL, RANDOM }; + enum DBState { FRESH, EXISTING }; Benchmark() - : db_(nullptr), - db_num_(0), - num_(FLAGS_num), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - bytes_(0), - rand_(301) { + : db_(nullptr), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { std::vector files; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); @@ -426,10 +421,8 @@ class Benchmark { // Open database std::string tmp_dir; Env::Default()->GetTestDirectory(&tmp_dir); - snprintf(file_name, sizeof(file_name), - "%s/dbbench_sqlite3-%d.db", - tmp_dir.c_str(), - db_num_); + snprintf(file_name, sizeof(file_name), "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), db_num_); status = sqlite3_open(file_name, &db_); if (status) { fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); @@ -460,26 +453,26 @@ class Benchmark { std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); - status = sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, - &err_msg); + status = + sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } // Change locking mode to exclusive and create tables/index for database std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; std::string create_stmt = - "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; - std::string stmt_array[] = { locking_stmt, create_stmt }; + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = {locking_stmt, create_stmt}; int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); for (int i = 0; i < stmt_array_length; i++) { - status = sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, - &err_msg); + status = + sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } } - void Write(bool write_sync, Order order, DBState state, - int num_entries, int value_size, int entries_per_batch) { + void Write(bool write_sync, Order order, DBState state, int num_entries, + int value_size, int entries_per_batch) { // Create new database if state == FRESH if (state == FRESH) { if (FLAGS_use_existing_db) { @@ -507,20 +500,20 @@ class Benchmark { std::string end_trans_str = "END TRANSACTION;"; // Check for synchronous flag in options - std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : - "PRAGMA synchronous = OFF"; + std::string sync_stmt = + (write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF"; status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); // Preparing sqlite3 statements - status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, - &replace_stmt, nullptr); + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, &replace_stmt, + nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, &begin_trans_stmt, nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, nullptr); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt, + nullptr); ErrorCheck(status); bool transaction = (entries_per_batch > 1); @@ -538,16 +531,16 @@ class Benchmark { const char* value = gen_.Generate(value_size).data(); // Create values for key-value pair - const int k = (order == SEQUENTIAL) ? i + j : - (rand_.Next() % num_entries); + const int k = + (order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries); char key[100]; snprintf(key, sizeof(key), "%016d", k); // Bind KV values into replace_stmt status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); ErrorCheck(status); - status = sqlite3_bind_blob(replace_stmt, 2, value, - value_size, SQLITE_STATIC); + status = sqlite3_bind_blob(replace_stmt, 2, value, value_size, + SQLITE_STATIC); ErrorCheck(status); // Execute replace_stmt @@ -593,8 +586,8 @@ class Benchmark { status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, &begin_trans_stmt, nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, nullptr); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt, + nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr); ErrorCheck(status); @@ -621,7 +614,8 @@ class Benchmark { ErrorCheck(status); // Execute read statement - while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {} + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) { + } StepErrorCheck(status); // Reset SQLite statement for another use @@ -651,7 +645,7 @@ class Benchmark { void ReadSequential() { int status; - sqlite3_stmt *pStmt; + sqlite3_stmt* pStmt; std::string read_str = "SELECT * FROM test ORDER BY key"; status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr); @@ -664,7 +658,6 @@ class Benchmark { status = sqlite3_finalize(pStmt); ErrorCheck(status); } - }; } // namespace leveldb @@ -710,9 +703,9 @@ int main(int argc, char** argv) { // Choose a location for the test database if none given with --db= if (FLAGS_db == nullptr) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/doc/bench/db_bench_tree_db.cc b/doc/bench/db_bench_tree_db.cc index 9f8fb9011b..b2f6646d89 100644 --- a/doc/bench/db_bench_tree_db.cc +++ b/doc/bench/db_bench_tree_db.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include #include #include -#include + #include "util/histogram.h" #include "util/random.h" #include "util/testutil.h" @@ -34,8 +35,7 @@ static const char* FLAGS_benchmarks = "fillrand100K," "fillseq100K," "readseq100K," - "readrand100K," - ; + "readrand100K,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -71,9 +71,7 @@ static bool FLAGS_compression = true; // Use the db with the following name. static const char* FLAGS_db = nullptr; -inline -static void DBSynchronize(kyotocabinet::TreeDB* db_) -{ +inline static void DBSynchronize(kyotocabinet::TreeDB* db_) { // Synchronize will flush writes to disk if (!db_->synchronize()) { fprintf(stderr, "synchronize error: %s\n", db_->error().name()); @@ -121,7 +119,7 @@ static Slice TrimSpace(Slice s) { start++; } int limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); @@ -146,7 +144,7 @@ class Benchmark { // State kept for progress messages int done_; - int next_report_; // When to report next + int next_report_; // When to report next void PrintHeader() { const int kKeySize = 16; @@ -157,20 +155,20 @@ class Benchmark { static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); fprintf(stdout, "FileSize: %.1f MB (estimated)\n", - (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) - / 1048576.0)); + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -237,13 +235,20 @@ class Benchmark { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } @@ -261,16 +266,14 @@ class Benchmark { snprintf(rate, sizeof(rate), "%6.1f MB/s", (bytes_ / 1048576.0) / (finish - start_)); if (!message_.empty()) { - message_ = std::string(rate) + " " + message_; + message_ = std::string(rate) + " " + message_; } else { message_ = rate; } } - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - (finish - start_) * 1e6 / done_, - (message_.empty() ? "" : " "), + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "), message_.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); @@ -279,21 +282,15 @@ class Benchmark { } public: - enum Order { - SEQUENTIAL, - RANDOM - }; - enum DBState { - FRESH, - EXISTING - }; + enum Order { SEQUENTIAL, RANDOM }; + enum DBState { FRESH, EXISTING }; Benchmark() - : db_(nullptr), - num_(FLAGS_num), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - bytes_(0), - rand_(301) { + : db_(nullptr), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { std::vector files; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); @@ -386,7 +383,7 @@ class Benchmark { } private: - void Open(bool sync) { + void Open(bool sync) { assert(db_ == nullptr); // Initialize db_ @@ -395,16 +392,14 @@ class Benchmark { db_num_++; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); - snprintf(file_name, sizeof(file_name), - "%s/dbbench_polyDB-%d.kct", - test_dir.c_str(), - db_num_); + snprintf(file_name, sizeof(file_name), "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), db_num_); // Create tuning options and open the database - int open_options = kyotocabinet::PolyDB::OWRITER | - kyotocabinet::PolyDB::OCREATE; - int tune_options = kyotocabinet::TreeDB::TSMALL | - kyotocabinet::TreeDB::TLINEAR; + int open_options = + kyotocabinet::PolyDB::OWRITER | kyotocabinet::PolyDB::OCREATE; + int tune_options = + kyotocabinet::TreeDB::TSMALL | kyotocabinet::TreeDB::TLINEAR; if (FLAGS_compression) { tune_options |= kyotocabinet::TreeDB::TCOMPRESS; db_->tune_compressor(&comp_); @@ -412,7 +407,7 @@ class Benchmark { db_->tune_options(tune_options); db_->tune_page_cache(FLAGS_cache_size); db_->tune_page(FLAGS_page_size); - db_->tune_map(256LL<<20); + db_->tune_map(256LL << 20); if (sync) { open_options |= kyotocabinet::PolyDB::OAUTOSYNC; } @@ -421,8 +416,8 @@ class Benchmark { } } - void Write(bool sync, Order order, DBState state, - int num_entries, int value_size, int entries_per_batch) { + void Write(bool sync, Order order, DBState state, int num_entries, + int value_size, int entries_per_batch) { // Create new database if state == FRESH if (state == FRESH) { if (FLAGS_use_existing_db) { @@ -442,8 +437,7 @@ class Benchmark { } // Write to database - for (int i = 0; i < num_entries; i++) - { + for (int i = 0; i < num_entries; i++) { const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); char key[100]; snprintf(key, sizeof(key), "%016d", k); @@ -517,9 +511,9 @@ int main(int argc, char** argv) { // Choose a location for the test database if none given with --db= if (FLAGS_db == nullptr) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index ff384e4f05..58dc538db9 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -134,9 +134,7 @@ class FileState { private: // Private since only Unref() should be used to delete it. - ~FileState() { - Truncate(); - } + ~FileState() { Truncate(); } // No copying allowed. FileState(const FileState&); @@ -158,9 +156,7 @@ class SequentialFileImpl : public SequentialFile { file_->Ref(); } - ~SequentialFileImpl() { - file_->Unref(); - } + ~SequentialFileImpl() { file_->Unref(); } virtual Status Read(size_t n, Slice* result, char* scratch) { Status s = file_->Read(pos_, n, result, scratch); @@ -189,13 +185,9 @@ class SequentialFileImpl : public SequentialFile { class RandomAccessFileImpl : public RandomAccessFile { public: - explicit RandomAccessFileImpl(FileState* file) : file_(file) { - file_->Ref(); - } + explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~RandomAccessFileImpl() { - file_->Unref(); - } + ~RandomAccessFileImpl() { file_->Unref(); } virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { @@ -208,17 +200,11 @@ class RandomAccessFileImpl : public RandomAccessFile { class WritableFileImpl : public WritableFile { public: - WritableFileImpl(FileState* file) : file_(file) { - file_->Ref(); - } + WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~WritableFileImpl() { - file_->Unref(); - } + ~WritableFileImpl() { file_->Unref(); } - virtual Status Append(const Slice& data) { - return file_->Append(data); - } + virtual Status Append(const Slice& data) { return file_->Append(data); } virtual Status Close() { return Status::OK(); } virtual Status Flush() { return Status::OK(); } @@ -230,15 +216,16 @@ class WritableFileImpl : public WritableFile { class NoOpLogger : public Logger { public: - virtual void Logv(const char* format, va_list ap) { } + virtual void Logv(const char* format, va_list ap) {} }; class InMemoryEnv : public EnvWrapper { public: - explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {} virtual ~InMemoryEnv() { - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); + ++i) { i->second->Unref(); } } @@ -311,7 +298,8 @@ class InMemoryEnv : public EnvWrapper { MutexLock lock(&mutex_); result->clear(); - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ + for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); + ++i) { const std::string& filename = i->first; if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && @@ -343,13 +331,9 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status CreateDir(const std::string& dirname) { - return Status::OK(); - } + virtual Status CreateDir(const std::string& dirname) { return Status::OK(); } - virtual Status DeleteDir(const std::string& dirname) { - return Status::OK(); - } + virtual Status DeleteDir(const std::string& dirname) { return Status::OK(); } virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { MutexLock lock(&mutex_); @@ -361,8 +345,7 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status RenameFile(const std::string& src, - const std::string& target) { + virtual Status RenameFile(const std::string& src, const std::string& target) { MutexLock lock(&mutex_); if (file_map_.find(src) == file_map_.end()) { return Status::IOError(src, "File not found"); @@ -403,8 +386,6 @@ class InMemoryEnv : public EnvWrapper { } // namespace -Env* NewMemEnv(Env* base_env) { - return new InMemoryEnv(base_env); -} +Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); } } // namespace leveldb diff --git a/helpers/memenv/memenv_test.cc b/helpers/memenv/memenv_test.cc index 466479563f..a0a9469123 100644 --- a/helpers/memenv/memenv_test.cc +++ b/helpers/memenv/memenv_test.cc @@ -4,12 +4,13 @@ #include "helpers/memenv/memenv.h" +#include +#include + #include "db/db_impl.h" #include "leveldb/db.h" #include "leveldb/env.h" #include "util/testharness.h" -#include -#include namespace leveldb { @@ -17,12 +18,8 @@ class MemEnvTest { public: Env* env_; - MemEnvTest() - : env_(NewMemEnv(Env::Default())) { - } - ~MemEnvTest() { - delete env_; - } + MemEnvTest() : env_(NewMemEnv(Env::Default())) {} + ~MemEnvTest() { delete env_; } }; TEST(MemEnvTest, Basics) { @@ -109,25 +106,25 @@ TEST(MemEnvTest, ReadWrite) { // Read sequentially. ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); - ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". ASSERT_EQ(0, result.compare("hello")); ASSERT_OK(seq_file->Skip(1)); - ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". ASSERT_EQ(0, result.compare("world")); - ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. ASSERT_EQ(0, result.size()); - ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. ASSERT_OK(seq_file->Read(1000, &result, scratch)); ASSERT_EQ(0, result.size()); delete seq_file; // Random reads. ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); - ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". ASSERT_EQ(0, result.compare("world")); - ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". ASSERT_EQ(0, result.compare("hello")); - ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". ASSERT_EQ(0, result.compare("d")); // Too high offset. @@ -176,7 +173,7 @@ TEST(MemEnvTest, LargeWrite) { SequentialFile* seq_file; Slice result; ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); - ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". ASSERT_EQ(0, result.compare("foo")); size_t read = 0; @@ -188,7 +185,7 @@ TEST(MemEnvTest, LargeWrite) { } ASSERT_TRUE(write_data == read_data); delete seq_file; - delete [] scratch; + delete[] scratch; } TEST(MemEnvTest, OverwriteOpenFile) { @@ -259,6 +256,4 @@ TEST(MemEnvTest, DBTest) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/include/leveldb/c.h b/include/leveldb/c.h index d8aab5b1d0..8e0d592ac3 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -47,26 +47,27 @@ extern "C" { #include #include #include + #include "leveldb/export.h" /* Exported types */ -typedef struct leveldb_t leveldb_t; -typedef struct leveldb_cache_t leveldb_cache_t; -typedef struct leveldb_comparator_t leveldb_comparator_t; -typedef struct leveldb_env_t leveldb_env_t; -typedef struct leveldb_filelock_t leveldb_filelock_t; -typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; -typedef struct leveldb_iterator_t leveldb_iterator_t; -typedef struct leveldb_logger_t leveldb_logger_t; -typedef struct leveldb_options_t leveldb_options_t; -typedef struct leveldb_randomfile_t leveldb_randomfile_t; -typedef struct leveldb_readoptions_t leveldb_readoptions_t; -typedef struct leveldb_seqfile_t leveldb_seqfile_t; -typedef struct leveldb_snapshot_t leveldb_snapshot_t; -typedef struct leveldb_writablefile_t leveldb_writablefile_t; -typedef struct leveldb_writebatch_t leveldb_writebatch_t; -typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; /* DB operations */ @@ -189,10 +190,7 @@ LEVELDB_EXPORT void leveldb_options_set_block_restart_interval( LEVELDB_EXPORT void leveldb_options_set_max_file_size(leveldb_options_t*, size_t); -enum { - leveldb_no_compression = 0, - leveldb_snappy_compression = 1 -}; +enum { leveldb_no_compression = 0, leveldb_snappy_compression = 1 }; LEVELDB_EXPORT void leveldb_options_set_compression(leveldb_options_t*, int); /* Comparator */ @@ -266,7 +264,7 @@ LEVELDB_EXPORT int leveldb_major_version(); LEVELDB_EXPORT int leveldb_minor_version(); #ifdef __cplusplus -} /* end extern "C" */ +} /* end extern "C" */ #endif -#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/include/leveldb/cache.h b/include/leveldb/cache.h index e416ea5e84..7d1a221193 100644 --- a/include/leveldb/cache.h +++ b/include/leveldb/cache.h @@ -19,6 +19,7 @@ #define STORAGE_LEVELDB_INCLUDE_CACHE_H_ #include + #include "leveldb/export.h" #include "leveldb/slice.h" @@ -42,7 +43,7 @@ class LEVELDB_EXPORT Cache { virtual ~Cache(); // Opaque handle to an entry stored in the cache. - struct Handle { }; + struct Handle {}; // Insert a mapping from key->value into the cache and assign it // the specified charge against the total cache capacity. diff --git a/include/leveldb/comparator.h b/include/leveldb/comparator.h index 9b09684a6b..a85b51ebd8 100644 --- a/include/leveldb/comparator.h +++ b/include/leveldb/comparator.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_ #include + #include "leveldb/export.h" namespace leveldb { @@ -44,9 +45,8 @@ class LEVELDB_EXPORT Comparator { // If *start < limit, changes *start to a short string in [start,limit). // Simple comparator implementations may return with *start unchanged, // i.e., an implementation of this method that does nothing is correct. - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const = 0; + virtual void FindShortestSeparator(std::string* start, + const Slice& limit) const = 0; // Changes *key to a short string >= *key. // Simple comparator implementations may return with *key unchanged, diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 0239593368..0b8dc24ef3 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -7,6 +7,7 @@ #include #include + #include "leveldb/export.h" #include "leveldb/iterator.h" #include "leveldb/options.h" @@ -32,11 +33,11 @@ class LEVELDB_EXPORT Snapshot { // A range of keys struct LEVELDB_EXPORT Range { - Slice start; // Included in the range - Slice limit; // Not included in the range + Slice start; // Included in the range + Slice limit; // Not included in the range - Range() { } - Range(const Slice& s, const Slice& l) : start(s), limit(l) { } + Range() {} + Range(const Slice& s, const Slice& l) : start(s), limit(l) {} }; // A DB is a persistent ordered map from keys to values. @@ -49,8 +50,7 @@ class LEVELDB_EXPORT DB { // OK on success. // Stores nullptr in *dbptr and returns a non-OK status on error. // Caller should delete *dbptr when it is no longer needed. - static Status Open(const Options& options, - const std::string& name, + static Status Open(const Options& options, const std::string& name, DB** dbptr); DB() = default; @@ -63,8 +63,7 @@ class LEVELDB_EXPORT DB { // Set the database entry for "key" to "value". Returns OK on success, // and a non-OK status on error. // Note: consider setting options.sync = true. - virtual Status Put(const WriteOptions& options, - const Slice& key, + virtual Status Put(const WriteOptions& options, const Slice& key, const Slice& value) = 0; // Remove the database entry (if any) for "key". Returns OK on @@ -85,8 +84,8 @@ class LEVELDB_EXPORT DB { // a status for which Status::IsNotFound() returns true. // // May return some other Status on an error. - virtual Status Get(const ReadOptions& options, - const Slice& key, std::string* value) = 0; + virtual Status Get(const ReadOptions& options, const Slice& key, + std::string* value) = 0; // Return a heap-allocated iterator over the contents of the database. // The result of NewIterator() is initially invalid (caller must diff --git a/include/leveldb/dumpfile.h b/include/leveldb/dumpfile.h index 659774163b..a58bc6b36c 100644 --- a/include/leveldb/dumpfile.h +++ b/include/leveldb/dumpfile.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ #include + #include "leveldb/env.h" #include "leveldb/export.h" #include "leveldb/status.h" diff --git a/include/leveldb/env.h b/include/leveldb/env.h index ea728c9fe1..112fe964fb 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -15,8 +15,10 @@ #include #include + #include #include + #include "leveldb/export.h" #include "leveldb/status.h" @@ -164,9 +166,7 @@ class LEVELDB_EXPORT Env { // added to the same Env may run concurrently in different threads. // I.e., the caller may not assume that background work items are // serialized. - virtual void Schedule( - void (*function)(void* arg), - void* arg) = 0; + virtual void Schedule(void (*function)(void* arg), void* arg) = 0; // Start a new thread, invoking "function(arg)" within the new thread. // When "function(arg)" returns, the thread will be destroyed. @@ -287,9 +287,9 @@ class LEVELDB_EXPORT FileLock { // Log the specified data to *info_log if info_log is non-null. void Log(Logger* info_log, const char* format, ...) -# if defined(__GNUC__) || defined(__clang__) - __attribute__((__format__ (__printf__, 2, 3))) -# endif +#if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__(__printf__, 2, 3))) +#endif ; // A utility routine: write "data" to the named file. @@ -306,7 +306,7 @@ LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname, class LEVELDB_EXPORT EnvWrapper : public Env { public: // Initialize an EnvWrapper that delegates all calls to *t. - explicit EnvWrapper(Env* t) : target_(t) { } + explicit EnvWrapper(Env* t) : target_(t) {} virtual ~EnvWrapper(); // Return the target to which this Env forwards all calls. @@ -364,9 +364,7 @@ class LEVELDB_EXPORT EnvWrapper : public Env { Status NewLogger(const std::string& fname, Logger** result) override { return target_->NewLogger(fname, result); } - uint64_t NowMicros() override { - return target_->NowMicros(); - } + uint64_t NowMicros() override { return target_->NowMicros(); } void SleepForMicroseconds(int micros) override { target_->SleepForMicroseconds(micros); } diff --git a/include/leveldb/filter_policy.h b/include/leveldb/filter_policy.h index ba027202a1..49c8eda776 100644 --- a/include/leveldb/filter_policy.h +++ b/include/leveldb/filter_policy.h @@ -17,6 +17,7 @@ #define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ #include + #include "leveldb/export.h" namespace leveldb { @@ -39,8 +40,8 @@ class LEVELDB_EXPORT FilterPolicy { // // Warning: do not change the initial contents of *dst. Instead, // append the newly constructed filter to *dst. - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) - const = 0; + virtual void CreateFilter(const Slice* keys, int n, + std::string* dst) const = 0; // "filter" contains the data appended by a preceding call to // CreateFilter() on this class. This method must return true if diff --git a/include/leveldb/iterator.h b/include/leveldb/iterator.h index 6c1d91b9c9..447e9507f3 100644 --- a/include/leveldb/iterator.h +++ b/include/leveldb/iterator.h @@ -93,7 +93,10 @@ class LEVELDB_EXPORT Iterator { // True if the node is not used. Only head nodes might be unused. bool IsEmpty() const { return function == nullptr; } // Invokes the cleanup function. - void Run() { assert(function != nullptr); (*function)(arg1, arg2); } + void Run() { + assert(function != nullptr); + (*function)(arg1, arg2); + } }; CleanupNode cleanup_head_; }; diff --git a/include/leveldb/options.h b/include/leveldb/options.h index 90aa19e149..7e26dc6c0b 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_ #include + #include "leveldb/export.h" namespace leveldb { @@ -24,7 +25,7 @@ class Snapshot; enum CompressionType { // NOTE: do not change the values of existing entries, as these are // part of the persistent format on disk. - kNoCompression = 0x0, + kNoCompression = 0x0, kSnappyCompression = 0x1 }; diff --git a/include/leveldb/slice.h b/include/leveldb/slice.h index a86e8a6476..2df417dc31 100644 --- a/include/leveldb/slice.h +++ b/include/leveldb/slice.h @@ -18,7 +18,9 @@ #include #include #include + #include + #include "leveldb/export.h" namespace leveldb { @@ -26,16 +28,16 @@ namespace leveldb { class LEVELDB_EXPORT Slice { public: // Create an empty slice. - Slice() : data_(""), size_(0) { } + Slice() : data_(""), size_(0) {} // Create a slice that refers to d[0,n-1]. - Slice(const char* d, size_t n) : data_(d), size_(n) { } + Slice(const char* d, size_t n) : data_(d), size_(n) {} // Create a slice that refers to the contents of "s" - Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + Slice(const std::string& s) : data_(s.data()), size_(s.size()) {} // Create a slice that refers to s[0,strlen(s)-1] - Slice(const char* s) : data_(s), size_(strlen(s)) { } + Slice(const char* s) : data_(s), size_(strlen(s)) {} // Intentionally copyable. Slice(const Slice&) = default; @@ -58,7 +60,10 @@ class LEVELDB_EXPORT Slice { } // Change this slice to refer to an empty array - void clear() { data_ = ""; size_ = 0; } + void clear() { + data_ = ""; + size_ = 0; + } // Drop the first "n" bytes from this slice. void remove_prefix(size_t n) { @@ -78,8 +83,7 @@ class LEVELDB_EXPORT Slice { // Return true iff "x" is a prefix of "*this" bool starts_with(const Slice& x) const { - return ((size_ >= x.size_) && - (memcmp(data_, x.data_, x.size_) == 0)); + return ((size_ >= x.size_) && (memcmp(data_, x.data_, x.size_) == 0)); } private: @@ -92,21 +96,20 @@ inline bool operator==(const Slice& x, const Slice& y) { (memcmp(x.data(), y.data(), x.size()) == 0)); } -inline bool operator!=(const Slice& x, const Slice& y) { - return !(x == y); -} +inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); } inline int Slice::compare(const Slice& b) const { const size_t min_len = (size_ < b.size_) ? size_ : b.size_; int r = memcmp(data_, b.data_, min_len); if (r == 0) { - if (size_ < b.size_) r = -1; - else if (size_ > b.size_) r = +1; + if (size_ < b.size_) + r = -1; + else if (size_ > b.size_) + r = +1; } return r; } } // namespace leveldb - #endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/include/leveldb/status.h b/include/leveldb/status.h index ee9fac2067..54cf377103 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -15,6 +15,7 @@ #include #include + #include "leveldb/export.h" #include "leveldb/slice.h" @@ -23,7 +24,7 @@ namespace leveldb { class LEVELDB_EXPORT Status { public: // Create a success status. - Status() noexcept : state_(nullptr) { } + Status() noexcept : state_(nullptr) {} ~Status() { delete[] state_; } Status(const Status& rhs); diff --git a/include/leveldb/table.h b/include/leveldb/table.h index e9f6641562..14a6a444f4 100644 --- a/include/leveldb/table.h +++ b/include/leveldb/table.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_H_ #include + #include "leveldb/export.h" #include "leveldb/iterator.h" @@ -36,10 +37,8 @@ class LEVELDB_EXPORT Table { // for the duration of the returned table's lifetime. // // *file must remain live while this Table is in use. - static Status Open(const Options& options, - RandomAccessFile* file, - uint64_t file_size, - Table** table); + static Status Open(const Options& options, RandomAccessFile* file, + uint64_t file_size, Table** table); Table(const Table&) = delete; void operator=(const Table&) = delete; @@ -70,11 +69,9 @@ class LEVELDB_EXPORT Table { // to Seek(key). May not make such a call if filter policy says // that key is not present. friend class TableCache; - Status InternalGet( - const ReadOptions&, const Slice& key, - void* arg, - void (*handle_result)(void* arg, const Slice& k, const Slice& v)); - + Status InternalGet(const ReadOptions&, const Slice& key, void* arg, + void (*handle_result)(void* arg, const Slice& k, + const Slice& v)); void ReadMeta(const Footer& footer); void ReadFilter(const Slice& filter_handle_value); diff --git a/include/leveldb/table_builder.h b/include/leveldb/table_builder.h index 8d05d3328e..f8361fd8c0 100644 --- a/include/leveldb/table_builder.h +++ b/include/leveldb/table_builder.h @@ -14,6 +14,7 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ #include + #include "leveldb/export.h" #include "leveldb/options.h" #include "leveldb/status.h" diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 5380c537cc..21f7c63ed4 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -22,6 +22,7 @@ #define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ #include + #include "leveldb/export.h" #include "leveldb/status.h" @@ -35,7 +36,7 @@ class LEVELDB_EXPORT WriteBatch { // Intentionally copyable. WriteBatch(const WriteBatch&) = default; - WriteBatch& operator =(const WriteBatch&) = default; + WriteBatch& operator=(const WriteBatch&) = default; ~WriteBatch(); diff --git a/issues/issue178_test.cc b/issues/issue178_test.cc index 1b1cf8bb28..d50ffeb9d4 100644 --- a/issues/issue178_test.cc +++ b/issues/issue178_test.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. // Test for issue 178: a manual compaction causes deleted data to reappear. +#include #include #include -#include #include "leveldb/db.h" #include "leveldb/write_batch.h" @@ -21,11 +21,9 @@ std::string Key1(int i) { return buf; } -std::string Key2(int i) { - return Key1(i) + "_xxx"; -} +std::string Key2(int i) { return Key1(i) + "_xxx"; } -class Issue178 { }; +class Issue178 {}; TEST(Issue178, Test) { // Get rid of any state from an old run. @@ -87,6 +85,4 @@ TEST(Issue178, Test) { } // anonymous namespace -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/issues/issue200_test.cc b/issues/issue200_test.cc index 1cec79f443..877b2afc47 100644 --- a/issues/issue200_test.cc +++ b/issues/issue200_test.cc @@ -11,14 +11,14 @@ namespace leveldb { -class Issue200 { }; +class Issue200 {}; TEST(Issue200, Test) { // Get rid of any state from an old run. std::string dbpath = test::TmpDir() + "/leveldb_issue200_test"; DestroyDB(dbpath, Options()); - DB *db; + DB* db; Options options; options.create_if_missing = true; ASSERT_OK(DB::Open(options, dbpath, &db)); @@ -31,7 +31,7 @@ TEST(Issue200, Test) { ASSERT_OK(db->Put(write_options, "5", "f")); ReadOptions read_options; - Iterator *iter = db->NewIterator(read_options); + Iterator* iter = db->NewIterator(read_options); // Add an element that should not be reflected in the iterator. ASSERT_OK(db->Put(write_options, "25", "cd")); @@ -54,6 +54,4 @@ TEST(Issue200, Test) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/port/port.h b/port/port.h index b2210a7553..4b247f74f9 100644 --- a/port/port.h +++ b/port/port.h @@ -11,9 +11,9 @@ // porting to a new platform, see "port_example.h" for documentation // of what the new port_.h file must provide. #if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_WINDOWS) -# include "port/port_stdcxx.h" +#include "port/port_stdcxx.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) -# include "port/port_chromium.h" +#include "port/port_chromium.h" #endif #endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/port/port_stdcxx.h b/port/port_stdcxx.h index 7638dedaef..e9cb0e53af 100644 --- a/port/port_stdcxx.h +++ b/port/port_stdcxx.h @@ -30,10 +30,10 @@ #endif // HAVE_SNAPPY #include +#include // NOLINT #include #include -#include // NOLINT -#include // NOLINT +#include // NOLINT #include #include "port/thread_annotations.h" @@ -56,7 +56,7 @@ class LOCKABLE Mutex { void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); } void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); } - void AssertHeld() ASSERT_EXCLUSIVE_LOCK() { } + void AssertHeld() ASSERT_EXCLUSIVE_LOCK() {} private: friend class CondVar; @@ -79,6 +79,7 @@ class CondVar { } void Signal() { cv_.notify_one(); } void SignalAll() { cv_.notify_all(); } + private: std::condition_variable cv_; Mutex* const mu_; @@ -94,7 +95,9 @@ inline bool Snappy_Compress(const char* input, size_t length, return true; #else // Silence compiler warnings about unused arguments. - (void)input; (void)length; (void)output; + (void)input; + (void)length; + (void)output; #endif // HAVE_SNAPPY return false; @@ -106,7 +109,9 @@ inline bool Snappy_GetUncompressedLength(const char* input, size_t length, return snappy::GetUncompressedLength(input, length, result); #else // Silence compiler warnings about unused arguments. - (void)input; (void)length; (void)result; + (void)input; + (void)length; + (void)result; return false; #endif // HAVE_SNAPPY } @@ -116,14 +121,17 @@ inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { return snappy::RawUncompress(input, length, output); #else // Silence compiler warnings about unused arguments. - (void)input; (void)length; (void)output; + (void)input; + (void)length; + (void)output; return false; #endif // HAVE_SNAPPY } inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { // Silence compiler warnings about unused arguments. - (void)func; (void)arg; + (void)func; + (void)arg; return false; } @@ -132,7 +140,9 @@ inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { return ::crc32c::Extend(crc, reinterpret_cast(buf), size); #else // Silence compiler warnings about unused arguments. - (void)crc; (void)buf; (void)size; + (void)crc; + (void)buf; + (void)size; return 0; #endif // HAVE_CRC32C } diff --git a/port/thread_annotations.h b/port/thread_annotations.h index b737c69014..1547df908f 100644 --- a/port/thread_annotations.h +++ b/port/thread_annotations.h @@ -13,9 +13,9 @@ #if defined(__clang__) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op #endif #endif // !defined(THREAD_ANNOTATION_ATTRIBUTE__) @@ -54,18 +54,15 @@ #endif #ifndef LOCK_RETURNED -#define LOCK_RETURNED(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) #endif #ifndef LOCKABLE -#define LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(lockable) +#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) #endif #ifndef SCOPED_LOCKABLE -#define SCOPED_LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) #endif #ifndef EXCLUSIVE_LOCK_FUNCTION diff --git a/table/block.cc b/table/block.cc index 6fdfdeae59..ad0ee98aa7 100644 --- a/table/block.cc +++ b/table/block.cc @@ -6,8 +6,9 @@ #include "table/block.h" -#include #include +#include + #include "leveldb/comparator.h" #include "table/format.h" #include "util/coding.h" @@ -27,7 +28,7 @@ Block::Block(const BlockContents& contents) if (size_ < sizeof(uint32_t)) { size_ = 0; // Error marker } else { - size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t); + size_t max_restarts_allowed = (size_ - sizeof(uint32_t)) / sizeof(uint32_t); if (NumRestarts() > max_restarts_allowed) { // The size is too small for NumRestarts() size_ = 0; @@ -51,8 +52,7 @@ Block::~Block() { // If any errors are detected, returns nullptr. Otherwise, returns a // pointer to the key delta (just past the three decoded values). static inline const char* DecodeEntry(const char* p, const char* limit, - uint32_t* shared, - uint32_t* non_shared, + uint32_t* shared, uint32_t* non_shared, uint32_t* value_length) { if (limit - p < 3) return nullptr; *shared = reinterpret_cast(p)[0]; @@ -76,9 +76,9 @@ static inline const char* DecodeEntry(const char* p, const char* limit, class Block::Iter : public Iterator { private: const Comparator* const comparator_; - const char* const data_; // underlying block contents - uint32_t const restarts_; // Offset of restart array (list of fixed32) - uint32_t const num_restarts_; // Number of uint32_t entries in restart array + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array // current_ is offset in data_ of current entry. >= restarts_ if !Valid uint32_t current_; @@ -112,9 +112,7 @@ class Block::Iter : public Iterator { } public: - Iter(const Comparator* comparator, - const char* data, - uint32_t restarts, + Iter(const Comparator* comparator, const char* data, uint32_t restarts, uint32_t num_restarts) : comparator_(comparator), data_(data), @@ -171,9 +169,9 @@ class Block::Iter : public Iterator { uint32_t mid = (left + right + 1) / 2; uint32_t region_offset = GetRestartPoint(mid); uint32_t shared, non_shared, value_length; - const char* key_ptr = DecodeEntry(data_ + region_offset, - data_ + restarts_, - &shared, &non_shared, &value_length); + const char* key_ptr = + DecodeEntry(data_ + region_offset, data_ + restarts_, &shared, + &non_shared, &value_length); if (key_ptr == nullptr || (shared != 0)) { CorruptionError(); return; @@ -253,7 +251,7 @@ class Block::Iter : public Iterator { } }; -Iterator* Block::NewIterator(const Comparator* cmp) { +Iterator* Block::NewIterator(const Comparator* comparator) { if (size_ < sizeof(uint32_t)) { return NewErrorIterator(Status::Corruption("bad block contents")); } @@ -261,7 +259,7 @@ Iterator* Block::NewIterator(const Comparator* cmp) { if (num_restarts == 0) { return NewEmptyIterator(); } else { - return new Iter(cmp, data_, restart_offset_, num_restarts); + return new Iter(comparator, data_, restart_offset_, num_restarts); } } diff --git a/table/block.h b/table/block.h index 2493eb9f9f..3d4b03c6bb 100644 --- a/table/block.h +++ b/table/block.h @@ -7,6 +7,7 @@ #include #include + #include "leveldb/iterator.h" namespace leveldb { @@ -29,8 +30,8 @@ class Block { const char* data_; size_t size_; - uint32_t restart_offset_; // Offset in data_ of restart array - bool owned_; // Block owns data_[] + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] // No copying allowed Block(const Block&); diff --git a/table/block_builder.cc b/table/block_builder.cc index db660cd07c..f7cb1b0a47 100644 --- a/table/block_builder.cc +++ b/table/block_builder.cc @@ -28,8 +28,10 @@ #include "table/block_builder.h" -#include #include + +#include + #include "leveldb/comparator.h" #include "leveldb/table_builder.h" #include "util/coding.h" @@ -37,27 +39,24 @@ namespace leveldb { BlockBuilder::BlockBuilder(const Options* options) - : options_(options), - restarts_(), - counter_(0), - finished_(false) { + : options_(options), restarts_(), counter_(0), finished_(false) { assert(options->block_restart_interval >= 1); - restarts_.push_back(0); // First restart point is at offset 0 + restarts_.push_back(0); // First restart point is at offset 0 } void BlockBuilder::Reset() { buffer_.clear(); restarts_.clear(); - restarts_.push_back(0); // First restart point is at offset 0 + restarts_.push_back(0); // First restart point is at offset 0 counter_ = 0; finished_ = false; last_key_.clear(); } size_t BlockBuilder::CurrentSizeEstimate() const { - return (buffer_.size() + // Raw data buffer - restarts_.size() * sizeof(uint32_t) + // Restart array - sizeof(uint32_t)); // Restart array length + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length } Slice BlockBuilder::Finish() { @@ -74,7 +73,7 @@ void BlockBuilder::Add(const Slice& key, const Slice& value) { Slice last_key_piece(last_key_); assert(!finished_); assert(counter_ <= options_->block_restart_interval); - assert(buffer_.empty() // No values yet? + assert(buffer_.empty() // No values yet? || options_->comparator->Compare(key, last_key_piece) > 0); size_t shared = 0; if (counter_ < options_->block_restart_interval) { diff --git a/table/block_builder.h b/table/block_builder.h index 4fbcb33972..d0d9b6e6ad 100644 --- a/table/block_builder.h +++ b/table/block_builder.h @@ -5,9 +5,10 @@ #ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ #define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#include + #include -#include #include "leveldb/slice.h" namespace leveldb { @@ -35,17 +36,15 @@ class BlockBuilder { size_t CurrentSizeEstimate() const; // Return true iff no entries have been added since the last Reset() - bool empty() const { - return buffer_.empty(); - } + bool empty() const { return buffer_.empty(); } private: - const Options* options_; - std::string buffer_; // Destination buffer - std::vector restarts_; // Restart points - int counter_; // Number of entries emitted since restart - bool finished_; // Has Finish() been called? - std::string last_key_; + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; // No copying allowed BlockBuilder(const BlockBuilder&); diff --git a/table/filter_block.cc b/table/filter_block.cc index ce0aa048e9..09ec0094bd 100644 --- a/table/filter_block.cc +++ b/table/filter_block.cc @@ -16,8 +16,7 @@ static const size_t kFilterBaseLg = 11; static const size_t kFilterBase = 1 << kFilterBaseLg; FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) - : policy_(policy) { -} + : policy_(policy) {} void FilterBlockBuilder::StartBlock(uint64_t block_offset) { uint64_t filter_index = (block_offset / kFilterBase); @@ -62,7 +61,7 @@ void FilterBlockBuilder::GenerateFilter() { tmp_keys_.resize(num_keys); for (size_t i = 0; i < num_keys; i++) { const char* base = keys_.data() + start_[i]; - size_t length = start_[i+1] - start_[i]; + size_t length = start_[i + 1] - start_[i]; tmp_keys_[i] = Slice(base, length); } @@ -77,14 +76,10 @@ void FilterBlockBuilder::GenerateFilter() { FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, const Slice& contents) - : policy_(policy), - data_(nullptr), - offset_(nullptr), - num_(0), - base_lg_(0) { + : policy_(policy), data_(nullptr), offset_(nullptr), num_(0), base_lg_(0) { size_t n = contents.size(); if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array - base_lg_ = contents[n-1]; + base_lg_ = contents[n - 1]; uint32_t last_word = DecodeFixed32(contents.data() + n - 5); if (last_word > n - 5) return; data_ = contents.data(); @@ -95,8 +90,8 @@ FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { uint64_t index = block_offset >> base_lg_; if (index < num_) { - uint32_t start = DecodeFixed32(offset_ + index*4); - uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); + uint32_t start = DecodeFixed32(offset_ + index * 4); + uint32_t limit = DecodeFixed32(offset_ + index * 4 + 4); if (start <= limit && limit <= static_cast(offset_ - data_)) { Slice filter = Slice(data_ + start, limit - start); return policy_->KeyMayMatch(key, filter); @@ -108,4 +103,4 @@ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { return true; // Errors are treated as potential matches } -} +} // namespace leveldb diff --git a/table/filter_block.h b/table/filter_block.h index c67d010bd1..1b034dc92a 100644 --- a/table/filter_block.h +++ b/table/filter_block.h @@ -11,8 +11,10 @@ #include #include + #include #include + #include "leveldb/slice.h" #include "util/hash.h" @@ -38,10 +40,10 @@ class FilterBlockBuilder { void GenerateFilter(); const FilterPolicy* policy_; - std::string keys_; // Flattened key contents - std::vector start_; // Starting index in keys_ of each key - std::string result_; // Filter data computed so far - std::vector tmp_keys_; // policy_->CreateFilter() argument + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument std::vector filter_offsets_; // No copying allowed @@ -51,7 +53,7 @@ class FilterBlockBuilder { class FilterBlockReader { public: - // REQUIRES: "contents" and *policy must stay live while *this is live. + // REQUIRES: "contents" and *policy must stay live while *this is live. FilterBlockReader(const FilterPolicy* policy, const Slice& contents); bool KeyMayMatch(uint64_t block_offset, const Slice& key); @@ -63,6 +65,6 @@ class FilterBlockReader { size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) }; -} +} // namespace leveldb #endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/table/filter_block_test.cc b/table/filter_block_test.cc index 8c4a4741f2..6cdd435cf9 100644 --- a/table/filter_block_test.cc +++ b/table/filter_block_test.cc @@ -16,9 +16,7 @@ namespace leveldb { // For testing: emit an array with one hash value per key class TestHashFilter : public FilterPolicy { public: - virtual const char* Name() const { - return "TestHashFilter"; - } + virtual const char* Name() const { return "TestHashFilter"; } virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { for (int i = 0; i < n; i++) { @@ -69,8 +67,8 @@ TEST(FilterBlockTest, SingleChunk) { ASSERT_TRUE(reader.KeyMayMatch(100, "box")); ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); - ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); + ASSERT_TRUE(!reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(!reader.KeyMayMatch(100, "other")); } TEST(FilterBlockTest, MultiChunk) { @@ -99,30 +97,28 @@ TEST(FilterBlockTest, MultiChunk) { // Check first filter ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(!reader.KeyMayMatch(0, "hello")); // Check second filter ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "hello")); // Check third filter (empty) - ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "hello")); // Check last filter ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); - ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(9000, "bar")); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/table/format.cc b/table/format.cc index 24e4e02445..e1839779e4 100644 --- a/table/format.cc +++ b/table/format.cc @@ -21,8 +21,7 @@ void BlockHandle::EncodeTo(std::string* dst) const { } Status BlockHandle::DecodeFrom(Slice* input) { - if (GetVarint64(input, &offset_) && - GetVarint64(input, &size_)) { + if (GetVarint64(input, &offset_) && GetVarint64(input, &size_)) { return Status::OK(); } else { return Status::Corruption("bad block handle"); @@ -62,10 +61,8 @@ Status Footer::DecodeFrom(Slice* input) { return result; } -Status ReadBlock(RandomAccessFile* file, - const ReadOptions& options, - const BlockHandle& handle, - BlockContents* result) { +Status ReadBlock(RandomAccessFile* file, const ReadOptions& options, + const BlockHandle& handle, BlockContents* result) { result->data = Slice(); result->cachable = false; result->heap_allocated = false; @@ -86,7 +83,7 @@ Status ReadBlock(RandomAccessFile* file, } // Check the crc of the type and the block contents - const char* data = contents.data(); // Pointer to where Read put the data + const char* data = contents.data(); // Pointer to where Read put the data if (options.verify_checksums) { const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); const uint32_t actual = crc32c::Value(data, n + 1); diff --git a/table/format.h b/table/format.h index 144ff5528f..dacaa9f605 100644 --- a/table/format.h +++ b/table/format.h @@ -5,8 +5,10 @@ #ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ #define STORAGE_LEVELDB_TABLE_FORMAT_H_ -#include #include + +#include + #include "leveldb/slice.h" #include "leveldb/status.h" #include "leveldb/table_builder.h" @@ -46,19 +48,15 @@ class BlockHandle { // end of every table file. class Footer { public: - Footer() { } + Footer() {} // The block handle for the metaindex block of the table const BlockHandle& metaindex_handle() const { return metaindex_handle_; } void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } // The block handle for the index block of the table - const BlockHandle& index_handle() const { - return index_handle_; - } - void set_index_handle(const BlockHandle& h) { - index_handle_ = h; - } + const BlockHandle& index_handle() const { return index_handle_; } + void set_index_handle(const BlockHandle& h) { index_handle_ = h; } void EncodeTo(std::string* dst) const; Status DecodeFrom(Slice* input); @@ -66,9 +64,7 @@ class Footer { // Encoded length of a Footer. Note that the serialization of a // Footer will always occupy exactly this many bytes. It consists // of two block handles and a magic number. - enum { - kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 - }; + enum { kEncodedLength = 2 * BlockHandle::kMaxEncodedLength + 8 }; private: BlockHandle metaindex_handle_; @@ -91,17 +87,13 @@ struct BlockContents { // Read the block identified by "handle" from "file". On failure // return non-OK. On success fill *result and return OK. -Status ReadBlock(RandomAccessFile* file, - const ReadOptions& options, - const BlockHandle& handle, - BlockContents* result); +Status ReadBlock(RandomAccessFile* file, const ReadOptions& options, + const BlockHandle& handle, BlockContents* result); // Implementation details follow. Clients should ignore, inline BlockHandle::BlockHandle() - : offset_(~static_cast(0)), - size_(~static_cast(0)) { -} + : offset_(~static_cast(0)), size_(~static_cast(0)) {} } // namespace leveldb diff --git a/table/iterator.cc b/table/iterator.cc index 41ec1aa645..dfef083d4d 100644 --- a/table/iterator.cc +++ b/table/iterator.cc @@ -14,7 +14,7 @@ Iterator::Iterator() { Iterator::~Iterator() { if (!cleanup_head_.IsEmpty()) { cleanup_head_.Run(); - for (CleanupNode* node = cleanup_head_.next; node != nullptr; ) { + for (CleanupNode* node = cleanup_head_.next; node != nullptr;) { node->Run(); CleanupNode* next_node = node->next; delete node; @@ -42,17 +42,23 @@ namespace { class EmptyIterator : public Iterator { public: - EmptyIterator(const Status& s) : status_(s) { } + EmptyIterator(const Status& s) : status_(s) {} ~EmptyIterator() override = default; bool Valid() const override { return false; } - void Seek(const Slice& target) override { } - void SeekToFirst() override { } - void SeekToLast() override { } + void Seek(const Slice& target) override {} + void SeekToFirst() override {} + void SeekToLast() override {} void Next() override { assert(false); } void Prev() override { assert(false); } - Slice key() const override { assert(false); return Slice(); } - Slice value() const override { assert(false); return Slice(); } + Slice key() const override { + assert(false); + return Slice(); + } + Slice value() const override { + assert(false); + return Slice(); + } Status status() const override { return status_; } private: @@ -61,9 +67,7 @@ class EmptyIterator : public Iterator { } // anonymous namespace -Iterator* NewEmptyIterator() { - return new EmptyIterator(Status::OK()); -} +Iterator* NewEmptyIterator() { return new EmptyIterator(Status::OK()); } Iterator* NewErrorIterator(const Status& status) { return new EmptyIterator(status); diff --git a/table/iterator_wrapper.h b/table/iterator_wrapper.h index f1814cab43..c230572529 100644 --- a/table/iterator_wrapper.h +++ b/table/iterator_wrapper.h @@ -16,10 +16,8 @@ namespace leveldb { // cache locality. class IteratorWrapper { public: - IteratorWrapper(): iter_(nullptr), valid_(false) { } - explicit IteratorWrapper(Iterator* iter): iter_(nullptr) { - Set(iter); - } + IteratorWrapper() : iter_(nullptr), valid_(false) {} + explicit IteratorWrapper(Iterator* iter) : iter_(nullptr) { Set(iter); } ~IteratorWrapper() { delete iter_; } Iterator* iter() const { return iter_; } @@ -35,18 +33,46 @@ class IteratorWrapper { } } - // Iterator interface methods - bool Valid() const { return valid_; } - Slice key() const { assert(Valid()); return key_; } - Slice value() const { assert(Valid()); return iter_->value(); } + bool Valid() const { return valid_; } + Slice key() const { + assert(Valid()); + return key_; + } + Slice value() const { + assert(Valid()); + return iter_->value(); + } // Methods below require iter() != nullptr - Status status() const { assert(iter_); return iter_->status(); } - void Next() { assert(iter_); iter_->Next(); Update(); } - void Prev() { assert(iter_); iter_->Prev(); Update(); } - void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } - void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } - void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + Status status() const { + assert(iter_); + return iter_->status(); + } + void Next() { + assert(iter_); + iter_->Next(); + Update(); + } + void Prev() { + assert(iter_); + iter_->Prev(); + Update(); + } + void Seek(const Slice& k) { + assert(iter_); + iter_->Seek(k); + Update(); + } + void SeekToFirst() { + assert(iter_); + iter_->SeekToFirst(); + Update(); + } + void SeekToLast() { + assert(iter_); + iter_->SeekToLast(); + Update(); + } private: void Update() { diff --git a/table/merger.cc b/table/merger.cc index e079680807..3a5c3e4947 100644 --- a/table/merger.cc +++ b/table/merger.cc @@ -24,13 +24,9 @@ class MergingIterator : public Iterator { } } - virtual ~MergingIterator() { - delete[] children_; - } + virtual ~MergingIterator() { delete[] children_; } - virtual bool Valid() const { - return (current_ != nullptr); - } + virtual bool Valid() const { return (current_ != nullptr); } virtual void SeekToFirst() { for (int i = 0; i < n_; i++) { @@ -145,10 +141,7 @@ class MergingIterator : public Iterator { IteratorWrapper* current_; // Which direction is the iterator moving? - enum Direction { - kForward, - kReverse - }; + enum Direction { kForward, kReverse }; Direction direction_; }; @@ -169,7 +162,7 @@ void MergingIterator::FindSmallest() { void MergingIterator::FindLargest() { IteratorWrapper* largest = nullptr; - for (int i = n_-1; i >= 0; i--) { + for (int i = n_ - 1; i >= 0; i--) { IteratorWrapper* child = &children_[i]; if (child->Valid()) { if (largest == nullptr) { @@ -183,14 +176,15 @@ void MergingIterator::FindLargest() { } } // namespace -Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { +Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children, + int n) { assert(n >= 0); if (n == 0) { return NewEmptyIterator(); } else if (n == 1) { - return list[0]; + return children[0]; } else { - return new MergingIterator(cmp, list, n); + return new MergingIterator(comparator, children, n); } } diff --git a/table/merger.h b/table/merger.h index bafdf5aef7..41cedc5254 100644 --- a/table/merger.h +++ b/table/merger.h @@ -18,8 +18,8 @@ class Iterator; // key is present in K child iterators, it will be yielded K times. // // REQUIRES: n >= 0 -Iterator* NewMergingIterator( - const Comparator* comparator, Iterator** children, int n); +Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children, + int n); } // namespace leveldb diff --git a/table/table.cc b/table/table.cc index 8e737e1b50..b07bc88c7e 100644 --- a/table/table.cc +++ b/table/table.cc @@ -20,7 +20,7 @@ namespace leveldb { struct Table::Rep { ~Rep() { delete filter; - delete [] filter_data; + delete[] filter_data; delete index_block; } @@ -35,10 +35,8 @@ struct Table::Rep { Block* index_block; }; -Status Table::Open(const Options& options, - RandomAccessFile* file, - uint64_t size, - Table** table) { +Status Table::Open(const Options& options, RandomAccessFile* file, + uint64_t size, Table** table) { *table = nullptr; if (size < Footer::kEncodedLength) { return Status::Corruption("file is too short to be an sstable"); @@ -130,14 +128,12 @@ void Table::ReadFilter(const Slice& filter_handle_value) { return; } if (block.heap_allocated) { - rep_->filter_data = block.data.data(); // Will need to delete later + rep_->filter_data = block.data.data(); // Will need to delete later } rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); } -Table::~Table() { - delete rep_; -} +Table::~Table() { delete rep_; } static void DeleteBlock(void* arg, void* ignored) { delete reinterpret_cast(arg); @@ -156,8 +152,7 @@ static void ReleaseBlock(void* arg, void* h) { // Convert an index iterator value (i.e., an encoded BlockHandle) // into an iterator over the contents of the corresponding block. -Iterator* Table::BlockReader(void* arg, - const ReadOptions& options, +Iterator* Table::BlockReader(void* arg, const ReadOptions& options, const Slice& index_value) { Table* table = reinterpret_cast(arg); Cache* block_cache = table->rep_->options.block_cache; @@ -175,7 +170,7 @@ Iterator* Table::BlockReader(void* arg, if (block_cache != nullptr) { char cache_key_buffer[16]; EncodeFixed64(cache_key_buffer, table->rep_->cache_id); - EncodeFixed64(cache_key_buffer+8, handle.offset()); + EncodeFixed64(cache_key_buffer + 8, handle.offset()); Slice key(cache_key_buffer, sizeof(cache_key_buffer)); cache_handle = block_cache->Lookup(key); if (cache_handle != nullptr) { @@ -185,8 +180,8 @@ Iterator* Table::BlockReader(void* arg, if (s.ok()) { block = new Block(contents); if (contents.cachable && options.fill_cache) { - cache_handle = block_cache->Insert( - key, block, block->size(), &DeleteCachedBlock); + cache_handle = block_cache->Insert(key, block, block->size(), + &DeleteCachedBlock); } } } @@ -218,9 +213,9 @@ Iterator* Table::NewIterator(const ReadOptions& options) const { &Table::BlockReader, const_cast(this), options); } -Status Table::InternalGet(const ReadOptions& options, const Slice& k, - void* arg, - void (*saver)(void*, const Slice&, const Slice&)) { +Status Table::InternalGet(const ReadOptions& options, const Slice& k, void* arg, + void (*handle_result)(void*, const Slice&, + const Slice&)) { Status s; Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); iiter->Seek(k); @@ -228,15 +223,14 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, Slice handle_value = iiter->value(); FilterBlockReader* filter = rep_->filter; BlockHandle handle; - if (filter != nullptr && - handle.DecodeFrom(&handle_value).ok() && + if (filter != nullptr && handle.DecodeFrom(&handle_value).ok() && !filter->KeyMayMatch(handle.offset(), k)) { // Not found } else { Iterator* block_iter = BlockReader(this, options, iiter->value()); block_iter->Seek(k); if (block_iter->Valid()) { - (*saver)(arg, block_iter->key(), block_iter->value()); + (*handle_result)(arg, block_iter->key(), block_iter->value()); } s = block_iter->status(); delete block_iter; @@ -249,7 +243,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, return s; } - uint64_t Table::ApproximateOffsetOf(const Slice& key) const { Iterator* index_iter = rep_->index_block->NewIterator(rep_->options.comparator); diff --git a/table/table_builder.cc b/table/table_builder.cc index 444d4f96fb..9afff767c3 100644 --- a/table/table_builder.cc +++ b/table/table_builder.cc @@ -5,6 +5,7 @@ #include "leveldb/table_builder.h" #include + #include "leveldb/comparator.h" #include "leveldb/env.h" #include "leveldb/filter_policy.h" @@ -27,7 +28,7 @@ struct TableBuilder::Rep { BlockBuilder index_block; std::string last_key; int64_t num_entries; - bool closed; // Either Finish() or Abandon() has been called. + bool closed; // Either Finish() or Abandon() has been called. FilterBlockBuilder* filter_block; // We do not emit the index entry for a block until we have seen the @@ -53,8 +54,9 @@ struct TableBuilder::Rep { index_block(&index_block_options), num_entries(0), closed(false), - filter_block(opt.filter_policy == nullptr ? nullptr - : new FilterBlockBuilder(opt.filter_policy)), + filter_block(opt.filter_policy == nullptr + ? nullptr + : new FilterBlockBuilder(opt.filter_policy)), pending_index_entry(false) { index_block_options.block_restart_interval = 1; } @@ -173,8 +175,7 @@ void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { } void TableBuilder::WriteRawBlock(const Slice& block_contents, - CompressionType type, - BlockHandle* handle) { + CompressionType type, BlockHandle* handle) { Rep* r = rep_; handle->set_offset(r->offset); handle->set_size(block_contents.size()); @@ -184,7 +185,7 @@ void TableBuilder::WriteRawBlock(const Slice& block_contents, trailer[0] = type; uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type - EncodeFixed32(trailer+1, crc32c::Mask(crc)); + EncodeFixed32(trailer + 1, crc32c::Mask(crc)); r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); if (r->status.ok()) { r->offset += block_contents.size() + kBlockTrailerSize; @@ -192,9 +193,7 @@ void TableBuilder::WriteRawBlock(const Slice& block_contents, } } -Status TableBuilder::status() const { - return rep_->status; -} +Status TableBuilder::status() const { return rep_->status; } Status TableBuilder::Finish() { Rep* r = rep_; @@ -259,12 +258,8 @@ void TableBuilder::Abandon() { r->closed = true; } -uint64_t TableBuilder::NumEntries() const { - return rep_->num_entries; -} +uint64_t TableBuilder::NumEntries() const { return rep_->num_entries; } -uint64_t TableBuilder::FileSize() const { - return rep_->offset; -} +uint64_t TableBuilder::FileSize() const { return rep_->offset; } } // namespace leveldb diff --git a/table/table_test.cc b/table/table_test.cc index 5573be6e1e..0974052303 100644 --- a/table/table_test.cc +++ b/table/table_test.cc @@ -6,6 +6,7 @@ #include #include + #include "db/dbformat.h" #include "db/memtable.h" #include "db/write_batch_internal.h" @@ -27,8 +28,8 @@ namespace leveldb { static std::string Reverse(const Slice& key) { std::string str(key.ToString()); std::string rev(""); - for (std::string::reverse_iterator rit = str.rbegin(); - rit != str.rend(); ++rit) { + for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); + ++rit) { rev.push_back(*rit); } return rev; @@ -45,9 +46,8 @@ class ReverseKeyComparator : public Comparator { return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); } - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const { + virtual void FindShortestSeparator(std::string* start, + const Slice& limit) const { std::string s = Reverse(*start); std::string l = Reverse(limit); BytewiseComparator()->FindShortestSeparator(&s, l); @@ -79,17 +79,17 @@ namespace { struct STLLessThan { const Comparator* cmp; - STLLessThan() : cmp(BytewiseComparator()) { } - STLLessThan(const Comparator* c) : cmp(c) { } + STLLessThan() : cmp(BytewiseComparator()) {} + STLLessThan(const Comparator* c) : cmp(c) {} bool operator()(const std::string& a, const std::string& b) const { return cmp->Compare(Slice(a), Slice(b)) < 0; } }; } // namespace -class StringSink: public WritableFile { +class StringSink : public WritableFile { public: - ~StringSink() { } + ~StringSink() {} const std::string& contents() const { return contents_; } @@ -106,19 +106,17 @@ class StringSink: public WritableFile { std::string contents_; }; - -class StringSource: public RandomAccessFile { +class StringSource : public RandomAccessFile { public: StringSource(const Slice& contents) - : contents_(contents.data(), contents.size()) { - } + : contents_(contents.data(), contents.size()) {} - virtual ~StringSource() { } + virtual ~StringSource() {} uint64_t Size() const { return contents_.size(); } virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + char* scratch) const { if (offset >= contents_.size()) { return Status::InvalidArgument("invalid Read offset"); } @@ -140,8 +138,8 @@ typedef std::map KVMap; // BlockBuilder/TableBuilder and Block/Table. class Constructor { public: - explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } - virtual ~Constructor() { } + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) {} + virtual ~Constructor() {} void Add(const std::string& key, const Slice& value) { data_[key] = value.ToString(); @@ -150,14 +148,11 @@ class Constructor { // Finish constructing the data structure with all the keys that have // been added so far. Returns the keys in sorted order in "*keys" // and stores the key/value pairs in "*kvmap" - void Finish(const Options& options, - std::vector* keys, + void Finish(const Options& options, std::vector* keys, KVMap* kvmap) { *kvmap = data_; keys->clear(); - for (KVMap::const_iterator it = data_.begin(); - it != data_.end(); - ++it) { + for (KVMap::const_iterator it = data_.begin(); it != data_.end(); ++it) { keys->push_back(it->first); } data_.clear(); @@ -178,23 +173,17 @@ class Constructor { KVMap data_; }; -class BlockConstructor: public Constructor { +class BlockConstructor : public Constructor { public: explicit BlockConstructor(const Comparator* cmp) - : Constructor(cmp), - comparator_(cmp), - block_(nullptr) { } - ~BlockConstructor() { - delete block_; - } + : Constructor(cmp), comparator_(cmp), block_(nullptr) {} + ~BlockConstructor() { delete block_; } virtual Status FinishImpl(const Options& options, const KVMap& data) { delete block_; block_ = nullptr; BlockBuilder builder(&options); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { + for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { builder.Add(it->first, it->second); } // Open the block @@ -218,23 +207,17 @@ class BlockConstructor: public Constructor { BlockConstructor(); }; -class TableConstructor: public Constructor { +class TableConstructor : public Constructor { public: TableConstructor(const Comparator* cmp) - : Constructor(cmp), - source_(nullptr), table_(nullptr) { - } - ~TableConstructor() { - Reset(); - } + : Constructor(cmp), source_(nullptr), table_(nullptr) {} + ~TableConstructor() { Reset(); } virtual Status FinishImpl(const Options& options, const KVMap& data) { Reset(); StringSink sink; TableBuilder builder(options, &sink); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { + for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { builder.Add(it->first, it->second); ASSERT_TRUE(builder.status().ok()); } @@ -273,9 +256,9 @@ class TableConstructor: public Constructor { }; // A helper class that converts internal format keys into user keys -class KeyConvertingIterator: public Iterator { +class KeyConvertingIterator : public Iterator { public: - explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) {} virtual ~KeyConvertingIterator() { delete iter_; } virtual bool Valid() const { return iter_->Valid(); } virtual void Seek(const Slice& target) { @@ -313,25 +296,20 @@ class KeyConvertingIterator: public Iterator { void operator=(const KeyConvertingIterator&); }; -class MemTableConstructor: public Constructor { +class MemTableConstructor : public Constructor { public: explicit MemTableConstructor(const Comparator* cmp) - : Constructor(cmp), - internal_comparator_(cmp) { + : Constructor(cmp), internal_comparator_(cmp) { memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); } - ~MemTableConstructor() { - memtable_->Unref(); - } + ~MemTableConstructor() { memtable_->Unref(); } virtual Status FinishImpl(const Options& options, const KVMap& data) { memtable_->Unref(); memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); int seq = 1; - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { + for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { memtable_->Add(seq, kTypeValue, it->first, it->second); seq++; } @@ -346,24 +324,19 @@ class MemTableConstructor: public Constructor { MemTable* memtable_; }; -class DBConstructor: public Constructor { +class DBConstructor : public Constructor { public: explicit DBConstructor(const Comparator* cmp) - : Constructor(cmp), - comparator_(cmp) { + : Constructor(cmp), comparator_(cmp) { db_ = nullptr; NewDB(); } - ~DBConstructor() { - delete db_; - } + ~DBConstructor() { delete db_; } virtual Status FinishImpl(const Options& options, const KVMap& data) { delete db_; db_ = nullptr; NewDB(); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { + for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { WriteBatch batch; batch.Put(it->first, it->second); ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); @@ -396,12 +369,7 @@ class DBConstructor: public Constructor { DB* db_; }; -enum TestType { - TABLE_TEST, - BLOCK_TEST, - MEMTABLE_TEST, - DB_TEST -}; +enum TestType { TABLE_TEST, BLOCK_TEST, MEMTABLE_TEST, DB_TEST }; struct TestArgs { TestType type; @@ -410,33 +378,33 @@ struct TestArgs { }; static const TestArgs kTestArgList[] = { - { TABLE_TEST, false, 16 }, - { TABLE_TEST, false, 1 }, - { TABLE_TEST, false, 1024 }, - { TABLE_TEST, true, 16 }, - { TABLE_TEST, true, 1 }, - { TABLE_TEST, true, 1024 }, - - { BLOCK_TEST, false, 16 }, - { BLOCK_TEST, false, 1 }, - { BLOCK_TEST, false, 1024 }, - { BLOCK_TEST, true, 16 }, - { BLOCK_TEST, true, 1 }, - { BLOCK_TEST, true, 1024 }, - - // Restart interval does not matter for memtables - { MEMTABLE_TEST, false, 16 }, - { MEMTABLE_TEST, true, 16 }, - - // Do not bother with restart interval variations for DB - { DB_TEST, false, 16 }, - { DB_TEST, true, 16 }, + {TABLE_TEST, false, 16}, + {TABLE_TEST, false, 1}, + {TABLE_TEST, false, 1024}, + {TABLE_TEST, true, 16}, + {TABLE_TEST, true, 1}, + {TABLE_TEST, true, 1024}, + + {BLOCK_TEST, false, 16}, + {BLOCK_TEST, false, 1}, + {BLOCK_TEST, false, 1024}, + {BLOCK_TEST, true, 16}, + {BLOCK_TEST, true, 1}, + {BLOCK_TEST, true, 1024}, + + // Restart interval does not matter for memtables + {MEMTABLE_TEST, false, 16}, + {MEMTABLE_TEST, true, 16}, + + // Do not bother with restart interval variations for DB + {DB_TEST, false, 16}, + {DB_TEST, true, 16}, }; static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); class Harness { public: - Harness() : constructor_(nullptr) { } + Harness() : constructor_(nullptr) {} void Init(const TestArgs& args) { delete constructor_; @@ -466,9 +434,7 @@ class Harness { } } - ~Harness() { - delete constructor_; - } + ~Harness() { delete constructor_; } void Add(const std::string& key, const std::string& value) { constructor_->Add(key, value); @@ -490,8 +456,7 @@ class Harness { ASSERT_TRUE(!iter->Valid()); iter->SeekToFirst(); for (KVMap::const_iterator model_iter = data.begin(); - model_iter != data.end(); - ++model_iter) { + model_iter != data.end(); ++model_iter) { ASSERT_EQ(ToString(data, model_iter), ToString(iter)); iter->Next(); } @@ -505,8 +470,7 @@ class Harness { ASSERT_TRUE(!iter->Valid()); iter->SeekToLast(); for (KVMap::const_reverse_iterator model_iter = data.rbegin(); - model_iter != data.rend(); - ++model_iter) { + model_iter != data.rend(); ++model_iter) { ASSERT_EQ(ToString(data, model_iter), ToString(iter)); iter->Prev(); } @@ -514,8 +478,7 @@ class Harness { delete iter; } - void TestRandomAccess(Random* rnd, - const std::vector& keys, + void TestRandomAccess(Random* rnd, const std::vector& keys, const KVMap& data) { static const bool kVerbose = false; Iterator* iter = constructor_->NewIterator(); @@ -546,8 +509,8 @@ class Harness { case 2: { std::string key = PickRandomKey(rnd, keys); model_iter = data.lower_bound(key); - if (kVerbose) fprintf(stderr, "Seek '%s'\n", - EscapeString(key).c_str()); + if (kVerbose) + fprintf(stderr, "Seek '%s'\n", EscapeString(key).c_str()); iter->Seek(Slice(key)); ASSERT_EQ(ToString(data, model_iter), ToString(iter)); break; @@ -558,7 +521,7 @@ class Harness { if (kVerbose) fprintf(stderr, "Prev\n"); iter->Prev(); if (model_iter == data.begin()) { - model_iter = data.end(); // Wrap around to invalid value + model_iter = data.end(); // Wrap around to invalid value } else { --model_iter; } @@ -621,8 +584,8 @@ class Harness { break; case 1: { // Attempt to return something smaller than an existing key - if (result.size() > 0 && result[result.size()-1] > '\0') { - result[result.size()-1]--; + if (!result.empty() && result[result.size() - 1] > '\0') { + result[result.size() - 1]--; } break; } @@ -720,8 +683,8 @@ TEST(Harness, Randomized) { for (int num_entries = 0; num_entries < 2000; num_entries += (num_entries < 50 ? 1 : 200)) { if ((num_entries % 10) == 0) { - fprintf(stderr, "case %d of %d: num_entries = %d\n", - (i + 1), int(kNumTestArgs), num_entries); + fprintf(stderr, "case %d of %d: num_entries = %d\n", (i + 1), + int(kNumTestArgs), num_entries); } for (int e = 0; e < num_entries; e++) { std::string v; @@ -735,7 +698,7 @@ TEST(Harness, Randomized) { TEST(Harness, RandomizedLongDB) { Random rnd(test::RandomSeed()); - TestArgs args = { DB_TEST, false, 16 }; + TestArgs args = {DB_TEST, false, 16}; Init(args); int num_entries = 100000; for (int e = 0; e < num_entries; e++) { @@ -757,7 +720,7 @@ TEST(Harness, RandomizedLongDB) { ASSERT_GT(files, 0); } -class MemTableTest { }; +class MemTableTest {}; TEST(MemTableTest, Simple) { InternalKeyComparator cmp(BytewiseComparator()); @@ -774,8 +737,7 @@ TEST(MemTableTest, Simple) { Iterator* iter = memtable->NewIterator(); iter->SeekToFirst(); while (iter->Valid()) { - fprintf(stderr, "key: '%s' -> '%s'\n", - iter->key().ToString().c_str(), + fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(), iter->value().ToString().c_str()); iter->Next(); } @@ -788,14 +750,13 @@ static bool Between(uint64_t val, uint64_t low, uint64_t high) { bool result = (val >= low) && (val <= high); if (!result) { fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", - (unsigned long long)(val), - (unsigned long long)(low), + (unsigned long long)(val), (unsigned long long)(low), (unsigned long long)(high)); } return result; } -class TableTest { }; +class TableTest {}; TEST(TableTest, ApproximateOffsetOfPlain) { TableConstructor c(BytewiseComparator()); @@ -813,18 +774,17 @@ TEST(TableTest, ApproximateOffsetOfPlain) { options.compression = kNoCompression; c.Finish(options, &keys, &kvmap); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); - + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); } static bool SnappyCompressionSupported() { @@ -855,7 +815,7 @@ TEST(TableTest, ApproximateOffsetOfCompressed) { // Expected upper and lower bounds of space used by compressible strings. static const int kSlop = 1000; // Compressor effectiveness varies. - const int expected = 2500; // 10000 * compression ratio (0.25) + const int expected = 2500; // 10000 * compression ratio (0.25) const int min_z = expected - kSlop; const int max_z = expected + kSlop; @@ -871,6 +831,4 @@ TEST(TableTest, ApproximateOffsetOfCompressed) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/table/two_level_iterator.cc b/table/two_level_iterator.cc index 4e6f420e80..5340a4d3e5 100644 --- a/table/two_level_iterator.cc +++ b/table/two_level_iterator.cc @@ -15,13 +15,10 @@ namespace { typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); -class TwoLevelIterator: public Iterator { +class TwoLevelIterator : public Iterator { public: - TwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options); + TwoLevelIterator(Iterator* index_iter, BlockFunction block_function, + void* arg, const ReadOptions& options); virtual ~TwoLevelIterator(); @@ -31,9 +28,7 @@ class TwoLevelIterator: public Iterator { virtual void Next(); virtual void Prev(); - virtual bool Valid() const { - return data_iter_.Valid(); - } + virtual bool Valid() const { return data_iter_.Valid(); } virtual Slice key() const { assert(Valid()); return data_iter_.key(); @@ -67,26 +62,22 @@ class TwoLevelIterator: public Iterator { const ReadOptions options_; Status status_; IteratorWrapper index_iter_; - IteratorWrapper data_iter_; // May be nullptr + IteratorWrapper data_iter_; // May be nullptr // If data_iter_ is non-null, then "data_block_handle_" holds the // "index_value" passed to block_function_ to create the data_iter_. std::string data_block_handle_; }; -TwoLevelIterator::TwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options) +TwoLevelIterator::TwoLevelIterator(Iterator* index_iter, + BlockFunction block_function, void* arg, + const ReadOptions& options) : block_function_(block_function), arg_(arg), options_(options), index_iter_(index_iter), - data_iter_(nullptr) { -} + data_iter_(nullptr) {} -TwoLevelIterator::~TwoLevelIterator() { -} +TwoLevelIterator::~TwoLevelIterator() {} void TwoLevelIterator::Seek(const Slice& target) { index_iter_.Seek(target); @@ -121,7 +112,6 @@ void TwoLevelIterator::Prev() { SkipEmptyDataBlocksBackward(); } - void TwoLevelIterator::SkipEmptyDataBlocksForward() { while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block @@ -158,7 +148,8 @@ void TwoLevelIterator::InitDataBlock() { SetDataIterator(nullptr); } else { Slice handle = index_iter_.value(); - if (data_iter_.iter() != nullptr && handle.compare(data_block_handle_) == 0) { + if (data_iter_.iter() != nullptr && + handle.compare(data_block_handle_) == 0) { // data_iter_ is already constructed with this iterator, so // no need to change anything } else { @@ -171,11 +162,9 @@ void TwoLevelIterator::InitDataBlock() { } // namespace -Iterator* NewTwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options) { +Iterator* NewTwoLevelIterator(Iterator* index_iter, + BlockFunction block_function, void* arg, + const ReadOptions& options) { return new TwoLevelIterator(index_iter, block_function, arg, options); } diff --git a/table/two_level_iterator.h b/table/two_level_iterator.h index a93ba899a0..81ffe809ac 100644 --- a/table/two_level_iterator.h +++ b/table/two_level_iterator.h @@ -22,12 +22,9 @@ struct ReadOptions; // an iterator over the contents of the corresponding block. Iterator* NewTwoLevelIterator( Iterator* index_iter, - Iterator* (*block_function)( - void* arg, - const ReadOptions& options, - const Slice& index_value), - void* arg, - const ReadOptions& options); + Iterator* (*block_function)(void* arg, const ReadOptions& options, + const Slice& index_value), + void* arg, const ReadOptions& options); } // namespace leveldb diff --git a/util/arena.cc b/util/arena.cc index a496ad030b..eadec8acfb 100644 --- a/util/arena.cc +++ b/util/arena.cc @@ -39,8 +39,9 @@ char* Arena::AllocateFallback(size_t bytes) { char* Arena::AllocateAligned(size_t bytes) { const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; - assert((align & (align-1)) == 0); // Pointer size should be a power of 2 - size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + static_assert((align & (align - 1)) == 0, + "Pointer size should be a power of 2"); + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align - 1); size_t slop = (current_mod == 0 ? 0 : align - current_mod); size_t needed = bytes + slop; char* result; @@ -52,7 +53,7 @@ char* Arena::AllocateAligned(size_t bytes) { // AllocateFallback always returned aligned memory result = AllocateFallback(bytes); } - assert((reinterpret_cast(result) & (align-1)) == 0); + assert((reinterpret_cast(result) & (align - 1)) == 0); return result; } diff --git a/util/arena_test.cc b/util/arena_test.cc index 58e870ec44..f34095cc26 100644 --- a/util/arena_test.cc +++ b/util/arena_test.cc @@ -9,11 +9,9 @@ namespace leveldb { -class ArenaTest { }; +class ArenaTest {}; -TEST(ArenaTest, Empty) { - Arena arena; -} +TEST(ArenaTest, Empty) { Arena arena; } TEST(ArenaTest, Simple) { std::vector > allocated; @@ -26,8 +24,9 @@ TEST(ArenaTest, Simple) { if (i % (N / 10) == 0) { s = i; } else { - s = rnd.OneIn(4000) ? rnd.Uniform(6000) : - (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + s = rnd.OneIn(4000) + ? rnd.Uniform(6000) + : (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); } if (s == 0) { // Our arena disallows size 0 allocations. @@ -47,7 +46,7 @@ TEST(ArenaTest, Simple) { bytes += s; allocated.push_back(std::make_pair(s, r)); ASSERT_GE(arena.MemoryUsage(), bytes); - if (i > N/10) { + if (i > N / 10) { ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); } } @@ -63,6 +62,4 @@ TEST(ArenaTest, Simple) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/bloom.cc b/util/bloom.cc index bf3e4ca6e9..097ce7ab2e 100644 --- a/util/bloom.cc +++ b/util/bloom.cc @@ -20,17 +20,14 @@ class BloomFilterPolicy : public FilterPolicy { size_t k_; public: - explicit BloomFilterPolicy(int bits_per_key) - : bits_per_key_(bits_per_key) { + explicit BloomFilterPolicy(int bits_per_key) : bits_per_key_(bits_per_key) { // We intentionally round down to reduce probing cost a little bit k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) if (k_ < 1) k_ = 1; if (k_ > 30) k_ = 30; } - virtual const char* Name() const { - return "leveldb.BuiltinBloomFilter2"; - } + virtual const char* Name() const { return "leveldb.BuiltinBloomFilter2"; } virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { // Compute bloom filter size (in both bits and bytes) @@ -54,7 +51,7 @@ class BloomFilterPolicy : public FilterPolicy { const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits for (size_t j = 0; j < k_; j++) { const uint32_t bitpos = h % bits; - array[bitpos/8] |= (1 << (bitpos % 8)); + array[bitpos / 8] |= (1 << (bitpos % 8)); h += delta; } } @@ -69,7 +66,7 @@ class BloomFilterPolicy : public FilterPolicy { // Use the encoded k so that we can read filters generated by // bloom filters created using different parameters. - const size_t k = array[len-1]; + const size_t k = array[len - 1]; if (k > 30) { // Reserved for potentially new encodings for short bloom filters. // Consider it a match. @@ -80,13 +77,13 @@ class BloomFilterPolicy : public FilterPolicy { const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits for (size_t j = 0; j < k; j++) { const uint32_t bitpos = h % bits; - if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + if ((array[bitpos / 8] & (1 << (bitpos % 8))) == 0) return false; h += delta; } return true; } }; -} +} // namespace const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { return new BloomFilterPolicy(bits_per_key); diff --git a/util/bloom_test.cc b/util/bloom_test.cc index 1b87a2be3f..71c4115615 100644 --- a/util/bloom_test.cc +++ b/util/bloom_test.cc @@ -25,20 +25,16 @@ class BloomTest { std::vector keys_; public: - BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + BloomTest() : policy_(NewBloomFilterPolicy(10)) {} - ~BloomTest() { - delete policy_; - } + ~BloomTest() { delete policy_; } void Reset() { keys_.clear(); filter_.clear(); } - void Add(const Slice& s) { - keys_.push_back(s.ToString()); - } + void Add(const Slice& s) { keys_.push_back(s.ToString()); } void Build() { std::vector key_slices; @@ -52,16 +48,14 @@ class BloomTest { if (kVerbose >= 2) DumpFilter(); } - size_t FilterSize() const { - return filter_.size(); - } + size_t FilterSize() const { return filter_.size(); } void DumpFilter() { fprintf(stderr, "F("); - for (size_t i = 0; i+1 < filter_.size(); i++) { + for (size_t i = 0; i + 1 < filter_.size(); i++) { const unsigned int c = static_cast(filter_[i]); for (int j = 0; j < 8; j++) { - fprintf(stderr, "%c", (c & (1 <= 1) { fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", - rate*100.0, length, static_cast(FilterSize())); + rate * 100.0, length, static_cast(FilterSize())); } - ASSERT_LE(rate, 0.02); // Must not be over 2% - if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often - else good_filters++; + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) + mediocre_filters++; // Allowed, but not too often + else + good_filters++; } if (kVerbose >= 1) { - fprintf(stderr, "Filters: %d good, %d mediocre\n", - good_filters, mediocre_filters); + fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters, + mediocre_filters); } - ASSERT_LE(mediocre_filters, good_filters/5); + ASSERT_LE(mediocre_filters, good_filters / 5); } // Different bits-per-byte } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/cache.cc b/util/cache.cc index 25b51b535e..0f801ccc8e 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -14,8 +14,7 @@ namespace leveldb { -Cache::~Cache() { -} +Cache::~Cache() {} namespace { @@ -46,12 +45,12 @@ struct LRUHandle { LRUHandle* next_hash; LRUHandle* next; LRUHandle* prev; - size_t charge; // TODO(opt): Only allow uint32_t? + size_t charge; // TODO(opt): Only allow uint32_t? size_t key_length; - bool in_cache; // Whether entry is in the cache. - uint32_t refs; // References, including cache reference, if present. - uint32_t hash; // Hash of key(); used for fast sharding and comparisons - char key_data[1]; // Beginning of key + bool in_cache; // Whether entry is in the cache. + uint32_t refs; // References, including cache reference, if present. + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key Slice key() const { // next_ is only equal to this if the LRU handle is the list head of an @@ -114,8 +113,7 @@ class HandleTable { // pointer to the trailing slot in the corresponding linked list. LRUHandle** FindPointer(const Slice& key, uint32_t hash) { LRUHandle** ptr = &list_[hash & (length_ - 1)]; - while (*ptr != nullptr && - ((*ptr)->hash != hash || key != (*ptr)->key())) { + while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) { ptr = &(*ptr)->next_hash; } return ptr; @@ -158,8 +156,8 @@ class LRUCache { void SetCapacity(size_t capacity) { capacity_ = capacity; } // Like Cache methods, but with an extra "hash" parameter. - Cache::Handle* Insert(const Slice& key, uint32_t hash, - void* value, size_t charge, + Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value, + size_t charge, void (*deleter)(const Slice& key, void* value)); Cache::Handle* Lookup(const Slice& key, uint32_t hash); void Release(Cache::Handle* handle); @@ -172,7 +170,7 @@ class LRUCache { private: void LRU_Remove(LRUHandle* e); - void LRU_Append(LRUHandle*list, LRUHandle* e); + void LRU_Append(LRUHandle* list, LRUHandle* e); void Ref(LRUHandle* e); void Unref(LRUHandle* e); bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); @@ -206,7 +204,7 @@ LRUCache::LRUCache() : capacity_(0), usage_(0) { LRUCache::~LRUCache() { assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle - for (LRUHandle* e = lru_.next; e != &lru_; ) { + for (LRUHandle* e = lru_.next; e != &lru_;) { LRUHandle* next = e->next; assert(e->in_cache); e->in_cache = false; @@ -265,13 +263,14 @@ void LRUCache::Release(Cache::Handle* handle) { Unref(reinterpret_cast(handle)); } -Cache::Handle* LRUCache::Insert( - const Slice& key, uint32_t hash, void* value, size_t charge, - void (*deleter)(const Slice& key, void* value)) { +Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value, + size_t charge, + void (*deleter)(const Slice& key, + void* value)) { MutexLock l(&mutex_); - LRUHandle* e = reinterpret_cast( - malloc(sizeof(LRUHandle)-1 + key.size())); + LRUHandle* e = + reinterpret_cast(malloc(sizeof(LRUHandle) - 1 + key.size())); e->value = value; e->deleter = deleter; e->charge = charge; @@ -346,19 +345,16 @@ class ShardedLRUCache : public Cache { return Hash(s.data(), s.size(), 0); } - static uint32_t Shard(uint32_t hash) { - return hash >> (32 - kNumShardBits); - } + static uint32_t Shard(uint32_t hash) { return hash >> (32 - kNumShardBits); } public: - explicit ShardedLRUCache(size_t capacity) - : last_id_(0) { + explicit ShardedLRUCache(size_t capacity) : last_id_(0) { const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; for (int s = 0; s < kNumShards; s++) { shard_[s].SetCapacity(per_shard); } } - virtual ~ShardedLRUCache() { } + virtual ~ShardedLRUCache() {} virtual Handle* Insert(const Slice& key, void* value, size_t charge, void (*deleter)(const Slice& key, void* value)) { const uint32_t hash = HashSlice(key); @@ -399,8 +395,6 @@ class ShardedLRUCache : public Cache { } // end anonymous namespace -Cache* NewLRUCache(size_t capacity) { - return new ShardedLRUCache(capacity); -} +Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); } } // namespace leveldb diff --git a/util/cache_test.cc b/util/cache_test.cc index 8647febb9d..d5c1a1dafd 100644 --- a/util/cache_test.cc +++ b/util/cache_test.cc @@ -37,13 +37,9 @@ class CacheTest { std::vector deleted_values_; Cache* cache_; - CacheTest() : cache_(NewLRUCache(kCacheSize)) { - current_ = this; - } + CacheTest() : cache_(NewLRUCache(kCacheSize)) { current_ = this; } - ~CacheTest() { - delete cache_; - } + ~CacheTest() { delete cache_; } int Lookup(int key) { Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); @@ -64,9 +60,7 @@ class CacheTest { &CacheTest::Deleter); } - void Erase(int key) { - cache_->Erase(EncodeKey(key)); - } + void Erase(int key) { cache_->Erase(EncodeKey(key)); } }; CacheTest* CacheTest::current_; @@ -75,18 +69,18 @@ TEST(CacheTest, HitAndMiss) { Insert(100, 101); ASSERT_EQ(101, Lookup(100)); - ASSERT_EQ(-1, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); Insert(200, 201); ASSERT_EQ(101, Lookup(100)); ASSERT_EQ(201, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(300)); Insert(100, 102); ASSERT_EQ(102, Lookup(100)); ASSERT_EQ(201, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(300)); ASSERT_EQ(1, deleted_keys_.size()); ASSERT_EQ(100, deleted_keys_[0]); @@ -100,14 +94,14 @@ TEST(CacheTest, Erase) { Insert(100, 101); Insert(200, 201); Erase(100); - ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(-1, Lookup(100)); ASSERT_EQ(201, Lookup(200)); ASSERT_EQ(1, deleted_keys_.size()); ASSERT_EQ(100, deleted_keys_[0]); ASSERT_EQ(101, deleted_values_[0]); Erase(100); - ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(-1, Lookup(100)); ASSERT_EQ(201, Lookup(200)); ASSERT_EQ(1, deleted_keys_.size()); } @@ -146,8 +140,8 @@ TEST(CacheTest, EvictionPolicy) { // Frequently used entry must be kept around, // as must things that are still in use. for (int i = 0; i < kCacheSize + 100; i++) { - Insert(1000+i, 2000+i); - ASSERT_EQ(2000+i, Lookup(1000+i)); + Insert(1000 + i, 2000 + i); + ASSERT_EQ(2000 + i, Lookup(1000 + i)); ASSERT_EQ(101, Lookup(100)); } ASSERT_EQ(101, Lookup(100)); @@ -160,12 +154,12 @@ TEST(CacheTest, UseExceedsCacheSize) { // Overfill the cache, keeping handles on all inserted entries. std::vector h; for (int i = 0; i < kCacheSize + 100; i++) { - h.push_back(InsertAndReturnHandle(1000+i, 2000+i)); + h.push_back(InsertAndReturnHandle(1000 + i, 2000 + i)); } // Check that all the entries can be found in the cache. for (int i = 0; i < h.size(); i++) { - ASSERT_EQ(2000+i, Lookup(1000+i)); + ASSERT_EQ(2000 + i, Lookup(1000 + i)); } for (int i = 0; i < h.size(); i++) { @@ -181,9 +175,9 @@ TEST(CacheTest, HeavyEntries) { const int kHeavy = 10; int added = 0; int index = 0; - while (added < 2*kCacheSize) { + while (added < 2 * kCacheSize) { const int weight = (index & 1) ? kLight : kHeavy; - Insert(index, 1000+index, weight); + Insert(index, 1000 + index, weight); added += weight; index++; } @@ -194,10 +188,10 @@ TEST(CacheTest, HeavyEntries) { int r = Lookup(i); if (r >= 0) { cached_weight += weight; - ASSERT_EQ(1000+i, r); + ASSERT_EQ(1000 + i, r); } } - ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); + ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10); } TEST(CacheTest, NewId) { @@ -229,6 +223,4 @@ TEST(CacheTest, ZeroSizeCache) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/coding.cc b/util/coding.cc index 1a9e3337bd..e2089df983 100644 --- a/util/coding.cc +++ b/util/coding.cc @@ -6,29 +6,29 @@ namespace leveldb { -void EncodeFixed32(char* buf, uint32_t value) { +void EncodeFixed32(char* dst, uint32_t value) { if (port::kLittleEndian) { - memcpy(buf, &value, sizeof(value)); + memcpy(dst, &value, sizeof(value)); } else { - buf[0] = value & 0xff; - buf[1] = (value >> 8) & 0xff; - buf[2] = (value >> 16) & 0xff; - buf[3] = (value >> 24) & 0xff; + dst[0] = value & 0xff; + dst[1] = (value >> 8) & 0xff; + dst[2] = (value >> 16) & 0xff; + dst[3] = (value >> 24) & 0xff; } } -void EncodeFixed64(char* buf, uint64_t value) { +void EncodeFixed64(char* dst, uint64_t value) { if (port::kLittleEndian) { - memcpy(buf, &value, sizeof(value)); + memcpy(dst, &value, sizeof(value)); } else { - buf[0] = value & 0xff; - buf[1] = (value >> 8) & 0xff; - buf[2] = (value >> 16) & 0xff; - buf[3] = (value >> 24) & 0xff; - buf[4] = (value >> 32) & 0xff; - buf[5] = (value >> 40) & 0xff; - buf[6] = (value >> 48) & 0xff; - buf[7] = (value >> 56) & 0xff; + dst[0] = value & 0xff; + dst[1] = (value >> 8) & 0xff; + dst[2] = (value >> 16) & 0xff; + dst[3] = (value >> 24) & 0xff; + dst[4] = (value >> 32) & 0xff; + dst[5] = (value >> 40) & 0xff; + dst[6] = (value >> 48) & 0xff; + dst[7] = (value >> 56) & 0xff; } } @@ -48,26 +48,26 @@ char* EncodeVarint32(char* dst, uint32_t v) { // Operate on characters as unsigneds unsigned char* ptr = reinterpret_cast(dst); static const int B = 128; - if (v < (1<<7)) { + if (v < (1 << 7)) { *(ptr++) = v; - } else if (v < (1<<14)) { + } else if (v < (1 << 14)) { *(ptr++) = v | B; - *(ptr++) = v>>7; - } else if (v < (1<<21)) { + *(ptr++) = v >> 7; + } else if (v < (1 << 21)) { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = v>>14; - } else if (v < (1<<28)) { + *(ptr++) = (v >> 7) | B; + *(ptr++) = v >> 14; + } else if (v < (1 << 28)) { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = v>>21; + *(ptr++) = (v >> 7) | B; + *(ptr++) = (v >> 14) | B; + *(ptr++) = v >> 21; } else { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = (v>>21) | B; - *(ptr++) = v>>28; + *(ptr++) = (v >> 7) | B; + *(ptr++) = (v >> 14) | B; + *(ptr++) = (v >> 21) | B; + *(ptr++) = v >> 28; } return reinterpret_cast(ptr); } @@ -109,8 +109,7 @@ int VarintLength(uint64_t v) { return len; } -const char* GetVarint32PtrFallback(const char* p, - const char* limit, +const char* GetVarint32PtrFallback(const char* p, const char* limit, uint32_t* value) { uint32_t result = 0; for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { @@ -181,8 +180,7 @@ const char* GetLengthPrefixedSlice(const char* p, const char* limit, bool GetLengthPrefixedSlice(Slice* input, Slice* result) { uint32_t len; - if (GetVarint32(input, &len) && - input->size() >= len) { + if (GetVarint32(input, &len) && input->size() >= len) { *result = Slice(input->data(), len); input->remove_prefix(len); return true; diff --git a/util/coding.h b/util/coding.h index f0fa2cb4db..d9eeaa31da 100644 --- a/util/coding.h +++ b/util/coding.h @@ -64,10 +64,10 @@ inline uint32_t DecodeFixed32(const char* ptr) { memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load return result; } else { - return ((static_cast(static_cast(ptr[0]))) - | (static_cast(static_cast(ptr[1])) << 8) - | (static_cast(static_cast(ptr[2])) << 16) - | (static_cast(static_cast(ptr[3])) << 24)); + return ((static_cast(static_cast(ptr[0]))) | + (static_cast(static_cast(ptr[1])) << 8) | + (static_cast(static_cast(ptr[2])) << 16) | + (static_cast(static_cast(ptr[3])) << 24)); } } @@ -85,11 +85,9 @@ inline uint64_t DecodeFixed64(const char* ptr) { } // Internal routine for use by fallback path of GetVarint32Ptr -const char* GetVarint32PtrFallback(const char* p, - const char* limit, +const char* GetVarint32PtrFallback(const char* p, const char* limit, uint32_t* value); -inline const char* GetVarint32Ptr(const char* p, - const char* limit, +inline const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* value) { if (p < limit) { uint32_t result = *(reinterpret_cast(p)); diff --git a/util/coding_test.cc b/util/coding_test.cc index d315e19507..0d2a0c51f6 100644 --- a/util/coding_test.cc +++ b/util/coding_test.cc @@ -9,7 +9,7 @@ namespace leveldb { -class Coding { }; +class Coding {}; TEST(Coding, Fixed32) { std::string s; @@ -39,15 +39,15 @@ TEST(Coding, Fixed64) { uint64_t v = static_cast(1) << power; uint64_t actual; actual = DecodeFixed64(p); - ASSERT_EQ(v-1, actual); + ASSERT_EQ(v - 1, actual); p += sizeof(uint64_t); actual = DecodeFixed64(p); - ASSERT_EQ(v+0, actual); + ASSERT_EQ(v + 0, actual); p += sizeof(uint64_t); actual = DecodeFixed64(p); - ASSERT_EQ(v+1, actual); + ASSERT_EQ(v + 1, actual); p += sizeof(uint64_t); } } @@ -108,8 +108,8 @@ TEST(Coding, Varint64) { // Test values near powers of two const uint64_t power = 1ull << k; values.push_back(power); - values.push_back(power-1); - values.push_back(power+1); + values.push_back(power - 1); + values.push_back(power + 1); } std::string s; @@ -134,8 +134,8 @@ TEST(Coding, Varint64) { TEST(Coding, Varint32Overflow) { uint32_t result; std::string input("\x81\x82\x83\x84\x85\x11"); - ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) - == nullptr); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), + &result) == nullptr); } TEST(Coding, Varint32Truncation) { @@ -146,16 +146,16 @@ TEST(Coding, Varint32Truncation) { for (size_t len = 0; len < s.size() - 1; len++) { ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE( - GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != nullptr); + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != + nullptr); ASSERT_EQ(large_value, result); } TEST(Coding, Varint64Overflow) { uint64_t result; std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); - ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) - == nullptr); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), + &result) == nullptr); } TEST(Coding, Varint64Truncation) { @@ -166,8 +166,8 @@ TEST(Coding, Varint64Truncation) { for (size_t len = 0; len < s.size() - 1; len++) { ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE( - GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != nullptr); + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != + nullptr); ASSERT_EQ(large_value, result); } @@ -193,6 +193,4 @@ TEST(Coding, Strings) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/comparator.cc b/util/comparator.cc index e1e2963307..26d1eb3b74 100644 --- a/util/comparator.cc +++ b/util/comparator.cc @@ -13,24 +13,21 @@ namespace leveldb { -Comparator::~Comparator() { } +Comparator::~Comparator() {} namespace { class BytewiseComparatorImpl : public Comparator { public: - BytewiseComparatorImpl() { } + BytewiseComparatorImpl() {} - virtual const char* Name() const { - return "leveldb.BytewiseComparator"; - } + virtual const char* Name() const { return "leveldb.BytewiseComparator"; } virtual int Compare(const Slice& a, const Slice& b) const { return a.compare(b); } - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const { + virtual void FindShortestSeparator(std::string* start, + const Slice& limit) const { // Find length of common prefix size_t min_length = std::min(start->size(), limit.size()); size_t diff_index = 0; @@ -59,7 +56,7 @@ class BytewiseComparatorImpl : public Comparator { const uint8_t byte = (*key)[i]; if (byte != static_cast(0xff)) { (*key)[i] = byte + 1; - key->resize(i+1); + key->resize(i + 1); return; } } diff --git a/util/crc32c.cc b/util/crc32c.cc index 4f1d80fb08..c2e61f7dba 100644 --- a/util/crc32c.cc +++ b/util/crc32c.cc @@ -256,8 +256,8 @@ inline uint32_t ReadUint32LE(const uint8_t* buffer) { template constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) { return reinterpret_cast( - (reinterpret_cast(pointer) + (N - 1)) - & ~static_cast(N - 1)); + (reinterpret_cast(pointer) + (N - 1)) & + ~static_cast(N - 1)); } } // namespace @@ -273,14 +273,14 @@ static bool CanAccelerateCRC32C() { return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue; } -uint32_t Extend(uint32_t crc, const char* buf, size_t size) { +uint32_t Extend(uint32_t crc, const char* data, size_t n) { static bool accelerate = CanAccelerateCRC32C(); if (accelerate) { - return port::AcceleratedCRC32C(crc, buf, size); + return port::AcceleratedCRC32C(crc, data, n); } - const uint8_t* p = reinterpret_cast(buf); - const uint8_t* e = p + size; + const uint8_t* p = reinterpret_cast(data); + const uint8_t* e = p + n; uint32_t l = crc ^ kCRC32Xor; // Process one byte at a time. diff --git a/util/crc32c.h b/util/crc32c.h index 7864688b1c..98fabb0d2f 100644 --- a/util/crc32c.h +++ b/util/crc32c.h @@ -17,9 +17,7 @@ namespace crc32c { uint32_t Extend(uint32_t init_crc, const char* data, size_t n); // Return the crc32c of data[0,n-1] -inline uint32_t Value(const char* data, size_t n) { - return Extend(0, data, n); -} +inline uint32_t Value(const char* data, size_t n) { return Extend(0, data, n); } static const uint32_t kMaskDelta = 0xa282ead8ul; diff --git a/util/crc32c_test.cc b/util/crc32c_test.cc index 4b957ee120..dbd2ba4438 100644 --- a/util/crc32c_test.cc +++ b/util/crc32c_test.cc @@ -8,7 +8,7 @@ namespace leveldb { namespace crc32c { -class CRC { }; +class CRC {}; TEST(CRC, StandardResults) { // From rfc3720 section B.4. @@ -31,29 +31,18 @@ TEST(CRC, StandardResults) { ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); unsigned char data[48] = { - 0x01, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x18, - 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); } -TEST(CRC, Values) { - ASSERT_NE(Value("a", 1), Value("foo", 3)); -} +TEST(CRC, Values) { ASSERT_NE(Value("a", 1), Value("foo", 3)); } TEST(CRC, Extend) { - ASSERT_EQ(Value("hello world", 11), - Extend(Value("hello ", 6), "world", 5)); + ASSERT_EQ(Value("hello world", 11), Extend(Value("hello ", 6), "world", 5)); } TEST(CRC, Mask) { @@ -67,6 +56,4 @@ TEST(CRC, Mask) { } // namespace crc32c } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/env.cc b/util/env.cc index 40a1363913..6cd5f2e37e 100644 --- a/util/env.cc +++ b/util/env.cc @@ -6,27 +6,21 @@ namespace leveldb { -Env::~Env() { -} +Env::~Env() {} Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { return Status::NotSupported("NewAppendableFile", fname); } -SequentialFile::~SequentialFile() { -} +SequentialFile::~SequentialFile() {} -RandomAccessFile::~RandomAccessFile() { -} +RandomAccessFile::~RandomAccessFile() {} -WritableFile::~WritableFile() { -} +WritableFile::~WritableFile() {} -Logger::~Logger() { -} +Logger::~Logger() {} -FileLock::~FileLock() { -} +FileLock::~FileLock() {} void Log(Logger* info_log, const char* format, ...) { if (info_log != nullptr) { @@ -38,8 +32,7 @@ void Log(Logger* info_log, const char* format, ...) { } static Status DoWriteStringToFile(Env* env, const Slice& data, - const std::string& fname, - bool should_sync) { + const std::string& fname, bool should_sync) { WritableFile* file; Status s = env->NewWritableFile(fname, &file); if (!s.ok()) { @@ -94,7 +87,6 @@ Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { return s; } -EnvWrapper::~EnvWrapper() { -} +EnvWrapper::~EnvWrapper() {} } // namespace leveldb diff --git a/util/env_posix.cc b/util/env_posix.cc index 362adb3b0b..8c74f5abc2 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -32,8 +32,8 @@ #include "leveldb/status.h" #include "port/port.h" #include "port/thread_annotations.h" -#include "util/posix_logger.h" #include "util/env_posix_test_helper.h" +#include "util/posix_logger.h" namespace leveldb { @@ -76,8 +76,7 @@ class Limiter { int old_acquires_allowed = acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); - if (old_acquires_allowed > 0) - return true; + if (old_acquires_allowed > 0) return true; acquires_allowed_.fetch_add(1, std::memory_order_relaxed); return false; @@ -85,9 +84,7 @@ class Limiter { // Release a resource acquired by a previous call to Acquire() that returned // true. - void Release() { - acquires_allowed_.fetch_add(1, std::memory_order_relaxed); - } + void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } private: // The number of available resources. @@ -193,7 +190,7 @@ class PosixRandomAccessFile final : public RandomAccessFile { private: const bool has_permanent_fd_; // If false, the file is opened on every read. - const int fd_; // -1 if has_permanent_fd_ is false. + const int fd_; // -1 if has_permanent_fd_ is false. Limiter* const fd_limiter_; const std::string filename_; }; @@ -214,7 +211,9 @@ class PosixMmapReadableFile final : public RandomAccessFile { // instance is destroyed. PosixMmapReadableFile(std::string filename, char* mmap_base, size_t length, Limiter* mmap_limiter) - : mmap_base_(mmap_base), length_(length), mmap_limiter_(mmap_limiter), + : mmap_base_(mmap_base), + length_(length), + mmap_limiter_(mmap_limiter), filename_(std::move(filename)) {} ~PosixMmapReadableFile() override { @@ -243,8 +242,11 @@ class PosixMmapReadableFile final : public RandomAccessFile { class PosixWritableFile final : public WritableFile { public: PosixWritableFile(std::string filename, int fd) - : pos_(0), fd_(fd), is_manifest_(IsManifest(filename)), - filename_(std::move(filename)), dirname_(Dirname(filename_)) {} + : pos_(0), + fd_(fd), + is_manifest_(IsManifest(filename)), + filename_(std::move(filename)), + dirname_(Dirname(filename_)) {} ~PosixWritableFile() override { if (fd_ >= 0) { @@ -292,9 +294,7 @@ class PosixWritableFile final : public WritableFile { return status; } - Status Flush() override { - return FlushBuffer(); - } + Status Flush() override { return FlushBuffer(); } Status Sync() override { // Ensure new files referred to by the manifest are in the filesystem. @@ -517,12 +517,12 @@ class PosixEnv : public Env { uint64_t file_size; Status status = GetFileSize(filename, &file_size); if (status.ok()) { - void* mmap_base = ::mmap(/*addr=*/nullptr, file_size, PROT_READ, - MAP_SHARED, fd, 0); + void* mmap_base = + ::mmap(/*addr=*/nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0); if (mmap_base != MAP_FAILED) { - *result = new PosixMmapReadableFile( - filename, reinterpret_cast(mmap_base), file_size, - &mmap_limiter_); + *result = new PosixMmapReadableFile(filename, + reinterpret_cast(mmap_base), + file_size, &mmap_limiter_); } else { status = PosixError(filename, errno); } @@ -691,9 +691,7 @@ class PosixEnv : public Env { return static_cast(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec; } - void SleepForMicroseconds(int micros) override { - ::usleep(micros); - } + void SleepForMicroseconds(int micros) override { ::usleep(micros); } private: void BackgroundThreadMain(); @@ -712,11 +710,10 @@ class PosixEnv : public Env { explicit BackgroundWorkItem(void (*function)(void* arg), void* arg) : function(function), arg(arg) {} - void (* const function)(void*); + void (*const function)(void*); void* const arg; }; - port::Mutex background_work_mutex_; port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_); bool started_background_thread_ GUARDED_BY(background_work_mutex_); @@ -726,13 +723,11 @@ class PosixEnv : public Env { PosixLockTable locks_; // Thread-safe. Limiter mmap_limiter_; // Thread-safe. - Limiter fd_limiter_; // Thread-safe. + Limiter fd_limiter_; // Thread-safe. }; // Return the maximum number of concurrent mmaps. -int MaxMmaps() { - return g_mmap_limit; -} +int MaxMmaps() { return g_mmap_limit; } // Return the maximum number of read-only files to keep open. int MaxOpenFiles() { @@ -758,8 +753,7 @@ PosixEnv::PosixEnv() : background_work_cv_(&background_work_mutex_), started_background_thread_(false), mmap_limiter_(MaxMmaps()), - fd_limiter_(MaxOpenFiles()) { -} + fd_limiter_(MaxOpenFiles()) {} void PosixEnv::Schedule( void (*background_work_function)(void* background_work_arg), @@ -792,8 +786,7 @@ void PosixEnv::BackgroundThreadMain() { } assert(!background_work_queue_.empty()); - auto background_work_function = - background_work_queue_.front().function; + auto background_work_function = background_work_queue_.front().function; void* background_work_arg = background_work_queue_.front().arg; background_work_queue_.pop(); @@ -816,7 +809,7 @@ namespace { // static PlatformSingletonEnv default_env; // return default_env.env(); // } -template +template class SingletonEnv { public: SingletonEnv() { @@ -851,7 +844,7 @@ class SingletonEnv { }; #if !defined(NDEBUG) -template +template std::atomic SingletonEnv::env_initialized_; #endif // !defined(NDEBUG) diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index e28df9a5ea..6a2a1fc171 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -3,21 +3,19 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "leveldb/env.h" - #include "port/port.h" -#include "util/testharness.h" #include "util/env_posix_test_helper.h" +#include "util/testharness.h" namespace leveldb { -static const int kDelayMicros = 100000; static const int kReadOnlyFileLimit = 4; static const int kMMapLimit = 4; class EnvPosixTest { public: Env* env_; - EnvPosixTest() : env_(Env::Default()) { } + EnvPosixTest() : env_(Env::Default()) {} static void SetFileLimits(int read_only_file_limit, int mmap_limit) { EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); diff --git a/util/env_test.cc b/util/env_test.cc index b204089f81..3e81261dc2 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -16,13 +16,11 @@ namespace leveldb { static const int kDelayMicros = 100000; -static const int kReadOnlyFileLimit = 4; -static const int kMMapLimit = 4; class EnvTest { public: Env* env_; - EnvTest() : env_(Env::Default()) { } + EnvTest() : env_(Env::Default()) {} }; namespace { @@ -97,7 +95,7 @@ TEST(EnvTest, RunMany) { const int id_; // Order# for the execution of this callback. Callback(std::atomic* last_id_ptr, int id) - : last_id_ptr_(last_id_ptr), id_(id) { } + : last_id_ptr_(last_id_ptr), id_(id) {} static void Run(void* arg) { Callback* callback = reinterpret_cast(arg); @@ -125,7 +123,7 @@ struct State { int val GUARDED_BY(mu); int num_running GUARDED_BY(mu); - State(int val, int num_running) : val(val), num_running(num_running) { } + State(int val, int num_running) : val(val), num_running(num_running) {} }; static void ThreadBody(void* arg) { @@ -164,8 +162,8 @@ TEST(EnvTest, TestOpenNonExistentFile) { ASSERT_TRUE(!env_->FileExists(non_existent_file)); RandomAccessFile* random_access_file; - Status status = env_->NewRandomAccessFile( - non_existent_file, &random_access_file); + Status status = + env_->NewRandomAccessFile(non_existent_file, &random_access_file); ASSERT_TRUE(status.IsNotFound()); SequentialFile* sequential_file; @@ -223,6 +221,4 @@ TEST(EnvTest, ReopenAppendableFile) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/env_windows.cc b/util/env_windows.cc index 14e41e9b4f..c537938655 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -122,8 +122,7 @@ class Limiter { int old_acquires_allowed = acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); - if (old_acquires_allowed > 0) - return true; + if (old_acquires_allowed > 0) return true; acquires_allowed_.fetch_add(1, std::memory_order_relaxed); return false; @@ -131,9 +130,7 @@ class Limiter { // Release a resource acquired by a previous call to Acquire() that returned // true. - void Release() { - acquires_allowed_.fetch_add(1, std::memory_order_relaxed); - } + void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } private: // The number of available resources. diff --git a/util/filter_policy.cc b/util/filter_policy.cc index 7b045c8c91..90fd754d64 100644 --- a/util/filter_policy.cc +++ b/util/filter_policy.cc @@ -6,6 +6,6 @@ namespace leveldb { -FilterPolicy::~FilterPolicy() { } +FilterPolicy::~FilterPolicy() {} } // namespace leveldb diff --git a/util/hash.cc b/util/hash.cc index ed439ce7a2..67dc134e28 100644 --- a/util/hash.cc +++ b/util/hash.cc @@ -2,15 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "util/hash.h" + #include + #include "util/coding.h" -#include "util/hash.h" // The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through // between switch labels. The real definition should be provided externally. // This one is a fallback version for unsupported compilers. #ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED do { } while (0) +#define FALLTHROUGH_INTENDED \ + do { \ + } while (0) #endif namespace leveldb { @@ -48,5 +52,4 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) { return h; } - } // namespace leveldb diff --git a/util/hash_test.cc b/util/hash_test.cc index eaa1c92c23..8f579cc17e 100644 --- a/util/hash_test.cc +++ b/util/hash_test.cc @@ -7,7 +7,7 @@ namespace leveldb { -class HASH { }; +class HASH {}; TEST(HASH, SignedUnsignedIssue) { const unsigned char data1[1] = {0x62}; @@ -15,18 +15,10 @@ TEST(HASH, SignedUnsignedIssue) { const unsigned char data3[3] = {0xe2, 0x99, 0xa5}; const unsigned char data4[4] = {0xe1, 0x80, 0xb9, 0x32}; const unsigned char data5[48] = { - 0x01, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x18, - 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ASSERT_EQ(Hash(0, 0, 0xbc9f1d34), 0xbc9f1d34); @@ -49,6 +41,4 @@ TEST(HASH, SignedUnsignedIssue) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/histogram.cc b/util/histogram.cc index bb95f583ea..65092c88f2 100644 --- a/util/histogram.cc +++ b/util/histogram.cc @@ -2,36 +2,174 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "util/histogram.h" + #include #include + #include "port/port.h" -#include "util/histogram.h" namespace leveldb { const double Histogram::kBucketLimit[kNumBuckets] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, - 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, - 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, - 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, - 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, - 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, - 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, - 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, - 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, - 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, - 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, - 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, - 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, - 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, - 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, - 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, - 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, - 1e200, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 12, + 14, + 16, + 18, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 60, + 70, + 80, + 90, + 100, + 120, + 140, + 160, + 180, + 200, + 250, + 300, + 350, + 400, + 450, + 500, + 600, + 700, + 800, + 900, + 1000, + 1200, + 1400, + 1600, + 1800, + 2000, + 2500, + 3000, + 3500, + 4000, + 4500, + 5000, + 6000, + 7000, + 8000, + 9000, + 10000, + 12000, + 14000, + 16000, + 18000, + 20000, + 25000, + 30000, + 35000, + 40000, + 45000, + 50000, + 60000, + 70000, + 80000, + 90000, + 100000, + 120000, + 140000, + 160000, + 180000, + 200000, + 250000, + 300000, + 350000, + 400000, + 450000, + 500000, + 600000, + 700000, + 800000, + 900000, + 1000000, + 1200000, + 1400000, + 1600000, + 1800000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, + 4500000, + 5000000, + 6000000, + 7000000, + 8000000, + 9000000, + 10000000, + 12000000, + 14000000, + 16000000, + 18000000, + 20000000, + 25000000, + 30000000, + 35000000, + 40000000, + 45000000, + 50000000, + 60000000, + 70000000, + 80000000, + 90000000, + 100000000, + 120000000, + 140000000, + 160000000, + 180000000, + 200000000, + 250000000, + 300000000, + 350000000, + 400000000, + 450000000, + 500000000, + 600000000, + 700000000, + 800000000, + 900000000, + 1000000000, + 1200000000, + 1400000000, + 1600000000, + 1800000000, + 2000000000, + 2500000000.0, + 3000000000.0, + 3500000000.0, + 4000000000.0, + 4500000000.0, + 5000000000.0, + 6000000000.0, + 7000000000.0, + 8000000000.0, + 9000000000.0, + 1e200, }; void Histogram::Clear() { - min_ = kBucketLimit[kNumBuckets-1]; + min_ = kBucketLimit[kNumBuckets - 1]; max_ = 0; num_ = 0; sum_ = 0; @@ -66,9 +204,7 @@ void Histogram::Merge(const Histogram& other) { } } -double Histogram::Median() const { - return Percentile(50.0); -} +double Histogram::Median() const { return Percentile(50.0); } double Histogram::Percentile(double p) const { double threshold = num_ * (p / 100.0); @@ -77,7 +213,7 @@ double Histogram::Percentile(double p) const { sum += buckets_[b]; if (sum >= threshold) { // Scale linearly within this bucket - double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double left_point = (b == 0) ? 0 : kBucketLimit[b - 1]; double right_point = kBucketLimit[b]; double left_sum = sum - buckets_[b]; double right_sum = sum; @@ -105,12 +241,10 @@ double Histogram::StandardDeviation() const { std::string Histogram::ToString() const { std::string r; char buf[200]; - snprintf(buf, sizeof(buf), - "Count: %.0f Average: %.4f StdDev: %.2f\n", - num_, Average(), StandardDeviation()); + snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n", num_, + Average(), StandardDeviation()); r.append(buf); - snprintf(buf, sizeof(buf), - "Min: %.4f Median: %.4f Max: %.4f\n", + snprintf(buf, sizeof(buf), "Min: %.4f Median: %.4f Max: %.4f\n", (num_ == 0.0 ? 0.0 : min_), Median(), max_); r.append(buf); r.append("------------------------------------------------------\n"); @@ -119,17 +253,16 @@ std::string Histogram::ToString() const { for (int b = 0; b < kNumBuckets; b++) { if (buckets_[b] <= 0.0) continue; sum += buckets_[b]; - snprintf(buf, sizeof(buf), - "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", - ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left - kBucketLimit[b], // right - buckets_[b], // count - mult * buckets_[b], // percentage - mult * sum); // cumulative percentage + snprintf(buf, sizeof(buf), "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b - 1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage r.append(buf); // Add hash marks based on percentage; 20 marks for 100%. - int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + int marks = static_cast(20 * (buckets_[b] / num_) + 0.5); r.append(marks, '#'); r.push_back('\n'); } diff --git a/util/histogram.h b/util/histogram.h index 1ef9f3c8ab..fe281a98de 100644 --- a/util/histogram.h +++ b/util/histogram.h @@ -11,8 +11,8 @@ namespace leveldb { class Histogram { public: - Histogram() { } - ~Histogram() { } + Histogram() {} + ~Histogram() {} void Clear(); void Add(double value); diff --git a/util/logging.cc b/util/logging.cc index 411a30360d..1ad8f1c7f4 100644 --- a/util/logging.cc +++ b/util/logging.cc @@ -8,7 +8,9 @@ #include #include #include + #include + #include "leveldb/env.h" #include "leveldb/slice.h" @@ -16,7 +18,7 @@ namespace leveldb { void AppendNumberTo(std::string* str, uint64_t num) { char buf[30]; - snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + snprintf(buf, sizeof(buf), "%llu", (unsigned long long)num); str->append(buf); } @@ -62,8 +64,7 @@ bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { const unsigned char* current = start; for (; current != end; ++current) { const unsigned char ch = *current; - if (ch < '0' || ch > '9') - break; + if (ch < '0' || ch > '9') break; // Overflow check. // kMaxUint64 / 10 is also constant and will be optimized away. diff --git a/util/logging.h b/util/logging.h index 13351a2d03..8ff2da86b4 100644 --- a/util/logging.h +++ b/util/logging.h @@ -8,9 +8,11 @@ #ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ #define STORAGE_LEVELDB_UTIL_LOGGING_H_ -#include #include +#include + #include + #include "port/port.h" namespace leveldb { diff --git a/util/logging_test.cc b/util/logging_test.cc index 11665fc7e5..389cbeb14f 100644 --- a/util/logging_test.cc +++ b/util/logging_test.cc @@ -11,7 +11,7 @@ namespace leveldb { -class Logging { }; +class Logging {}; TEST(Logging, NumberToString) { ASSERT_EQ("0", NumberToString(0)); @@ -140,6 +140,4 @@ TEST(Logging, ConsumeDecimalNumberNoDigits) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/mutexlock.h b/util/mutexlock.h index 08d709a538..0cb2e250fb 100644 --- a/util/mutexlock.h +++ b/util/mutexlock.h @@ -22,8 +22,7 @@ namespace leveldb { class SCOPED_LOCKABLE MutexLock { public: - explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) - : mu_(mu) { + explicit MutexLock(port::Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { this->mu_->Lock(); } ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } @@ -32,10 +31,9 @@ class SCOPED_LOCKABLE MutexLock { MutexLock& operator=(const MutexLock&) = delete; private: - port::Mutex *const mu_; + port::Mutex* const mu_; }; } // namespace leveldb - #endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/util/no_destructor.h b/util/no_destructor.h index 4827e4579e..a0d3b8703d 100644 --- a/util/no_destructor.h +++ b/util/no_destructor.h @@ -13,7 +13,7 @@ namespace leveldb { // Wraps an instance whose destructor is never called. // // This is intended for use with function-level static variables. -template +template class NoDestructor { public: template @@ -23,8 +23,8 @@ class NoDestructor { static_assert( alignof(decltype(instance_storage_)) >= alignof(InstanceType), "instance_storage_ does not meet the instance's alignment requirement"); - new (&instance_storage_) InstanceType( - std::forward(constructor_args)...); + new (&instance_storage_) + InstanceType(std::forward(constructor_args)...); } ~NoDestructor() = default; @@ -37,9 +37,8 @@ class NoDestructor { } private: - typename - std::aligned_storage::type - instance_storage_; + typename std::aligned_storage::type instance_storage_; }; } // namespace leveldb diff --git a/util/no_destructor_test.cc b/util/no_destructor_test.cc index 7ce26311de..b41caca694 100644 --- a/util/no_destructor_test.cc +++ b/util/no_destructor_test.cc @@ -28,7 +28,7 @@ constexpr const uint64_t kGoldenB = 0xaabbccddeeffaabb; } // namespace -class NoDestructorTest { }; +class NoDestructorTest {}; TEST(NoDestructorTest, StackInstance) { NoDestructor instance(kGoldenA, kGoldenB); @@ -44,6 +44,4 @@ TEST(NoDestructorTest, StaticInstance) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/options.cc b/util/options.cc index 63284f8cf3..62de5bf0d2 100644 --- a/util/options.cc +++ b/util/options.cc @@ -9,9 +9,6 @@ namespace leveldb { -Options::Options() - : comparator(BytewiseComparator()), - env(Env::Default()) { -} +Options::Options() : comparator(BytewiseComparator()), env(Env::Default()) {} } // namespace leveldb diff --git a/util/posix_logger.h b/util/posix_logger.h index 5685fa3e99..28e15d10b4 100644 --- a/util/posix_logger.h +++ b/util/posix_logger.h @@ -26,13 +26,9 @@ class PosixLogger final : public Logger { // Creates a logger that writes to the given file. // // The PosixLogger instance takes ownership of the file handle. - explicit PosixLogger(std::FILE* fp) : fp_(fp) { - assert(fp != nullptr); - } + explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } - ~PosixLogger() override { - std::fclose(fp_); - } + ~PosixLogger() override { std::fclose(fp_); } void Logv(const char* format, va_list arguments) override { // Record the time as close to the Logv() call as possible. @@ -67,15 +63,10 @@ class PosixLogger final : public Logger { // Print the header into the buffer. int buffer_offset = snprintf( - buffer, buffer_size, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", - now_components.tm_year + 1900, - now_components.tm_mon + 1, - now_components.tm_mday, - now_components.tm_hour, - now_components.tm_min, - now_components.tm_sec, - static_cast(now_timeval.tv_usec), + buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.tm_year + 1900, now_components.tm_mon + 1, + now_components.tm_mday, now_components.tm_hour, now_components.tm_min, + now_components.tm_sec, static_cast(now_timeval.tv_usec), thread_id.c_str()); // The header can be at most 28 characters (10 date + 15 time + @@ -89,9 +80,9 @@ class PosixLogger final : public Logger { // Print the message into the buffer. std::va_list arguments_copy; va_copy(arguments_copy, arguments); - buffer_offset += std::vsnprintf(buffer + buffer_offset, - buffer_size - buffer_offset, format, - arguments_copy); + buffer_offset += + std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, + format, arguments_copy); va_end(arguments_copy); // The code below may append a newline at the end of the buffer, which diff --git a/util/random.h b/util/random.h index ddd51b1c7b..76f7daf52a 100644 --- a/util/random.h +++ b/util/random.h @@ -15,6 +15,7 @@ namespace leveldb { class Random { private: uint32_t seed_; + public: explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { // Avoid bad seeds. @@ -23,8 +24,8 @@ class Random { } } uint32_t Next() { - static const uint32_t M = 2147483647L; // 2^31-1 - static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 // We are computing // seed_ = (seed_ * A) % M, where M = 2^31-1 // @@ -54,9 +55,7 @@ class Random { // Skewed: pick "base" uniformly from range [0,max_log] and then // return "base" random bits. The effect is to pick a number in the // range [0,2^max_log-1] with exponential bias towards smaller numbers. - uint32_t Skewed(int max_log) { - return Uniform(1 << Uniform(max_log + 1)); - } + uint32_t Skewed(int max_log) { return Uniform(1 << Uniform(max_log + 1)); } }; } // namespace leveldb diff --git a/util/status.cc b/util/status.cc index 5591381a10..6ca8da6d8d 100644 --- a/util/status.cc +++ b/util/status.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/status.h" + #include + #include "port/port.h" -#include "leveldb/status.h" namespace leveldb { @@ -59,8 +61,8 @@ std::string Status::ToString() const { type = "IO error: "; break; default: - snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", - static_cast(code())); + snprintf(tmp, sizeof(tmp), + "Unknown code(%d): ", static_cast(code())); type = tmp; break; } diff --git a/util/status_test.cc b/util/status_test.cc index 7ed3b9eba9..2842319fbd 100644 --- a/util/status_test.cc +++ b/util/status_test.cc @@ -37,6 +37,4 @@ TEST(Status, MoveConstructor) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/util/testharness.cc b/util/testharness.cc index 37ba410b62..318ecfa3b7 100644 --- a/util/testharness.cc +++ b/util/testharness.cc @@ -23,7 +23,7 @@ struct Test { void (*func)(); }; std::vector* tests; -} +} // namespace bool RegisterTest(const char* base, const char* name, void (*func)()) { if (tests == nullptr) { diff --git a/util/testharness.h b/util/testharness.h index 8ee7972b89..72cd1629eb 100644 --- a/util/testharness.h +++ b/util/testharness.h @@ -47,9 +47,7 @@ class Tester { std::stringstream ss_; public: - Tester(const char* f, int l) - : ok_(true), fname_(f), line_(l) { - } + Tester(const char* f, int l) : ok_(true), fname_(f), line_(l) {} ~Tester() { if (!ok_) { @@ -74,14 +72,14 @@ class Tester { return *this; } -#define BINARY_OP(name, op) \ - template \ - Tester& name(const X& x, const Y& y) { \ - if (!(x op y)) { \ - ss_ << " failed: " << x << (" " #op " ") << y; \ - ok_ = false; \ - } \ - return *this; \ +#define BINARY_OP(name, op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (!(x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ } BINARY_OP(IsEq, ==) @@ -104,28 +102,34 @@ class Tester { #define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) #define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) -#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) -#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) -#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) -#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) -#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) -#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) +#define ASSERT_EQ(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a), (b)) +#define ASSERT_NE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a), (b)) +#define ASSERT_GE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a), (b)) +#define ASSERT_GT(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a), (b)) +#define ASSERT_LE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a), (b)) +#define ASSERT_LT(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a), (b)) #define TCONCAT(a, b) TCONCAT1(a, b) #define TCONCAT1(a, b) a##b -#define TEST(base, name) \ -class TCONCAT(_Test_, name) : public base { \ - public: \ - void _Run(); \ - static void _RunIt() { \ - TCONCAT(_Test_, name) t; \ - t._Run(); \ - } \ -}; \ -bool TCONCAT(_Test_ignored_, name) = \ - ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_, name)::_RunIt); \ -void TCONCAT(_Test_, name)::_Run() +#define TEST(base, name) \ + class TCONCAT(_Test_, name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_, name) t; \ + t._Run(); \ + } \ + }; \ + bool TCONCAT(_Test_ignored_, name) = ::leveldb::test::RegisterTest( \ + #base, #name, &TCONCAT(_Test_, name)::_RunIt); \ + void TCONCAT(_Test_, name)::_Run() // Register the specified test. Typically not used directly, but // invoked via the macro expansion of TEST. diff --git a/util/testutil.cc b/util/testutil.cc index 9d8079ce57..6b151b9e64 100644 --- a/util/testutil.cc +++ b/util/testutil.cc @@ -12,7 +12,7 @@ namespace test { Slice RandomString(Random* rnd, int len, std::string* dst) { dst->resize(len); for (int i = 0; i < len; i++) { - (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' } return Slice(*dst); } @@ -20,9 +20,8 @@ Slice RandomString(Random* rnd, int len, std::string* dst) { std::string RandomKey(Random* rnd, int len) { // Make sure to generate a wide variety of characters so we // test the boundary conditions for short-key optimizations. - static const char kTestChars[] = { - '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' - }; + static const char kTestChars[] = {'\0', '\1', 'a', 'b', 'c', + 'd', 'e', '\xfd', '\xfe', '\xff'}; std::string result; for (int i = 0; i < len; i++) { result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; @@ -30,9 +29,8 @@ std::string RandomKey(Random* rnd, int len) { return result; } - -Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst) { +Slice CompressibleString(Random* rnd, double compressed_fraction, size_t len, + std::string* dst) { int raw = static_cast(len * compressed_fraction); if (raw < 1) raw = 1; std::string raw_data; diff --git a/util/testutil.h b/util/testutil.h index a568824e8e..bb4051ba07 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -24,8 +24,8 @@ std::string RandomKey(Random* rnd, int len); // Store in *dst a string of length "len" that will compress to // "N*compressed_fraction" bytes and return a Slice that references // the generated data. -Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst); +Slice CompressibleString(Random* rnd, double compressed_fraction, size_t len, + std::string* dst); // A wrapper that allows injection of errors. class ErrorEnv : public EnvWrapper { @@ -33,12 +33,11 @@ class ErrorEnv : public EnvWrapper { bool writable_file_error_; int num_writable_file_errors_; - ErrorEnv() : EnvWrapper(NewMemEnv(Env::Default())), - writable_file_error_(false), - num_writable_file_errors_(0) { } - ~ErrorEnv() override { - delete target(); - } + ErrorEnv() + : EnvWrapper(NewMemEnv(Env::Default())), + writable_file_error_(false), + num_writable_file_errors_(0) {} + ~ErrorEnv() override { delete target(); } Status NewWritableFile(const std::string& fname, WritableFile** result) override { diff --git a/util/windows_logger.h b/util/windows_logger.h index 96799bce99..92960638d1 100644 --- a/util/windows_logger.h +++ b/util/windows_logger.h @@ -23,13 +23,9 @@ class WindowsLogger final : public Logger { // Creates a logger that writes to the given file. // // The PosixLogger instance takes ownership of the file handle. - explicit WindowsLogger(std::FILE* fp) : fp_(fp) { - assert(fp != nullptr); - } + explicit WindowsLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } - ~WindowsLogger() override { - std::fclose(fp_); - } + ~WindowsLogger() override { std::fclose(fp_); } void Logv(const char* format, va_list arguments) override { // Record the time as close to the Logv() call as possible. @@ -61,14 +57,9 @@ class WindowsLogger final : public Logger { // Print the header into the buffer. int buffer_offset = snprintf( - buffer, buffer_size, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", - now_components.wYear, - now_components.wMonth, - now_components.wDay, - now_components.wHour, - now_components.wMinute, - now_components.wSecond, + buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.wYear, now_components.wMonth, now_components.wDay, + now_components.wHour, now_components.wMinute, now_components.wSecond, static_cast(now_components.wMilliseconds * 1000), thread_id.c_str()); @@ -83,9 +74,9 @@ class WindowsLogger final : public Logger { // Print the message into the buffer. std::va_list arguments_copy; va_copy(arguments_copy, arguments); - buffer_offset += std::vsnprintf(buffer + buffer_offset, - buffer_size - buffer_offset, format, - arguments_copy); + buffer_offset += + std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, + format, arguments_copy); va_end(arguments_copy); // The code below may append a newline at the end of the buffer, which From c784d63b931d07895833fb80185b10d44ad63cce Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 2 May 2019 13:59:41 -0700 Subject: [PATCH 159/174] Moved port/README to port/README.md. Easier to read on sites supporting Markdown (i.e. GitHub). PiperOrigin-RevId: 246385089 --- port/{README => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename port/{README => README.md} (100%) diff --git a/port/README b/port/README.md similarity index 100% rename from port/README rename to port/README.md From 9bd23c767601a2420478eec158927882b879bada Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Fri, 3 May 2019 09:31:18 -0700 Subject: [PATCH 160/174] Correct class/structure declaration order. 1. Correct the class/struct declaration order to be IAW the Google C++ style guide[1]. 2. For non-copyable classes, switched from non-implemented private methods to explicitly deleted[2] methods. 3. Minor const and member initialization fixes. [1] https://google.github.io/styleguide/cppguide.html#Declaration_Order [2] http://eel.is/c++draft/dcl.fct.def.delete PiperOrigin-RevId: 246521844 --- db/autocompact_test.cc | 11 +-- db/c.cc | 32 +++---- db/corruption_test.cc | 22 ++--- db/db_impl.cc | 37 ++++---- db/db_impl.h | 64 +++++++------- db/db_iter.cc | 10 +-- db/db_test.cc | 24 ++--- db/dbformat.h | 7 +- db/dumpfile.cc | 6 +- db/log_reader.h | 45 +++++----- db/log_test.cc | 151 ++++++++++++++++---------------- db/log_writer.h | 11 ++- db/memtable.h | 14 +-- db/repair.cc | 32 +++---- db/skiplist.h | 31 ++++--- db/table_cache.h | 4 +- db/version_edit.h | 4 +- db/version_set.h | 50 +++++------ db/version_set_test.cc | 8 +- helpers/memenv/memenv.cc | 13 +-- helpers/memenv/memenv_test.cc | 4 +- include/leveldb/db.h | 6 +- include/leveldb/iterator.h | 12 +-- include/leveldb/options.h | 14 +-- include/leveldb/status.h | 14 +-- include/leveldb/table.h | 10 ++- include/leveldb/table_builder.h | 2 +- include/leveldb/write_batch.h | 13 +-- table/block.h | 11 ++- table/block_builder.h | 7 +- table/filter_block.h | 7 +- table/format.h | 16 ++-- table/merger.cc | 6 +- table/table_builder.cc | 32 +++---- util/arena.cc | 6 +- util/arena.h | 8 +- util/bloom.cc | 8 +- util/bloom_test.cc | 10 +-- util/cache_test.cc | 4 +- util/env_posix_test.cc | 7 +- util/env_test.cc | 3 +- util/env_windows.cc | 16 ++-- util/env_windows_test.cc | 7 +- util/histogram.h | 16 ++-- 44 files changed, 412 insertions(+), 403 deletions(-) diff --git a/db/autocompact_test.cc b/db/autocompact_test.cc index 00e3672801..e6c97a05a6 100644 --- a/db/autocompact_test.cc +++ b/db/autocompact_test.cc @@ -12,11 +12,6 @@ namespace leveldb { class AutoCompactTest { public: - std::string dbname_; - Cache* tiny_cache_; - Options options_; - DB* db_; - AutoCompactTest() { dbname_ = test::TmpDir() + "/autocompact_test"; tiny_cache_ = NewLRUCache(100); @@ -47,6 +42,12 @@ class AutoCompactTest { } void DoReads(int n); + + private: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; }; static const int kValueSize = 200 * 1024; diff --git a/db/c.cc b/db/c.cc index 72f6daabb1..e0f3367dcb 100644 --- a/db/c.cc +++ b/db/c.cc @@ -84,12 +84,6 @@ struct leveldb_filelock_t { }; struct leveldb_comparator_t : public Comparator { - void* state_; - void (*destructor_)(void*); - int (*compare_)(void*, const char* a, size_t alen, const char* b, - size_t blen); - const char* (*name_)(void*); - virtual ~leveldb_comparator_t() { (*destructor_)(state_); } virtual int Compare(const Slice& a, const Slice& b) const { @@ -101,18 +95,15 @@ struct leveldb_comparator_t : public Comparator { // No-ops since the C binding does not support key shortening methods. virtual void FindShortestSeparator(std::string*, const Slice&) const {} virtual void FindShortSuccessor(std::string* key) const {} -}; -struct leveldb_filterpolicy_t : public FilterPolicy { void* state_; void (*destructor_)(void*); + int (*compare_)(void*, const char* a, size_t alen, const char* b, + size_t blen); const char* (*name_)(void*); - char* (*create_)(void*, const char* const* key_array, - const size_t* key_length_array, int num_keys, - size_t* filter_length); - unsigned char (*key_match_)(void*, const char* key, size_t length, - const char* filter, size_t filter_length); +}; +struct leveldb_filterpolicy_t : public FilterPolicy { virtual ~leveldb_filterpolicy_t() { (*destructor_)(state_); } virtual const char* Name() const { return (*name_)(state_); } @@ -134,6 +125,15 @@ struct leveldb_filterpolicy_t : public FilterPolicy { return (*key_match_)(state_, key.data(), key.size(), filter.data(), filter.size()); } + + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length); + unsigned char (*key_match_)(void*, const char* key, size_t length, + const char* filter, size_t filter_length); }; struct leveldb_env_t { @@ -470,7 +470,8 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { // they delegate to a NewBloomFilterPolicy() instead of user // supplied C functions. struct Wrapper : public leveldb_filterpolicy_t { - const FilterPolicy* rep_; + static void DoNothing(void*) {} + ~Wrapper() { delete rep_; } const char* Name() const { return rep_->Name(); } void CreateFilter(const Slice* keys, int n, std::string* dst) const { @@ -479,7 +480,8 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { bool KeyMayMatch(const Slice& key, const Slice& filter) const { return rep_->KeyMayMatch(key, filter); } - static void DoNothing(void*) {} + + const FilterPolicy* rep_; }; Wrapper* wrapper = new Wrapper; wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); diff --git a/db/corruption_test.cc b/db/corruption_test.cc index e6f64ee89e..42f5237c65 100644 --- a/db/corruption_test.cc +++ b/db/corruption_test.cc @@ -22,20 +22,14 @@ static const int kValueSize = 1000; class CorruptionTest { public: - test::ErrorEnv env_; - std::string dbname_; - Cache* tiny_cache_; - Options options_; - DB* db_; - - CorruptionTest() { - tiny_cache_ = NewLRUCache(100); + CorruptionTest() + : db_(nullptr), + dbname_("/memenv/corruption_test"), + tiny_cache_(NewLRUCache(100)) { options_.env = &env_; options_.block_cache = tiny_cache_; - dbname_ = "/memenv/corruption_test"; DestroyDB(dbname_, options_); - db_ = nullptr; options_.create_if_missing = true; Reopen(); options_.create_if_missing = false; @@ -185,6 +179,14 @@ class CorruptionTest { Random r(k); return test::RandomString(&r, kValueSize, storage); } + + test::ErrorEnv env_; + Options options_; + DB* db_; + + private: + std::string dbname_; + Cache* tiny_cache_; }; TEST(CorruptionTest, Recovery) { diff --git a/db/db_impl.cc b/db/db_impl.cc index bff2d62148..761ebf6f4f 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -42,38 +42,23 @@ const int kNumNonTableCacheFiles = 10; // Information kept for every waiting writer struct DBImpl::Writer { + explicit Writer(port::Mutex* mu) + : batch(nullptr), sync(false), done(false), cv(mu) {} + Status status; WriteBatch* batch; bool sync; bool done; port::CondVar cv; - - explicit Writer(port::Mutex* mu) - : batch(nullptr), sync(false), done(false), cv(mu) {} }; struct DBImpl::CompactionState { - Compaction* const compaction; - - // Sequence numbers < smallest_snapshot are not significant since we - // will never have to service a snapshot below smallest_snapshot. - // Therefore if we have seen a sequence number S <= smallest_snapshot, - // we can drop all entries for the same key with sequence numbers < S. - SequenceNumber smallest_snapshot; - // Files produced by compaction struct Output { uint64_t number; uint64_t file_size; InternalKey smallest, largest; }; - std::vector outputs; - - // State kept for output being generated - WritableFile* outfile; - TableBuilder* builder; - - uint64_t total_bytes; Output* current_output() { return &outputs[outputs.size() - 1]; } @@ -83,6 +68,22 @@ struct DBImpl::CompactionState { outfile(nullptr), builder(nullptr), total_bytes(0) {} + + Compaction* const compaction; + + // Sequence numbers < smallest_snapshot are not significant since we + // will never have to service a snapshot below smallest_snapshot. + // Therefore if we have seen a sequence number S <= smallest_snapshot, + // we can drop all entries for the same key with sequence numbers < S. + SequenceNumber smallest_snapshot; + + std::vector outputs; + + // State kept for output being generated + WritableFile* outfile; + TableBuilder* builder; + + uint64_t total_bytes; }; // Fix user-supplied options to be reasonable diff --git a/db/db_impl.h b/db/db_impl.h index c895952313..ae87d6e334 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -29,6 +29,10 @@ class VersionSet; class DBImpl : public DB { public: DBImpl(const Options& options, const std::string& dbname); + + DBImpl(const DBImpl&) = delete; + DBImpl& operator=(const DBImpl&) = delete; + virtual ~DBImpl(); // Implementations of the DB interface @@ -71,6 +75,31 @@ class DBImpl : public DB { struct CompactionState; struct Writer; + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // null means beginning of key range + const InternalKey* end; // null means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) {} + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + }; + Iterator* NewInternalIterator(const ReadOptions&, SequenceNumber* latest_snapshot, uint32_t* seed); @@ -121,6 +150,10 @@ class DBImpl : public DB { Status InstallCompactionResults(CompactionState* compact) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } + // Constant after construction Env* const env_; const InternalKeyComparator internal_comparator_; @@ -161,14 +194,6 @@ class DBImpl : public DB { // Has a background compaction been scheduled or is running? bool background_compaction_scheduled_ GUARDED_BY(mutex_); - // Information for a manual compaction - struct ManualCompaction { - int level; - bool done; - const InternalKey* begin; // null means beginning of key range - const InternalKey* end; // null means end of key range - InternalKey tmp_storage; // Used to keep track of compaction progress - }; ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); VersionSet* const versions_; @@ -176,30 +201,7 @@ class DBImpl : public DB { // Have we encountered a background error in paranoid mode? Status bg_error_ GUARDED_BY(mutex_); - // Per level compaction stats. stats_[level] stores the stats for - // compactions that produced data for the specified "level". - struct CompactionStats { - int64_t micros; - int64_t bytes_read; - int64_t bytes_written; - - CompactionStats() : micros(0), bytes_read(0), bytes_written(0) {} - - void Add(const CompactionStats& c) { - this->micros += c.micros; - this->bytes_read += c.bytes_read; - this->bytes_written += c.bytes_written; - } - }; CompactionStats stats_[config::kNumLevels] GUARDED_BY(mutex_); - - // No copying allowed - DBImpl(const DBImpl&); - void operator=(const DBImpl&); - - const Comparator* user_comparator() const { - return internal_comparator_.user_comparator(); - } }; // Sanitize db options. The caller should delete result.info_log if diff --git a/db/db_iter.cc b/db/db_iter.cc index 1e5b5e2d31..8ff288e4f2 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -55,6 +55,10 @@ class DBIter : public Iterator { valid_(false), rnd_(seed), bytes_until_read_sampling_(RandomCompactionPeriod()) {} + + DBIter(const DBIter&) = delete; + DBIter& operator=(const DBIter&) = delete; + virtual ~DBIter() { delete iter_; } virtual bool Valid() const { return valid_; } virtual Slice key() const { @@ -106,19 +110,13 @@ class DBIter : public Iterator { const Comparator* const user_comparator_; Iterator* const iter_; SequenceNumber const sequence_; - Status status_; std::string saved_key_; // == current key when direction_==kReverse std::string saved_value_; // == current raw value when direction_==kReverse Direction direction_; bool valid_; - Random rnd_; size_t bytes_until_read_sampling_; - - // No copying allowed - DBIter(const DBIter&); - void operator=(const DBIter&); }; inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { diff --git a/db/db_test.cc b/db/db_test.cc index 43432163d1..78296d57f9 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -40,10 +40,6 @@ static std::string RandomKey(Random* rnd) { namespace { class AtomicCounter { - private: - port::Mutex mu_; - int count_ GUARDED_BY(mu_); - public: AtomicCounter() : count_(0) {} void Increment() { IncrementBy(1); } @@ -59,6 +55,10 @@ class AtomicCounter { MutexLock l(&mu_); count_ = 0; } + + private: + port::Mutex mu_; + int count_ GUARDED_BY(mu_); }; void DelayMilliseconds(int millis) { @@ -227,13 +227,6 @@ class SpecialEnv : public EnvWrapper { }; class DBTest { - private: - const FilterPolicy* filter_policy_; - - // Sequence of option configurations to try - enum OptionConfig { kDefault, kReuse, kFilter, kUncompressed, kEnd }; - int option_config_; - public: std::string dbname_; SpecialEnv* env_; @@ -241,7 +234,7 @@ class DBTest { Options last_options_; - DBTest() : option_config_(kDefault), env_(new SpecialEnv(Env::Default())) { + DBTest() : env_(new SpecialEnv(Env::Default())), option_config_(kDefault) { filter_policy_ = NewBloomFilterPolicy(10); dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, Options()); @@ -533,6 +526,13 @@ class DBTest { } return files_renamed; } + + private: + // Sequence of option configurations to try + enum OptionConfig { kDefault, kReuse, kFilter, kUncompressed, kEnd }; + + const FilterPolicy* filter_policy_; + int option_config_; }; TEST(DBTest, Empty) { diff --git a/db/dbformat.h b/db/dbformat.h index bdc23b8bd8..013028a76d 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -181,6 +181,9 @@ class LookupKey { // the specified sequence number. LookupKey(const Slice& user_key, SequenceNumber sequence); + LookupKey(const LookupKey&) = delete; + LookupKey& operator=(const LookupKey&) = delete; + ~LookupKey(); // Return a key suitable for lookup in a MemTable. @@ -204,10 +207,6 @@ class LookupKey { const char* kstart_; const char* end_; char space_[200]; // Avoid allocation for short keys - - // No copying allowed - LookupKey(const LookupKey&); - void operator=(const LookupKey&); }; inline LookupKey::~LookupKey() { diff --git a/db/dumpfile.cc b/db/dumpfile.cc index 1dbff5e657..9d22d58d1b 100644 --- a/db/dumpfile.cc +++ b/db/dumpfile.cc @@ -38,7 +38,6 @@ bool GuessType(const std::string& fname, FileType* type) { // Notified when log reader encounters corruption. class CorruptionReporter : public log::Reader::Reporter { public: - WritableFile* dst_; virtual void Corruption(size_t bytes, const Status& status) { std::string r = "corruption: "; AppendNumberTo(&r, bytes); @@ -47,6 +46,8 @@ class CorruptionReporter : public log::Reader::Reporter { r.push_back('\n'); dst_->Append(r); } + + WritableFile* dst_; }; // Print contents of a log file. (*func)() is called on every record. @@ -73,7 +74,6 @@ Status PrintLogContents(Env* env, const std::string& fname, // Called on every item found in a WriteBatch. class WriteBatchItemPrinter : public WriteBatch::Handler { public: - WritableFile* dst_; virtual void Put(const Slice& key, const Slice& value) { std::string r = " put '"; AppendEscapedStringTo(&r, key); @@ -88,6 +88,8 @@ class WriteBatchItemPrinter : public WriteBatch::Handler { r += "'\n"; dst_->Append(r); } + + WritableFile* dst_; }; // Called on every log record (each one of which is a WriteBatch) diff --git a/db/log_reader.h b/db/log_reader.h index b27c1645ee..001da8948a 100644 --- a/db/log_reader.h +++ b/db/log_reader.h @@ -43,6 +43,9 @@ class Reader { Reader(SequentialFile* file, Reporter* reporter, bool checksum, uint64_t initial_offset); + Reader(const Reader&) = delete; + Reader& operator=(const Reader&) = delete; + ~Reader(); // Read the next record into *record. Returns true if read @@ -58,26 +61,6 @@ class Reader { uint64_t LastRecordOffset(); private: - SequentialFile* const file_; - Reporter* const reporter_; - bool const checksum_; - char* const backing_store_; - Slice buffer_; - bool eof_; // Last Read() indicated EOF by returning < kBlockSize - - // Offset of the last record returned by ReadRecord. - uint64_t last_record_offset_; - // Offset of the first location past the end of buffer_. - uint64_t end_of_buffer_offset_; - - // Offset at which to start looking for the first record to return - uint64_t const initial_offset_; - - // True if we are resynchronizing after a seek (initial_offset_ > 0). In - // particular, a run of kMiddleType and kLastType records can be silently - // skipped in this mode - bool resyncing_; - // Extend record types with the following special values enum { kEof = kMaxRecordType + 1, @@ -102,9 +85,25 @@ class Reader { void ReportCorruption(uint64_t bytes, const char* reason); void ReportDrop(uint64_t bytes, const Status& reason); - // No copying allowed - Reader(const Reader&); - void operator=(const Reader&); + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // True if we are resynchronizing after a seek (initial_offset_ > 0). In + // particular, a run of kMiddleType and kLastType records can be silently + // skipped in this mode + bool resyncing_; }; } // namespace log diff --git a/db/log_test.cc b/db/log_test.cc index 3acaa33cc5..809c4183b4 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -37,81 +37,6 @@ static std::string RandomSkewedString(int i, Random* rnd) { } class LogTest { - private: - class StringDest : public WritableFile { - public: - std::string contents_; - - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } - virtual Status Append(const Slice& slice) { - contents_.append(slice.data(), slice.size()); - return Status::OK(); - } - }; - - class StringSource : public SequentialFile { - public: - Slice contents_; - bool force_error_; - bool returned_partial_; - StringSource() : force_error_(false), returned_partial_(false) {} - - virtual Status Read(size_t n, Slice* result, char* scratch) { - ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; - - if (force_error_) { - force_error_ = false; - returned_partial_ = true; - return Status::Corruption("read error"); - } - - if (contents_.size() < n) { - n = contents_.size(); - returned_partial_ = true; - } - *result = Slice(contents_.data(), n); - contents_.remove_prefix(n); - return Status::OK(); - } - - virtual Status Skip(uint64_t n) { - if (n > contents_.size()) { - contents_.clear(); - return Status::NotFound("in-memory file skipped past end"); - } - - contents_.remove_prefix(n); - - return Status::OK(); - } - }; - - class ReportCollector : public Reader::Reporter { - public: - size_t dropped_bytes_; - std::string message_; - - ReportCollector() : dropped_bytes_(0) {} - virtual void Corruption(size_t bytes, const Status& status) { - dropped_bytes_ += bytes; - message_.append(status.ToString()); - } - }; - - StringDest dest_; - StringSource source_; - ReportCollector report_; - bool reading_; - Writer* writer_; - Reader* reader_; - - // Record metadata for testing initial offset functionality - static size_t initial_offset_record_sizes_[]; - static uint64_t initial_offset_last_record_offsets_[]; - static int num_initial_offset_records_; - public: LogTest() : reading_(false), @@ -232,6 +157,82 @@ class LogTest { } delete offset_reader; } + + private: + class StringDest : public WritableFile { + public: + virtual Status Close() { return Status::OK(); } + virtual Status Flush() { return Status::OK(); } + virtual Status Sync() { return Status::OK(); } + virtual Status Append(const Slice& slice) { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + + std::string contents_; + }; + + class StringSource : public SequentialFile { + public: + StringSource() : force_error_(false), returned_partial_(false) {} + + virtual Status Read(size_t n, Slice* result, char* scratch) { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + virtual Status Skip(uint64_t n) { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipped past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + + Slice contents_; + bool force_error_; + bool returned_partial_; + }; + + class ReportCollector : public Reader::Reporter { + public: + ReportCollector() : dropped_bytes_(0) {} + virtual void Corruption(size_t bytes, const Status& status) { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + + size_t dropped_bytes_; + std::string message_; + }; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + static int num_initial_offset_records_; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer* writer_; + Reader* reader_; }; size_t LogTest::initial_offset_record_sizes_[] = { diff --git a/db/log_writer.h b/db/log_writer.h index 840809d4be..c0a21147ee 100644 --- a/db/log_writer.h +++ b/db/log_writer.h @@ -29,11 +29,16 @@ class Writer { // "*dest" must remain live while this Writer is in use. Writer(WritableFile* dest, uint64_t dest_length); + Writer(const Writer&) = delete; + Writer& operator=(const Writer&) = delete; + ~Writer(); Status AddRecord(const Slice& slice); private: + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + WritableFile* dest_; int block_offset_; // Current offset in block @@ -41,12 +46,6 @@ class Writer { // pre-computed to reduce the overhead of computing the crc of the // record type stored in the header. uint32_t type_crc_[kMaxRecordType + 1]; - - Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); - - // No copying allowed - Writer(const Writer&); - void operator=(const Writer&); }; } // namespace log diff --git a/db/memtable.h b/db/memtable.h index ef18bb53f3..9d986b1070 100644 --- a/db/memtable.h +++ b/db/memtable.h @@ -23,6 +23,9 @@ class MemTable { // is zero and the caller must call Ref() at least once. explicit MemTable(const InternalKeyComparator& comparator); + MemTable(const MemTable&) = delete; + MemTable& operator=(const MemTable&) = delete; + // Increase reference count. void Ref() { ++refs_; } @@ -60,26 +63,23 @@ class MemTable { bool Get(const LookupKey& key, std::string* value, Status* s); private: - ~MemTable(); // Private since only Unref() should be used to delete it + friend class MemTableIterator; + friend class MemTableBackwardIterator; struct KeyComparator { const InternalKeyComparator comparator; explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {} int operator()(const char* a, const char* b) const; }; - friend class MemTableIterator; - friend class MemTableBackwardIterator; typedef SkipList Table; + ~MemTable(); // Private since only Unref() should be used to delete it + KeyComparator comparator_; int refs_; Arena arena_; Table table_; - - // No copying allowed - MemTable(const MemTable&); - void operator=(const MemTable&); }; } // namespace leveldb diff --git a/db/repair.cc b/db/repair.cc index d5ecc45e66..3c676ca34f 100644 --- a/db/repair.cc +++ b/db/repair.cc @@ -95,22 +95,6 @@ class Repairer { SequenceNumber max_sequence; }; - std::string const dbname_; - Env* const env_; - InternalKeyComparator const icmp_; - InternalFilterPolicy const ipolicy_; - Options const options_; - bool owns_info_log_; - bool owns_cache_; - TableCache* table_cache_; - VersionEdit edit_; - - std::vector manifests_; - std::vector table_numbers_; - std::vector logs_; - std::vector tables_; - uint64_t next_file_number_; - Status FindFiles() { std::vector filenames; Status status = env_->GetChildren(dbname_, &filenames); @@ -439,6 +423,22 @@ class Repairer { Log(options_.info_log, "Archiving %s: %s\n", fname.c_str(), s.ToString().c_str()); } + + const std::string dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + const Options options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; }; } // namespace diff --git a/db/skiplist.h b/db/skiplist.h index 05e57331a9..a59b45b380 100644 --- a/db/skiplist.h +++ b/db/skiplist.h @@ -49,6 +49,9 @@ class SkipList { // must remain allocated for the lifetime of the skiplist object. explicit SkipList(Comparator cmp, Arena* arena); + SkipList(const SkipList&) = delete; + SkipList& operator=(const SkipList&) = delete; + // Insert key into the list. // REQUIRES: nothing that compares equal to key is currently in the list. void Insert(const Key& key); @@ -98,23 +101,10 @@ class SkipList { private: enum { kMaxHeight = 12 }; - // Immutable after construction - Comparator const compare_; - Arena* const arena_; // Arena used for allocations of nodes - - Node* const head_; - - // Modified only by Insert(). Read racily by readers, but stale - // values are ok. - std::atomic max_height_; // Height of the entire list - inline int GetMaxHeight() const { return max_height_.load(std::memory_order_relaxed); } - // Read/written only by Insert(). - Random rnd_; - Node* NewNode(const Key& key, int height); int RandomHeight(); bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } @@ -137,9 +127,18 @@ class SkipList { // Return head_ if list is empty. Node* FindLast() const; - // No copying allowed - SkipList(const SkipList&); - void operator=(const SkipList&); + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + std::atomic max_height_; // Height of the entire list + + // Read/written only by Insert(). + Random rnd_; }; // Implementation details follow diff --git a/db/table_cache.h b/db/table_cache.h index 21ae92d27b..93069c8844 100644 --- a/db/table_cache.h +++ b/db/table_cache.h @@ -45,12 +45,12 @@ class TableCache { void Evict(uint64_t file_number); private: + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); + Env* const env_; const std::string dbname_; const Options& options_; Cache* cache_; - - Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); }; } // namespace leveldb diff --git a/db/version_edit.h b/db/version_edit.h index 3daf4ef6f6..2dadda7546 100644 --- a/db/version_edit.h +++ b/db/version_edit.h @@ -16,14 +16,14 @@ namespace leveldb { class VersionSet; struct FileMetaData { + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {} + int refs; int allowed_seeks; // Seeks allowed until compaction uint64_t number; uint64_t file_size; // File size in bytes InternalKey smallest; // Smallest internal key served by table InternalKey largest; // Largest internal key served by table - - FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {} }; class VersionEdit { diff --git a/db/version_set.h b/db/version_set.h index 334ebd9cc4..69f3d70133 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -59,11 +59,6 @@ bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, class Version { public: - // Append to *iters a sequence of iterators that will - // yield the contents of this Version when merged together. - // REQUIRES: This version has been saved (see VersionSet::SaveTo) - void AddIterators(const ReadOptions&, std::vector* iters); - // Lookup the value for key. If found, store it in *val and // return OK. Else return a non-OK status. Fills *stats. // REQUIRES: lock is not held @@ -71,6 +66,12 @@ class Version { FileMetaData* seek_file; int seek_file_level; }; + + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, GetStats* stats); @@ -118,6 +119,22 @@ class Version { friend class VersionSet; class LevelFileNumIterator; + + explicit Version(VersionSet* vset) + : vset_(vset), + next_(this), + prev_(this), + refs_(0), + file_to_compact_(nullptr), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) {} + + Version(const Version&) = delete; + Version& operator=(const Version&) = delete; + + ~Version(); + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; // Call func(arg, level, f) for every file that overlaps user_key in @@ -145,28 +162,15 @@ class Version { // are initialized by Finalize(). double compaction_score_; int compaction_level_; - - explicit Version(VersionSet* vset) - : vset_(vset), - next_(this), - prev_(this), - refs_(0), - file_to_compact_(nullptr), - file_to_compact_level_(-1), - compaction_score_(-1), - compaction_level_(-1) {} - - ~Version(); - - // No copying allowed - Version(const Version&); - void operator=(const Version&); }; class VersionSet { public: VersionSet(const std::string& dbname, const Options* options, TableCache* table_cache, const InternalKeyComparator*); + VersionSet(const VersionSet&) = delete; + VersionSet& operator=(const VersionSet&) = delete; + ~VersionSet(); // Apply *edit to the current version to form a new descriptor that @@ -309,10 +313,6 @@ class VersionSet { // Per-level key at which the next compaction at that level should start. // Either an empty string, or a valid InternalKey. std::string compact_pointer_[config::kNumLevels]; - - // No copying allowed - VersionSet(const VersionSet&); - void operator=(const VersionSet&); }; // A Compaction encapsulates information about a compaction. diff --git a/db/version_set_test.cc b/db/version_set_test.cc index 43b51d8051..f7efe2b553 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -11,9 +11,6 @@ namespace leveldb { class FindFileTest { public: - std::vector files_; - bool disjoint_sorted_files_; - FindFileTest() : disjoint_sorted_files_(true) {} ~FindFileTest() { @@ -46,6 +43,11 @@ class FindFileTest { (smallest != nullptr ? &s : nullptr), (largest != nullptr ? &l : nullptr)); } + + bool disjoint_sorted_files_; + + private: + std::vector files_; }; TEST(FindFileTest, Empty) { diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index 58dc538db9..b6b790cfa2 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -27,6 +27,10 @@ class FileState { // and the caller must call Ref() at least once. FileState() : refs_(0), size_(0) {} + // No copying allowed. + FileState(const FileState&) = delete; + FileState& operator=(const FileState&) = delete; + // Increase the reference count. void Ref() { MutexLock lock(&refs_mutex_); @@ -133,21 +137,17 @@ class FileState { } private: + enum { kBlockSize = 8 * 1024 }; + // Private since only Unref() should be used to delete it. ~FileState() { Truncate(); } - // No copying allowed. - FileState(const FileState&); - void operator=(const FileState&); - port::Mutex refs_mutex_; int refs_ GUARDED_BY(refs_mutex_); mutable port::Mutex blocks_mutex_; std::vector blocks_ GUARDED_BY(blocks_mutex_); uint64_t size_ GUARDED_BY(blocks_mutex_); - - enum { kBlockSize = 8 * 1024 }; }; class SequentialFileImpl : public SequentialFile { @@ -380,6 +380,7 @@ class InMemoryEnv : public EnvWrapper { private: // Map from filenames to FileState objects, representing a simple file system. typedef std::map FileSystem; + port::Mutex mutex_; FileSystem file_map_ GUARDED_BY(mutex_); }; diff --git a/helpers/memenv/memenv_test.cc b/helpers/memenv/memenv_test.cc index a0a9469123..94ad06be68 100644 --- a/helpers/memenv/memenv_test.cc +++ b/helpers/memenv/memenv_test.cc @@ -16,10 +16,10 @@ namespace leveldb { class MemEnvTest { public: - Env* env_; - MemEnvTest() : env_(NewMemEnv(Env::Default())) {} ~MemEnvTest() { delete env_; } + + Env* env_; }; TEST(MemEnvTest, Basics) { diff --git a/include/leveldb/db.h b/include/leveldb/db.h index 0b8dc24ef3..e52a5b6624 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -33,11 +33,11 @@ class LEVELDB_EXPORT Snapshot { // A range of keys struct LEVELDB_EXPORT Range { - Slice start; // Included in the range - Slice limit; // Not included in the range - Range() {} Range(const Slice& s, const Slice& l) : start(s), limit(l) {} + + Slice start; // Included in the range + Slice limit; // Not included in the range }; // A DB is a persistent ordered map from keys to values. diff --git a/include/leveldb/iterator.h b/include/leveldb/iterator.h index 447e9507f3..bb9a5df8f5 100644 --- a/include/leveldb/iterator.h +++ b/include/leveldb/iterator.h @@ -84,12 +84,6 @@ class LEVELDB_EXPORT Iterator { // Cleanup functions are stored in a single-linked list. // The list's head node is inlined in the iterator. struct CleanupNode { - // The head node is used if the function pointer is not null. - CleanupFunction function; - void* arg1; - void* arg2; - CleanupNode* next; - // True if the node is not used. Only head nodes might be unused. bool IsEmpty() const { return function == nullptr; } // Invokes the cleanup function. @@ -97,6 +91,12 @@ class LEVELDB_EXPORT Iterator { assert(function != nullptr); (*function)(arg1, arg2); } + + // The head node is used if the function pointer is not null. + CleanupFunction function; + void* arg1; + void* arg2; + CleanupNode* next; }; CleanupNode cleanup_head_; }; diff --git a/include/leveldb/options.h b/include/leveldb/options.h index 7e26dc6c0b..b7487726bc 100644 --- a/include/leveldb/options.h +++ b/include/leveldb/options.h @@ -31,6 +31,9 @@ enum CompressionType { // Options to control the behavior of a database (passed to DB::Open) struct LEVELDB_EXPORT Options { + // Create an Options object with default values for all fields. + Options(); + // ------------------- // Parameters that affect behavior @@ -137,13 +140,12 @@ struct LEVELDB_EXPORT Options { // Many applications will benefit from passing the result of // NewBloomFilterPolicy() here. const FilterPolicy* filter_policy = nullptr; - - // Create an Options object with default values for all fields. - Options(); }; // Options that control read operations struct LEVELDB_EXPORT ReadOptions { + ReadOptions() = default; + // If true, all data read from underlying storage will be // verified against corresponding checksums. bool verify_checksums = false; @@ -157,12 +159,12 @@ struct LEVELDB_EXPORT ReadOptions { // not have been released). If "snapshot" is null, use an implicit // snapshot of the state at the beginning of this read operation. const Snapshot* snapshot = nullptr; - - ReadOptions() = default; }; // Options that control write operations struct LEVELDB_EXPORT WriteOptions { + WriteOptions() = default; + // If true, the write will be flushed from the operating system // buffer cache (by calling WritableFile::Sync()) before the write // is considered complete. If this flag is true, writes will be @@ -178,8 +180,6 @@ struct LEVELDB_EXPORT WriteOptions { // with sync==true has similar crash semantics to a "write()" // system call followed by "fsync()". bool sync = false; - - WriteOptions() = default; }; } // namespace leveldb diff --git a/include/leveldb/status.h b/include/leveldb/status.h index 54cf377103..e3273144e4 100644 --- a/include/leveldb/status.h +++ b/include/leveldb/status.h @@ -76,13 +76,6 @@ class LEVELDB_EXPORT Status { std::string ToString() const; private: - // OK status has a null state_. Otherwise, state_ is a new[] array - // of the following form: - // state_[0..3] == length of message - // state_[4] == code - // state_[5..] == message - const char* state_; - enum Code { kOk = 0, kNotFound = 1, @@ -98,6 +91,13 @@ class LEVELDB_EXPORT Status { Status(Code code, const Slice& msg, const Slice& msg2); static const char* CopyState(const char* s); + + // OK status has a null state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; }; inline Status::Status(const Status& rhs) { diff --git a/include/leveldb/table.h b/include/leveldb/table.h index 14a6a444f4..25c6013116 100644 --- a/include/leveldb/table.h +++ b/include/leveldb/table.h @@ -41,7 +41,7 @@ class LEVELDB_EXPORT Table { uint64_t file_size, Table** table); Table(const Table&) = delete; - void operator=(const Table&) = delete; + Table& operator=(const Table&) = delete; ~Table(); @@ -59,22 +59,24 @@ class LEVELDB_EXPORT Table { uint64_t ApproximateOffsetOf(const Slice& key) const; private: + friend class TableCache; struct Rep; - Rep* rep_; - explicit Table(Rep* rep) { rep_ = rep; } static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + explicit Table(Rep* rep) : rep_(rep) {} + // Calls (*handle_result)(arg, ...) with the entry found after a call // to Seek(key). May not make such a call if filter policy says // that key is not present. - friend class TableCache; Status InternalGet(const ReadOptions&, const Slice& key, void* arg, void (*handle_result)(void* arg, const Slice& k, const Slice& v)); void ReadMeta(const Footer& footer); void ReadFilter(const Slice& filter_handle_value); + + Rep* const rep_; }; } // namespace leveldb diff --git a/include/leveldb/table_builder.h b/include/leveldb/table_builder.h index f8361fd8c0..7d8896bb89 100644 --- a/include/leveldb/table_builder.h +++ b/include/leveldb/table_builder.h @@ -33,7 +33,7 @@ class LEVELDB_EXPORT TableBuilder { TableBuilder(const Options& options, WritableFile* file); TableBuilder(const TableBuilder&) = delete; - void operator=(const TableBuilder&) = delete; + TableBuilder& operator=(const TableBuilder&) = delete; // REQUIRES: Either Finish() or Abandon() has been called. ~TableBuilder(); diff --git a/include/leveldb/write_batch.h b/include/leveldb/write_batch.h index 21f7c63ed4..94d4115fed 100644 --- a/include/leveldb/write_batch.h +++ b/include/leveldb/write_batch.h @@ -32,6 +32,13 @@ class Slice; class LEVELDB_EXPORT WriteBatch { public: + class LEVELDB_EXPORT Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + WriteBatch(); // Intentionally copyable. @@ -63,12 +70,6 @@ class LEVELDB_EXPORT WriteBatch { void Append(const WriteBatch& source); // Support for iterating over the contents of a batch. - class LEVELDB_EXPORT Handler { - public: - virtual ~Handler(); - virtual void Put(const Slice& key, const Slice& value) = 0; - virtual void Delete(const Slice& key) = 0; - }; Status Iterate(Handler* handler) const; private: diff --git a/table/block.h b/table/block.h index 3d4b03c6bb..c8f1f7b436 100644 --- a/table/block.h +++ b/table/block.h @@ -20,24 +20,23 @@ class Block { // Initialize the block with the specified contents. explicit Block(const BlockContents& contents); + Block(const Block&) = delete; + Block& operator=(const Block&) = delete; + ~Block(); size_t size() const { return size_; } Iterator* NewIterator(const Comparator* comparator); private: + class Iter; + uint32_t NumRestarts() const; const char* data_; size_t size_; uint32_t restart_offset_; // Offset in data_ of restart array bool owned_; // Block owns data_[] - - // No copying allowed - Block(const Block&); - void operator=(const Block&); - - class Iter; }; } // namespace leveldb diff --git a/table/block_builder.h b/table/block_builder.h index d0d9b6e6ad..f91f5e6d47 100644 --- a/table/block_builder.h +++ b/table/block_builder.h @@ -19,6 +19,9 @@ class BlockBuilder { public: explicit BlockBuilder(const Options* options); + BlockBuilder(const BlockBuilder&) = delete; + BlockBuilder& operator=(const BlockBuilder&) = delete; + // Reset the contents as if the BlockBuilder was just constructed. void Reset(); @@ -45,10 +48,6 @@ class BlockBuilder { int counter_; // Number of entries emitted since restart bool finished_; // Has Finish() been called? std::string last_key_; - - // No copying allowed - BlockBuilder(const BlockBuilder&); - void operator=(const BlockBuilder&); }; } // namespace leveldb diff --git a/table/filter_block.h b/table/filter_block.h index 1b034dc92a..73b5399249 100644 --- a/table/filter_block.h +++ b/table/filter_block.h @@ -32,6 +32,9 @@ class FilterBlockBuilder { public: explicit FilterBlockBuilder(const FilterPolicy*); + FilterBlockBuilder(const FilterBlockBuilder&) = delete; + FilterBlockBuilder& operator=(const FilterBlockBuilder&) = delete; + void StartBlock(uint64_t block_offset); void AddKey(const Slice& key); Slice Finish(); @@ -45,10 +48,6 @@ class FilterBlockBuilder { std::string result_; // Filter data computed so far std::vector tmp_keys_; // policy_->CreateFilter() argument std::vector filter_offsets_; - - // No copying allowed - FilterBlockBuilder(const FilterBlockBuilder&); - void operator=(const FilterBlockBuilder&); }; class FilterBlockReader { diff --git a/table/format.h b/table/format.h index dacaa9f605..2ad145c3e8 100644 --- a/table/format.h +++ b/table/format.h @@ -23,6 +23,9 @@ struct ReadOptions; // block or a meta block. class BlockHandle { public: + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + BlockHandle(); // The offset of the block in the file. @@ -36,9 +39,6 @@ class BlockHandle { void EncodeTo(std::string* dst) const; Status DecodeFrom(Slice* input); - // Maximum encoding length of a BlockHandle - enum { kMaxEncodedLength = 10 + 10 }; - private: uint64_t offset_; uint64_t size_; @@ -48,6 +48,11 @@ class BlockHandle { // end of every table file. class Footer { public: + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { kEncodedLength = 2 * BlockHandle::kMaxEncodedLength + 8 }; + Footer() {} // The block handle for the metaindex block of the table @@ -61,11 +66,6 @@ class Footer { void EncodeTo(std::string* dst) const; Status DecodeFrom(Slice* input); - // Encoded length of a Footer. Note that the serialization of a - // Footer will always occupy exactly this many bytes. It consists - // of two block handles and a magic number. - enum { kEncodedLength = 2 * BlockHandle::kMaxEncodedLength + 8 }; - private: BlockHandle metaindex_handle_; BlockHandle index_handle_; diff --git a/table/merger.cc b/table/merger.cc index 3a5c3e4947..1bbc6cfd0a 100644 --- a/table/merger.cc +++ b/table/merger.cc @@ -129,6 +129,9 @@ class MergingIterator : public Iterator { } private: + // Which direction is the iterator moving? + enum Direction { kForward, kReverse }; + void FindSmallest(); void FindLargest(); @@ -139,9 +142,6 @@ class MergingIterator : public Iterator { IteratorWrapper* children_; int n_; IteratorWrapper* current_; - - // Which direction is the iterator moving? - enum Direction { kForward, kReverse }; Direction direction_; }; diff --git a/table/table_builder.cc b/table/table_builder.cc index 9afff767c3..278febf94f 100644 --- a/table/table_builder.cc +++ b/table/table_builder.cc @@ -19,6 +19,22 @@ namespace leveldb { struct TableBuilder::Rep { + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == nullptr + ? nullptr + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } + Options options; Options index_block_options; WritableFile* file; @@ -44,22 +60,6 @@ struct TableBuilder::Rep { BlockHandle pending_handle; // Handle to add to index block std::string compressed_output; - - Rep(const Options& opt, WritableFile* f) - : options(opt), - index_block_options(opt), - file(f), - offset(0), - data_block(&options), - index_block(&index_block_options), - num_entries(0), - closed(false), - filter_block(opt.filter_policy == nullptr - ? nullptr - : new FilterBlockBuilder(opt.filter_policy)), - pending_index_entry(false) { - index_block_options.block_restart_interval = 1; - } }; TableBuilder::TableBuilder(const Options& options, WritableFile* file) diff --git a/util/arena.cc b/util/arena.cc index eadec8acfb..46e3b2eb8f 100644 --- a/util/arena.cc +++ b/util/arena.cc @@ -8,10 +8,8 @@ namespace leveldb { static const int kBlockSize = 4096; -Arena::Arena() : memory_usage_(0) { - alloc_ptr_ = nullptr; // First allocation will allocate a block - alloc_bytes_remaining_ = 0; -} +Arena::Arena() + : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {} Arena::~Arena() { for (size_t i = 0; i < blocks_.size(); i++) { diff --git a/util/arena.h b/util/arena.h index 624e262b4a..68fc55d4dd 100644 --- a/util/arena.h +++ b/util/arena.h @@ -16,6 +16,10 @@ namespace leveldb { class Arena { public: Arena(); + + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + ~Arena(); // Return a pointer to a newly allocated memory block of "bytes" bytes. @@ -46,10 +50,6 @@ class Arena { // TODO(costan): This member is accessed via atomics, but the others are // accessed without any locking. Is this OK? std::atomic memory_usage_; - - // No copying allowed - Arena(const Arena&); - void operator=(const Arena&); }; inline char* Arena::Allocate(size_t bytes) { diff --git a/util/bloom.cc b/util/bloom.cc index 097ce7ab2e..7f974649ed 100644 --- a/util/bloom.cc +++ b/util/bloom.cc @@ -15,10 +15,6 @@ static uint32_t BloomHash(const Slice& key) { } class BloomFilterPolicy : public FilterPolicy { - private: - size_t bits_per_key_; - size_t k_; - public: explicit BloomFilterPolicy(int bits_per_key) : bits_per_key_(bits_per_key) { // We intentionally round down to reduce probing cost a little bit @@ -82,6 +78,10 @@ class BloomFilterPolicy : public FilterPolicy { } return true; } + + private: + size_t bits_per_key_; + size_t k_; }; } // namespace diff --git a/util/bloom_test.cc b/util/bloom_test.cc index 71c4115615..436daa9e99 100644 --- a/util/bloom_test.cc +++ b/util/bloom_test.cc @@ -19,11 +19,6 @@ static Slice Key(int i, char* buffer) { } class BloomTest { - private: - const FilterPolicy* policy_; - std::string filter_; - std::vector keys_; - public: BloomTest() : policy_(NewBloomFilterPolicy(10)) {} @@ -78,6 +73,11 @@ class BloomTest { } return result / 10000.0; } + + private: + const FilterPolicy* policy_; + std::string filter_; + std::vector keys_; }; TEST(BloomTest, EmptyFilter) { diff --git a/util/cache_test.cc b/util/cache_test.cc index d5c1a1dafd..974334b9f8 100644 --- a/util/cache_test.cc +++ b/util/cache_test.cc @@ -25,8 +25,6 @@ static int DecodeValue(void* v) { return reinterpret_cast(v); } class CacheTest { public: - static CacheTest* current_; - static void Deleter(const Slice& key, void* v) { current_->deleted_keys_.push_back(DecodeKey(key)); current_->deleted_values_.push_back(DecodeValue(v)); @@ -61,6 +59,8 @@ class CacheTest { } void Erase(int key) { cache_->Erase(EncodeKey(key)); } + + static CacheTest* current_; }; CacheTest* CacheTest::current_; diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index 6a2a1fc171..4b72934ab7 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -14,13 +14,14 @@ static const int kMMapLimit = 4; class EnvPosixTest { public: - Env* env_; - EnvPosixTest() : env_(Env::Default()) {} - static void SetFileLimits(int read_only_file_limit, int mmap_limit) { EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); } + + EnvPosixTest() : env_(Env::Default()) {} + + Env* env_; }; TEST(EnvPosixTest, TestOpenOnRead) { diff --git a/util/env_test.cc b/util/env_test.cc index 3e81261dc2..9e2ad1ead2 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -19,8 +19,9 @@ static const int kDelayMicros = 100000; class EnvTest { public: - Env* env_; EnvTest() : env_(Env::Default()) {} + + Env* env_; }; namespace { diff --git a/util/env_windows.cc b/util/env_windows.cc index c537938655..09e3df6919 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -626,21 +626,19 @@ class WindowsEnv : public Env { } private: - // BGThread() is the body of the background thread - void BGThread(); - - std::mutex mu_; - std::condition_variable bgsignal_; - bool started_bgthread_; - // Entry per Schedule() call struct BGItem { void* arg; void (*function)(void*); }; - typedef std::deque BGQueue; - BGQueue queue_; + // BGThread() is the body of the background thread + void BGThread(); + + std::mutex mu_; + std::condition_variable bgsignal_; + bool started_bgthread_; + std::deque queue_; Limiter mmap_limiter_; }; diff --git a/util/env_windows_test.cc b/util/env_windows_test.cc index 4451b9e4d3..3c22133891 100644 --- a/util/env_windows_test.cc +++ b/util/env_windows_test.cc @@ -14,12 +14,13 @@ static const int kMMapLimit = 4; class EnvWindowsTest { public: - Env* env_; - EnvWindowsTest() : env_(Env::Default()) {} - static void SetFileLimits(int mmap_limit) { EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit); } + + EnvWindowsTest() : env_(Env::Default()) {} + + Env* env_; }; TEST(EnvWindowsTest, TestOpenOnRead) { diff --git a/util/histogram.h b/util/histogram.h index fe281a98de..4da60fba45 100644 --- a/util/histogram.h +++ b/util/histogram.h @@ -21,20 +21,22 @@ class Histogram { std::string ToString() const; private: + enum { kNumBuckets = 154 }; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; + + static const double kBucketLimit[kNumBuckets]; + double min_; double max_; double num_; double sum_; double sum_squares_; - enum { kNumBuckets = 154 }; - static const double kBucketLimit[kNumBuckets]; double buckets_[kNumBuckets]; - - double Median() const; - double Percentile(double p) const; - double Average() const; - double StandardDeviation() const; }; } // namespace leveldb From 78b39d68c15ba020c0d60a3906fb66dbf1697595 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Fri, 3 May 2019 12:55:26 -0700 Subject: [PATCH 161/174] Bump the version number from 1.21 to 1.22. PiperOrigin-RevId: 246558281 --- CMakeLists.txt | 2 +- include/leveldb/db.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97953abc60..1409c06ee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.9) # Keep the version below in sync with the one in db.h -project(leveldb VERSION 1.21.0 LANGUAGES C CXX) +project(leveldb VERSION 1.22.0 LANGUAGES C CXX) # This project can use C11, but will gracefully decay down to C89. set(CMAKE_C_STANDARD 11) diff --git a/include/leveldb/db.h b/include/leveldb/db.h index e52a5b6624..ea3d9e5a41 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -16,7 +16,7 @@ namespace leveldb { // Update CMakeLists.txt if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 21; +static const int kMinorVersion = 22; struct Options; struct ReadOptions; From 24424a1ef2c284f4ec30544a3458023362cbeacd Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Sat, 4 May 2019 17:40:21 -0700 Subject: [PATCH 162/174] Style cleanup. 1) Convert iterator-based for loops to C++11 foreach loops. 2) Convert "void operator=" to "T& operator=". 3) Switch from copy operators from private to public deleted. 4) Switch from empty ctors / dtors to "= default" where appropriate. PiperOrigin-RevId: 246679195 --- db/db_impl.cc | 4 +- db/db_test.cc | 22 ++++----- db/fault_injection_test.cc | 45 ++++++++++--------- db/log_reader.cc | 2 +- db/log_writer.cc | 2 +- db/memtable.cc | 27 +++++------ db/version_edit.cc | 14 +++--- db/version_edit.h | 2 +- db/version_set.cc | 19 ++++---- db/version_set_test.cc | 4 +- db/write_batch.cc | 8 ++-- helpers/memenv/memenv.cc | 53 +++++++++++----------- include/leveldb/db.h | 2 +- table/format.h | 2 +- table/table_test.cc | 89 +++++++++++++++++++------------------ table/two_level_iterator.cc | 2 +- util/comparator.cc | 18 ++++---- util/env.cc | 12 ++--- 18 files changed, 164 insertions(+), 163 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 761ebf6f4f..82be594976 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -1469,7 +1469,7 @@ Status DB::Delete(const WriteOptions& opt, const Slice& key) { return Write(opt, &batch); } -DB::~DB() {} +DB::~DB() = default; Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) { *dbptr = nullptr; @@ -1514,7 +1514,7 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) { return s; } -Snapshot::~Snapshot() {} +Snapshot::~Snapshot() = default; Status DestroyDB(const std::string& dbname, const Options& options) { Env* env = options.env; diff --git a/db/db_test.cc b/db/db_test.cc index 78296d57f9..e7386f7239 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -2024,19 +2024,19 @@ class ModelDB : public DB { }; explicit ModelDB(const Options& options) : options_(options) {} - ~ModelDB() {} + ~ModelDB() override = default; virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { return DB::Put(o, k, v); } - virtual Status Delete(const WriteOptions& o, const Slice& key) { + Status Delete(const WriteOptions& o, const Slice& key) override { return DB::Delete(o, key); } - virtual Status Get(const ReadOptions& options, const Slice& key, - std::string* value) { + Status Get(const ReadOptions& options, const Slice& key, + std::string* value) override { assert(false); // Not implemented return Status::NotFound(key); } - virtual Iterator* NewIterator(const ReadOptions& options) { + Iterator* NewIterator(const ReadOptions& options) override { if (options.snapshot == nullptr) { KVMap* saved = new KVMap; *saved = map_; @@ -2047,16 +2047,16 @@ class ModelDB : public DB { return new ModelIter(snapshot_state, false); } } - virtual const Snapshot* GetSnapshot() { + const Snapshot* GetSnapshot() override { ModelSnapshot* snapshot = new ModelSnapshot; snapshot->map_ = map_; return snapshot; } - virtual void ReleaseSnapshot(const Snapshot* snapshot) { + void ReleaseSnapshot(const Snapshot* snapshot) override { delete reinterpret_cast(snapshot); } - virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + Status Write(const WriteOptions& options, WriteBatch* batch) override { class Handler : public WriteBatch::Handler { public: KVMap* map_; @@ -2070,15 +2070,15 @@ class ModelDB : public DB { return batch->Iterate(&handler); } - virtual bool GetProperty(const Slice& property, std::string* value) { + bool GetProperty(const Slice& property, std::string* value) override { return false; } - virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) override { for (int i = 0; i < n; i++) { sizes[i] = 0; } } - virtual void CompactRange(const Slice* start, const Slice* end) {} + void CompactRange(const Slice* start, const Slice* end) override {} private: class ModelIter : public Iterator { diff --git a/db/fault_injection_test.cc b/db/fault_injection_test.cc index 7088ea707c..5b31bb8053 100644 --- a/db/fault_injection_test.cc +++ b/db/fault_injection_test.cc @@ -109,11 +109,11 @@ class TestWritableFile : public WritableFile { public: TestWritableFile(const FileState& state, WritableFile* f, FaultInjectionTestEnv* env); - virtual ~TestWritableFile(); - virtual Status Append(const Slice& data); - virtual Status Close(); - virtual Status Flush(); - virtual Status Sync(); + ~TestWritableFile() override; + Status Append(const Slice& data) override; + Status Close() override; + Status Flush() override; + Status Sync() override; private: FileState state_; @@ -128,13 +128,13 @@ class FaultInjectionTestEnv : public EnvWrapper { public: FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {} - virtual ~FaultInjectionTestEnv() {} - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result); - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result); - virtual Status DeleteFile(const std::string& f); - virtual Status RenameFile(const std::string& s, const std::string& t); + ~FaultInjectionTestEnv() override = default; + Status NewWritableFile(const std::string& fname, + WritableFile** result) override; + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override; + Status DeleteFile(const std::string& f) override; + Status RenameFile(const std::string& s, const std::string& t) override; void WritableFileClosed(const FileState& state); Status DropUnsyncedFileData(); @@ -268,10 +268,11 @@ Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname, Status FaultInjectionTestEnv::DropUnsyncedFileData() { Status s; MutexLock l(&mutex_); - for (std::map::const_iterator it = - db_file_state_.begin(); - s.ok() && it != db_file_state_.end(); ++it) { - const FileState& state = it->second; + for (const auto& kvp : db_file_state_) { + if (!s.ok()) { + break; + } + const FileState& state = kvp.second; if (!state.IsFullySynced()) { s = state.DropUnsyncedData(); } @@ -340,12 +341,14 @@ Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() { std::set new_files(new_files_since_last_dir_sync_.begin(), new_files_since_last_dir_sync_.end()); mutex_.Unlock(); - Status s; - std::set::const_iterator it; - for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) { - s = DeleteFile(*it); + Status status; + for (const auto& new_file : new_files) { + Status delete_status = DeleteFile(new_file); + if (!delete_status.ok() && status.ok()) { + status = std::move(delete_status); + } } - return s; + return status; } void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) { diff --git a/db/log_reader.cc b/db/log_reader.cc index f47272349c..b770fee1a0 100644 --- a/db/log_reader.cc +++ b/db/log_reader.cc @@ -13,7 +13,7 @@ namespace leveldb { namespace log { -Reader::Reporter::~Reporter() {} +Reader::Reporter::~Reporter() = default; Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, uint64_t initial_offset) diff --git a/db/log_writer.cc b/db/log_writer.cc index 5e83f6a261..bfb16fb486 100644 --- a/db/log_writer.cc +++ b/db/log_writer.cc @@ -29,7 +29,7 @@ Writer::Writer(WritableFile* dest, uint64_t dest_length) InitTypeCrc(type_crc_); } -Writer::~Writer() {} +Writer::~Writer() = default; Status Writer::AddRecord(const Slice& slice) { const char* ptr = slice.data(); diff --git a/db/memtable.cc b/db/memtable.cc index c91405c238..00931d4671 100644 --- a/db/memtable.cc +++ b/db/memtable.cc @@ -47,27 +47,28 @@ class MemTableIterator : public Iterator { public: explicit MemTableIterator(MemTable::Table* table) : iter_(table) {} - virtual bool Valid() const { return iter_.Valid(); } - virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } - virtual void SeekToFirst() { iter_.SeekToFirst(); } - virtual void SeekToLast() { iter_.SeekToLast(); } - virtual void Next() { iter_.Next(); } - virtual void Prev() { iter_.Prev(); } - virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } - virtual Slice value() const { + MemTableIterator(const MemTableIterator&) = delete; + MemTableIterator& operator=(const MemTableIterator&) = delete; + + ~MemTableIterator() override = default; + + bool Valid() const override { return iter_.Valid(); } + void Seek(const Slice& k) override { iter_.Seek(EncodeKey(&tmp_, k)); } + void SeekToFirst() override { iter_.SeekToFirst(); } + void SeekToLast() override { iter_.SeekToLast(); } + void Next() override { iter_.Next(); } + void Prev() override { iter_.Prev(); } + Slice key() const override { return GetLengthPrefixedSlice(iter_.key()); } + Slice value() const override { Slice key_slice = GetLengthPrefixedSlice(iter_.key()); return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); } - virtual Status status() const { return Status::OK(); } + Status status() const override { return Status::OK(); } private: MemTable::Table::Iterator iter_; std::string tmp_; // For passing to EncodeKey - - // No copying allowed - MemTableIterator(const MemTableIterator&); - void operator=(const MemTableIterator&); }; Iterator* MemTable::NewIterator() { return new MemTableIterator(&table_); } diff --git a/db/version_edit.cc b/db/version_edit.cc index 44a4d02230..2385e7dfd1 100644 --- a/db/version_edit.cc +++ b/db/version_edit.cc @@ -66,11 +66,10 @@ void VersionEdit::EncodeTo(std::string* dst) const { PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); } - for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); ++iter) { + for (const auto& deleted_file_kvp : deleted_files_) { PutVarint32(dst, kDeletedFile); - PutVarint32(dst, iter->first); // level - PutVarint64(dst, iter->second); // file number + PutVarint32(dst, deleted_file_kvp.first); // level + PutVarint64(dst, deleted_file_kvp.second); // file number } for (size_t i = 0; i < new_files_.size(); i++) { @@ -233,12 +232,11 @@ std::string VersionEdit::DebugString() const { r.append(" "); r.append(compact_pointers_[i].second.DebugString()); } - for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); ++iter) { + for (const auto& deleted_files_kvp : deleted_files_) { r.append("\n DeleteFile: "); - AppendNumberTo(&r, iter->first); + AppendNumberTo(&r, deleted_files_kvp.first); r.append(" "); - AppendNumberTo(&r, iter->second); + AppendNumberTo(&r, deleted_files_kvp.second); } for (size_t i = 0; i < new_files_.size(); i++) { const FileMetaData& f = new_files_[i].second; diff --git a/db/version_edit.h b/db/version_edit.h index 2dadda7546..86b2f22317 100644 --- a/db/version_edit.h +++ b/db/version_edit.h @@ -29,7 +29,7 @@ struct FileMetaData { class VersionEdit { public: VersionEdit() { Clear(); } - ~VersionEdit() {} + ~VersionEdit() = default; void Clear(); diff --git a/db/version_set.cc b/db/version_set.cc index 96a92cce2c..099fa5763d 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -656,11 +656,9 @@ class VersionSet::Builder { } // Delete files - const VersionEdit::DeletedFileSet& del = edit->deleted_files_; - for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); - iter != del.end(); ++iter) { - const int level = iter->first; - const uint64_t number = iter->second; + for (const auto& deleted_file_set_kvp : edit->deleted_files_) { + const int level = deleted_file_set_kvp.first; + const uint64_t number = deleted_file_set_kvp.second; levels_[level].deleted_files.insert(number); } @@ -701,18 +699,17 @@ class VersionSet::Builder { const std::vector& base_files = base_->files_[level]; std::vector::const_iterator base_iter = base_files.begin(); std::vector::const_iterator base_end = base_files.end(); - const FileSet* added = levels_[level].added_files; - v->files_[level].reserve(base_files.size() + added->size()); - for (FileSet::const_iterator added_iter = added->begin(); - added_iter != added->end(); ++added_iter) { + const FileSet* added_files = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added_files->size()); + for (const auto& added_file : *added_files) { // Add all smaller files listed in base_ for (std::vector::const_iterator bpos = - std::upper_bound(base_iter, base_end, *added_iter, cmp); + std::upper_bound(base_iter, base_end, added_file, cmp); base_iter != bpos; ++base_iter) { MaybeAddFile(v, level, *base_iter); } - MaybeAddFile(v, level, *added_iter); + MaybeAddFile(v, level, added_file); } // Add remaining base files diff --git a/db/version_set_test.cc b/db/version_set_test.cc index f7efe2b553..c1056a1e7d 100644 --- a/db/version_set_test.cc +++ b/db/version_set_test.cc @@ -184,14 +184,14 @@ class AddBoundaryInputsTest { std::vector all_files_; InternalKeyComparator icmp_; - AddBoundaryInputsTest() : icmp_(BytewiseComparator()){}; + AddBoundaryInputsTest() : icmp_(BytewiseComparator()) {} ~AddBoundaryInputsTest() { for (size_t i = 0; i < all_files_.size(); ++i) { delete all_files_[i]; } all_files_.clear(); - }; + } FileMetaData* CreateFileMetaData(uint64_t number, InternalKey smallest, InternalKey largest) { diff --git a/db/write_batch.cc b/db/write_batch.cc index 2dec642483..b54313c35e 100644 --- a/db/write_batch.cc +++ b/db/write_batch.cc @@ -28,9 +28,9 @@ static const size_t kHeader = 12; WriteBatch::WriteBatch() { Clear(); } -WriteBatch::~WriteBatch() {} +WriteBatch::~WriteBatch() = default; -WriteBatch::Handler::~Handler() {} +WriteBatch::Handler::~Handler() = default; void WriteBatch::Clear() { rep_.clear(); @@ -118,11 +118,11 @@ class MemTableInserter : public WriteBatch::Handler { SequenceNumber sequence_; MemTable* mem_; - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { mem_->Add(sequence_, kTypeValue, key, value); sequence_++; } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { mem_->Add(sequence_, kTypeDeletion, key, Slice()); sequence_++; } diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index b6b790cfa2..2d4fbaa09a 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -223,16 +223,15 @@ class InMemoryEnv : public EnvWrapper { public: explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {} - virtual ~InMemoryEnv() { - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); - ++i) { - i->second->Unref(); + ~InMemoryEnv() override { + for (const auto& kvp : file_map_) { + kvp.second->Unref(); } } // Partial implementation of the Env interface. - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) { + Status NewSequentialFile(const std::string& fname, + SequentialFile** result) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { *result = nullptr; @@ -243,8 +242,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) { + Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { *result = nullptr; @@ -255,8 +254,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { + Status NewWritableFile(const std::string& fname, + WritableFile** result) override { MutexLock lock(&mutex_); FileSystem::iterator it = file_map_.find(fname); @@ -275,8 +274,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result) { + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override { MutexLock lock(&mutex_); FileState** sptr = &file_map_[fname]; FileState* file = *sptr; @@ -288,19 +287,18 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual bool FileExists(const std::string& fname) { + bool FileExists(const std::string& fname) override { MutexLock lock(&mutex_); return file_map_.find(fname) != file_map_.end(); } - virtual Status GetChildren(const std::string& dir, - std::vector* result) { + Status GetChildren(const std::string& dir, + std::vector* result) override { MutexLock lock(&mutex_); result->clear(); - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); - ++i) { - const std::string& filename = i->first; + for (const auto& kvp : file_map_) { + const std::string& filename = kvp.first; if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && Slice(filename).starts_with(Slice(dir))) { @@ -321,7 +319,7 @@ class InMemoryEnv : public EnvWrapper { file_map_.erase(fname); } - virtual Status DeleteFile(const std::string& fname) { + Status DeleteFile(const std::string& fname) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { return Status::IOError(fname, "File not found"); @@ -331,11 +329,11 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status CreateDir(const std::string& dirname) { return Status::OK(); } + Status CreateDir(const std::string& dirname) override { return Status::OK(); } - virtual Status DeleteDir(const std::string& dirname) { return Status::OK(); } + Status DeleteDir(const std::string& dirname) override { return Status::OK(); } - virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + Status GetFileSize(const std::string& fname, uint64_t* file_size) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { return Status::IOError(fname, "File not found"); @@ -345,7 +343,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status RenameFile(const std::string& src, const std::string& target) { + Status RenameFile(const std::string& src, + const std::string& target) override { MutexLock lock(&mutex_); if (file_map_.find(src) == file_map_.end()) { return Status::IOError(src, "File not found"); @@ -357,22 +356,22 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status LockFile(const std::string& fname, FileLock** lock) { + Status LockFile(const std::string& fname, FileLock** lock) override { *lock = new FileLock; return Status::OK(); } - virtual Status UnlockFile(FileLock* lock) { + Status UnlockFile(FileLock* lock) override { delete lock; return Status::OK(); } - virtual Status GetTestDirectory(std::string* path) { + Status GetTestDirectory(std::string* path) override { *path = "/test"; return Status::OK(); } - virtual Status NewLogger(const std::string& fname, Logger** result) { + Status NewLogger(const std::string& fname, Logger** result) override { *result = new NoOpLogger; return Status::OK(); } diff --git a/include/leveldb/db.h b/include/leveldb/db.h index ea3d9e5a41..b73014a221 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -33,7 +33,7 @@ class LEVELDB_EXPORT Snapshot { // A range of keys struct LEVELDB_EXPORT Range { - Range() {} + Range() = default; Range(const Slice& s, const Slice& l) : start(s), limit(l) {} Slice start; // Included in the range diff --git a/table/format.h b/table/format.h index 2ad145c3e8..e49dfdc047 100644 --- a/table/format.h +++ b/table/format.h @@ -53,7 +53,7 @@ class Footer { // of two block handles and a magic number. enum { kEncodedLength = 2 * BlockHandle::kMaxEncodedLength + 8 }; - Footer() {} + Footer() = default; // The block handle for the metaindex block of the table const BlockHandle& metaindex_handle() const { return metaindex_handle_; } diff --git a/table/table_test.cc b/table/table_test.cc index 0974052303..3c63e32a94 100644 --- a/table/table_test.cc +++ b/table/table_test.cc @@ -89,7 +89,7 @@ struct STLLessThan { class StringSink : public WritableFile { public: - ~StringSink() {} + ~StringSink() = default; const std::string& contents() const { return contents_; } @@ -111,7 +111,7 @@ class StringSource : public RandomAccessFile { StringSource(const Slice& contents) : contents_(contents.data(), contents.size()) {} - virtual ~StringSource() {} + virtual ~StringSource() = default; uint64_t Size() const { return contents_.size(); } @@ -139,7 +139,7 @@ typedef std::map KVMap; class Constructor { public: explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) {} - virtual ~Constructor() {} + virtual ~Constructor() = default; void Add(const std::string& key, const Slice& value) { data_[key] = value.ToString(); @@ -152,8 +152,8 @@ class Constructor { KVMap* kvmap) { *kvmap = data_; keys->clear(); - for (KVMap::const_iterator it = data_.begin(); it != data_.end(); ++it) { - keys->push_back(it->first); + for (const auto& kvp : data_) { + keys->push_back(kvp.first); } data_.clear(); Status s = FinishImpl(options, *kvmap); @@ -165,7 +165,7 @@ class Constructor { virtual Iterator* NewIterator() const = 0; - virtual const KVMap& data() { return data_; } + const KVMap& data() const { return data_; } virtual DB* db() const { return nullptr; } // Overridden in DBConstructor @@ -177,14 +177,14 @@ class BlockConstructor : public Constructor { public: explicit BlockConstructor(const Comparator* cmp) : Constructor(cmp), comparator_(cmp), block_(nullptr) {} - ~BlockConstructor() { delete block_; } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~BlockConstructor() override { delete block_; } + Status FinishImpl(const Options& options, const KVMap& data) override { delete block_; block_ = nullptr; BlockBuilder builder(&options); - for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { - builder.Add(it->first, it->second); + for (const auto& kvp : data) { + builder.Add(kvp.first, kvp.second); } // Open the block data_ = builder.Finish().ToString(); @@ -195,12 +195,12 @@ class BlockConstructor : public Constructor { block_ = new Block(contents); return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return block_->NewIterator(comparator_); } private: - const Comparator* comparator_; + const Comparator* const comparator_; std::string data_; Block* block_; @@ -211,14 +211,14 @@ class TableConstructor : public Constructor { public: TableConstructor(const Comparator* cmp) : Constructor(cmp), source_(nullptr), table_(nullptr) {} - ~TableConstructor() { Reset(); } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~TableConstructor() override { Reset(); } + Status FinishImpl(const Options& options, const KVMap& data) override { Reset(); StringSink sink; TableBuilder builder(options, &sink); - for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { - builder.Add(it->first, it->second); + for (const auto& kvp : data) { + builder.Add(kvp.first, kvp.second); ASSERT_TRUE(builder.status().ok()); } Status s = builder.Finish(); @@ -233,7 +233,7 @@ class TableConstructor : public Constructor { return Table::Open(table_options, source_, sink.contents().size(), &table_); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return table_->NewIterator(ReadOptions()); } @@ -259,20 +259,25 @@ class TableConstructor : public Constructor { class KeyConvertingIterator : public Iterator { public: explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) {} - virtual ~KeyConvertingIterator() { delete iter_; } - virtual bool Valid() const { return iter_->Valid(); } - virtual void Seek(const Slice& target) { + + KeyConvertingIterator(const KeyConvertingIterator&) = delete; + KeyConvertingIterator& operator=(const KeyConvertingIterator&) = delete; + + ~KeyConvertingIterator() override { delete iter_; } + + bool Valid() const override { return iter_->Valid(); } + void Seek(const Slice& target) override { ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); std::string encoded; AppendInternalKey(&encoded, ikey); iter_->Seek(encoded); } - virtual void SeekToFirst() { iter_->SeekToFirst(); } - virtual void SeekToLast() { iter_->SeekToLast(); } - virtual void Next() { iter_->Next(); } - virtual void Prev() { iter_->Prev(); } + void SeekToFirst() override { iter_->SeekToFirst(); } + void SeekToLast() override { iter_->SeekToLast(); } + void Next() override { iter_->Next(); } + void Prev() override { iter_->Prev(); } - virtual Slice key() const { + Slice key() const override { assert(Valid()); ParsedInternalKey key; if (!ParseInternalKey(iter_->key(), &key)) { @@ -282,18 +287,14 @@ class KeyConvertingIterator : public Iterator { return key.user_key; } - virtual Slice value() const { return iter_->value(); } - virtual Status status() const { + Slice value() const override { return iter_->value(); } + Status status() const override { return status_.ok() ? iter_->status() : status_; } private: mutable Status status_; Iterator* iter_; - - // No copying allowed - KeyConvertingIterator(const KeyConvertingIterator&); - void operator=(const KeyConvertingIterator&); }; class MemTableConstructor : public Constructor { @@ -303,24 +304,24 @@ class MemTableConstructor : public Constructor { memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); } - ~MemTableConstructor() { memtable_->Unref(); } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~MemTableConstructor() override { memtable_->Unref(); } + Status FinishImpl(const Options& options, const KVMap& data) override { memtable_->Unref(); memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); int seq = 1; - for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { - memtable_->Add(seq, kTypeValue, it->first, it->second); + for (const auto& kvp : data) { + memtable_->Add(seq, kTypeValue, kvp.first, kvp.second); seq++; } return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return new KeyConvertingIterator(memtable_->NewIterator()); } private: - InternalKeyComparator internal_comparator_; + const InternalKeyComparator internal_comparator_; MemTable* memtable_; }; @@ -331,23 +332,23 @@ class DBConstructor : public Constructor { db_ = nullptr; NewDB(); } - ~DBConstructor() { delete db_; } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~DBConstructor() override { delete db_; } + Status FinishImpl(const Options& options, const KVMap& data) override { delete db_; db_ = nullptr; NewDB(); - for (KVMap::const_iterator it = data.begin(); it != data.end(); ++it) { + for (const auto& kvp : data) { WriteBatch batch; - batch.Put(it->first, it->second); + batch.Put(kvp.first, kvp.second); ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); } return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return db_->NewIterator(ReadOptions()); } - virtual DB* db() const { return db_; } + DB* db() const override { return db_; } private: void NewDB() { @@ -365,7 +366,7 @@ class DBConstructor : public Constructor { ASSERT_TRUE(status.ok()) << status.ToString(); } - const Comparator* comparator_; + const Comparator* const comparator_; DB* db_; }; diff --git a/table/two_level_iterator.cc b/table/two_level_iterator.cc index 5340a4d3e5..1fc46268c9 100644 --- a/table/two_level_iterator.cc +++ b/table/two_level_iterator.cc @@ -77,7 +77,7 @@ TwoLevelIterator::TwoLevelIterator(Iterator* index_iter, index_iter_(index_iter), data_iter_(nullptr) {} -TwoLevelIterator::~TwoLevelIterator() {} +TwoLevelIterator::~TwoLevelIterator() = default; void TwoLevelIterator::Seek(const Slice& target) { index_iter_.Seek(target); diff --git a/util/comparator.cc b/util/comparator.cc index 26d1eb3b74..c5766e9462 100644 --- a/util/comparator.cc +++ b/util/comparator.cc @@ -2,32 +2,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/comparator.h" + #include #include #include +#include -#include "leveldb/comparator.h" #include "leveldb/slice.h" #include "util/logging.h" #include "util/no_destructor.h" namespace leveldb { -Comparator::~Comparator() {} +Comparator::~Comparator() = default; namespace { class BytewiseComparatorImpl : public Comparator { public: - BytewiseComparatorImpl() {} + BytewiseComparatorImpl() = default; - virtual const char* Name() const { return "leveldb.BytewiseComparator"; } + const char* Name() const override { return "leveldb.BytewiseComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return a.compare(b); } - virtual void FindShortestSeparator(std::string* start, - const Slice& limit) const { + void FindShortestSeparator(std::string* start, + const Slice& limit) const override { // Find length of common prefix size_t min_length = std::min(start->size(), limit.size()); size_t diff_index = 0; @@ -49,7 +51,7 @@ class BytewiseComparatorImpl : public Comparator { } } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { // Find first character that can be incremented size_t n = key->size(); for (size_t i = 0; i < n; i++) { diff --git a/util/env.cc b/util/env.cc index 6cd5f2e37e..d2f0aef326 100644 --- a/util/env.cc +++ b/util/env.cc @@ -6,21 +6,21 @@ namespace leveldb { -Env::~Env() {} +Env::~Env() = default; Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { return Status::NotSupported("NewAppendableFile", fname); } -SequentialFile::~SequentialFile() {} +SequentialFile::~SequentialFile() = default; -RandomAccessFile::~RandomAccessFile() {} +RandomAccessFile::~RandomAccessFile() = default; -WritableFile::~WritableFile() {} +WritableFile::~WritableFile() = default; -Logger::~Logger() {} +Logger::~Logger() = default; -FileLock::~FileLock() {} +FileLock::~FileLock() = default; void Log(Logger* info_log, const char* format, ...) { if (info_log != nullptr) { From 506b1722ef1a58d87325575d9bbcd3c8869381c7 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Sat, 4 May 2019 18:05:13 -0700 Subject: [PATCH 163/174] Convert missed virtual -> override in db_test.cc. PiperOrigin-RevId: 246680419 --- db/db_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/db_test.cc b/db/db_test.cc index e7386f7239..1da8db26f7 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -2025,7 +2025,7 @@ class ModelDB : public DB { explicit ModelDB(const Options& options) : options_(options) {} ~ModelDB() override = default; - virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + Status Put(const WriteOptions& o, const Slice& k, const Slice& v) override { return DB::Put(o, k, v); } Status Delete(const WriteOptions& o, const Slice& key) override { From 4bd052d7e8b0469b2b87664388e2a99cb212ecdb Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Sun, 5 May 2019 12:55:41 -0700 Subject: [PATCH 164/174] Consolidate benchmark code to benchmarks/. Currently, the benchmark used to assess leveldb changes lives in db/. The codebase also contains two benchmarks against other database engines in doc/bench/. Moving all the benchmarks in one place opens up the way for extracting common code. PiperOrigin-RevId: 246737541 --- .clang-format | 2 +- CMakeLists.txt | 6 +++--- {db => benchmarks}/db_bench.cc | 0 {doc/bench => benchmarks}/db_bench_sqlite3.cc | 0 {doc/bench => benchmarks}/db_bench_tree_db.cc | 0 doc/benchmark.html | 6 +++--- 6 files changed, 7 insertions(+), 7 deletions(-) rename {db => benchmarks}/db_bench.cc (100%) rename {doc/bench => benchmarks}/db_bench_sqlite3.cc (100%) rename {doc/bench => benchmarks}/db_bench_tree_db.cc (100%) diff --git a/.clang-format b/.clang-format index 75f34012ff..f493f75382 100644 --- a/.clang-format +++ b/.clang-format @@ -8,7 +8,7 @@ DerivePointerAlignment: false # Order them so that when imported to the authoritative repository they will be # in correct alphabetical order. IncludeCategories: - - Regex: '^(<|"(db|helpers)/)' + - Regex: '^(<|"(benchmarks|db|helpers)/)' Priority: 1 - Regex: '^"(leveldb)/' Priority: 2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1409c06ee5..96592d0bf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,12 +374,12 @@ if(LEVELDB_BUILD_BENCHMARKS) endfunction(leveldb_benchmark) if(NOT BUILD_SHARED_LIBS) - leveldb_benchmark("${PROJECT_SOURCE_DIR}/db/db_bench.cc") + leveldb_benchmark("${PROJECT_SOURCE_DIR}/benchmarks/db_bench.cc") endif(NOT BUILD_SHARED_LIBS) check_library_exists(sqlite3 sqlite3_open "" HAVE_SQLITE3) if(HAVE_SQLITE3) - leveldb_benchmark("${PROJECT_SOURCE_DIR}/doc/bench/db_bench_sqlite3.cc") + leveldb_benchmark("${PROJECT_SOURCE_DIR}/benchmarks/db_bench_sqlite3.cc") target_link_libraries(db_bench_sqlite3 sqlite3) endif(HAVE_SQLITE3) @@ -399,7 +399,7 @@ int main() { " HAVE_KYOTOCABINET) set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQURED_LIBRARIES}) if(HAVE_KYOTOCABINET) - leveldb_benchmark("${PROJECT_SOURCE_DIR}/doc/bench/db_bench_tree_db.cc") + leveldb_benchmark("${PROJECT_SOURCE_DIR}/benchmarks/db_bench_tree_db.cc") target_link_libraries(db_bench_tree_db kyotocabinet) endif(HAVE_KYOTOCABINET) endif(LEVELDB_BUILD_BENCHMARKS) diff --git a/db/db_bench.cc b/benchmarks/db_bench.cc similarity index 100% rename from db/db_bench.cc rename to benchmarks/db_bench.cc diff --git a/doc/bench/db_bench_sqlite3.cc b/benchmarks/db_bench_sqlite3.cc similarity index 100% rename from doc/bench/db_bench_sqlite3.cc rename to benchmarks/db_bench_sqlite3.cc diff --git a/doc/bench/db_bench_tree_db.cc b/benchmarks/db_bench_tree_db.cc similarity index 100% rename from doc/bench/db_bench_tree_db.cc rename to benchmarks/db_bench_tree_db.cc diff --git a/doc/benchmark.html b/doc/benchmark.html index c4639772c1..f3fd77144c 100644 --- a/doc/benchmark.html +++ b/doc/benchmark.html @@ -90,9 +90,9 @@

LevelDB Benchmarks

Benchmark Source Code

We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's db_bench. The code for each of the benchmarks resides here:

Custom Build Specifications

From cd1ec032cd276409ba403cab4d0b2548dd26b890 Mon Sep 17 00:00:00 2001 From: allangj Date: Fri, 8 Apr 2016 09:08:21 -0600 Subject: [PATCH 165/174] Add argument definition for void c functions. Allow the use c.h on projects with -Wstrict-prototypes Modify CMakelist to include -Wstrict-prototypes --- CMakeLists.txt | 2 ++ include/leveldb/c.h | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96592d0bf5..4f16f62808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC) include(CheckCXXSourceCompiles) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes") + # Test whether -Wthread-safety is available. See # https://clang.llvm.org/docs/ThreadSafetyAnalysis.html # -Werror is necessary because unknown attributes only generate warnings. diff --git a/include/leveldb/c.h b/include/leveldb/c.h index 8e0d592ac3..04e383cdb7 100644 --- a/include/leveldb/c.h +++ b/include/leveldb/c.h @@ -147,7 +147,7 @@ LEVELDB_EXPORT void leveldb_iter_get_error(const leveldb_iterator_t*, /* Write batch */ -LEVELDB_EXPORT leveldb_writebatch_t* leveldb_writebatch_create(); +LEVELDB_EXPORT leveldb_writebatch_t* leveldb_writebatch_create(void); LEVELDB_EXPORT void leveldb_writebatch_destroy(leveldb_writebatch_t*); LEVELDB_EXPORT void leveldb_writebatch_clear(leveldb_writebatch_t*); LEVELDB_EXPORT void leveldb_writebatch_put(leveldb_writebatch_t*, @@ -164,7 +164,7 @@ LEVELDB_EXPORT void leveldb_writebatch_append( /* Options */ -LEVELDB_EXPORT leveldb_options_t* leveldb_options_create(); +LEVELDB_EXPORT leveldb_options_t* leveldb_options_create(void); LEVELDB_EXPORT void leveldb_options_destroy(leveldb_options_t*); LEVELDB_EXPORT void leveldb_options_set_comparator(leveldb_options_t*, leveldb_comparator_t*); @@ -219,7 +219,7 @@ LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( /* Read options */ -LEVELDB_EXPORT leveldb_readoptions_t* leveldb_readoptions_create(); +LEVELDB_EXPORT leveldb_readoptions_t* leveldb_readoptions_create(void); LEVELDB_EXPORT void leveldb_readoptions_destroy(leveldb_readoptions_t*); LEVELDB_EXPORT void leveldb_readoptions_set_verify_checksums( leveldb_readoptions_t*, unsigned char); @@ -230,7 +230,7 @@ LEVELDB_EXPORT void leveldb_readoptions_set_snapshot(leveldb_readoptions_t*, /* Write options */ -LEVELDB_EXPORT leveldb_writeoptions_t* leveldb_writeoptions_create(); +LEVELDB_EXPORT leveldb_writeoptions_t* leveldb_writeoptions_create(void); LEVELDB_EXPORT void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); LEVELDB_EXPORT void leveldb_writeoptions_set_sync(leveldb_writeoptions_t*, unsigned char); @@ -242,7 +242,7 @@ LEVELDB_EXPORT void leveldb_cache_destroy(leveldb_cache_t* cache); /* Env */ -LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(); +LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(void); LEVELDB_EXPORT void leveldb_env_destroy(leveldb_env_t*); /* If not NULL, the returned buffer must be released using leveldb_free(). */ @@ -258,10 +258,10 @@ LEVELDB_EXPORT char* leveldb_env_get_test_directory(leveldb_env_t*); LEVELDB_EXPORT void leveldb_free(void* ptr); /* Return the major version number for this release. */ -LEVELDB_EXPORT int leveldb_major_version(); +LEVELDB_EXPORT int leveldb_major_version(void); /* Return the minor version number for this release. */ -LEVELDB_EXPORT int leveldb_minor_version(); +LEVELDB_EXPORT int leveldb_minor_version(void); #ifdef __cplusplus } /* end extern "C" */ From 142035edd4b1ab431c0ecbd547d4a77f1eca0667 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Mon, 6 May 2019 10:51:11 -0700 Subject: [PATCH 166/174] Initialize Stats::start_ before first use in Stats::Start(). Avoids a use before initialization error. This fixes issue #676. PiperOrigin-RevId: 246855204 --- benchmarks/db_bench.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/benchmarks/db_bench.cc b/benchmarks/db_bench.cc index 3090b43b9e..3696023b70 100644 --- a/benchmarks/db_bench.cc +++ b/benchmarks/db_bench.cc @@ -187,14 +187,12 @@ class Stats { void Start() { next_report_ = 100; - last_op_finish_ = start_; hist_.Clear(); done_ = 0; bytes_ = 0; seconds_ = 0; - start_ = g_env->NowMicros(); - finish_ = start_; message_.clear(); + start_ = finish_ = last_op_finish_ = g_env->NowMicros(); } void Merge(const Stats& other) { From a7528a5d2bd29126b60a277b528ed606b67c1771 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Mon, 6 May 2019 10:58:38 -0700 Subject: [PATCH 167/174] Clean up util/coding.{h,cc}. 1) Inline EncodeFixed{32,64}(). They emit single machine instructions on 64-bit processors. 2) Remove size narrowing compiler warnings from DecodeFixed{32,64}(). 3) Add comments explaining the current state of optimizations in compilers we care about. 4) Change C-style includes, like , to C++ style, like . 5) memcpy -> std::memcpy. The optimization comments are based on https://godbolt.org/z/RdIqS1. The missed optimization opportunities in clang have been reported as https://bugs.llvm.org/show_bug.cgi?id=41761 The change does not have significant impact on benchmarks. Results below. LevelDB: version 1.22 Date: Mon May 6 10:42:18 2019 CPU: 72 * Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz CPUCache: 25344 KB Keys: 16 bytes each Values: 100 bytes each (50 bytes after compression) Entries: 1000000 RawSize: 110.6 MB (estimated) FileSize: 62.9 MB (estimated) With change ------------------------------------------------ fillseq : 2.327 micros/op; 47.5 MB/s fillsync : 4185.526 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.662 micros/op; 30.2 MB/s overwrite : 4.261 micros/op; 26.0 MB/s readrandom : 4.239 micros/op; (1000000 of 1000000 found) readrandom : 3.649 micros/op; (1000000 of 1000000 found) readseq : 0.174 micros/op; 636.7 MB/s readreverse : 0.271 micros/op; 408.7 MB/s compact : 570495.000 micros/op; readrandom : 2.735 micros/op; (1000000 of 1000000 found) readseq : 0.118 micros/op; 937.3 MB/s readreverse : 0.190 micros/op; 583.7 MB/s fill100K : 860.164 micros/op; 110.9 MB/s (1000 ops) crc32c : 1.131 micros/op; 3455.2 MB/s (4K per op) snappycomp : 3.034 micros/op; 1287.5 MB/s (output: 55.1%) snappyuncomp : 0.544 micros/op; 7176.0 MB/s Baseline ------------------------------------------------ fillseq : 2.365 micros/op; 46.8 MB/s fillsync : 4240.165 micros/op; 0.0 MB/s (1000 ops) fillrandom : 3.244 micros/op; 34.1 MB/s overwrite : 4.153 micros/op; 26.6 MB/s readrandom : 4.698 micros/op; (1000000 of 1000000 found) readrandom : 4.065 micros/op; (1000000 of 1000000 found) readseq : 0.192 micros/op; 576.3 MB/s readreverse : 0.286 micros/op; 386.7 MB/s compact : 635979.000 micros/op; readrandom : 3.264 micros/op; (1000000 of 1000000 found) readseq : 0.169 micros/op; 652.8 MB/s readreverse : 0.213 micros/op; 519.5 MB/s fill100K : 1055.367 micros/op; 90.4 MB/s (1000 ops) crc32c : 1.353 micros/op; 2887.3 MB/s (4K per op) snappycomp : 3.036 micros/op; 1286.7 MB/s (output: 55.1%) snappyuncomp : 0.540 micros/op; 7238.6 MB/s PiperOrigin-RevId: 246856811 --- util/coding.cc | 26 ------------- util/coding.h | 102 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/util/coding.cc b/util/coding.cc index e2089df983..55be020248 100644 --- a/util/coding.cc +++ b/util/coding.cc @@ -6,32 +6,6 @@ namespace leveldb { -void EncodeFixed32(char* dst, uint32_t value) { - if (port::kLittleEndian) { - memcpy(dst, &value, sizeof(value)); - } else { - dst[0] = value & 0xff; - dst[1] = (value >> 8) & 0xff; - dst[2] = (value >> 16) & 0xff; - dst[3] = (value >> 24) & 0xff; - } -} - -void EncodeFixed64(char* dst, uint64_t value) { - if (port::kLittleEndian) { - memcpy(dst, &value, sizeof(value)); - } else { - dst[0] = value & 0xff; - dst[1] = (value >> 8) & 0xff; - dst[2] = (value >> 16) & 0xff; - dst[3] = (value >> 24) & 0xff; - dst[4] = (value >> 32) & 0xff; - dst[5] = (value >> 40) & 0xff; - dst[6] = (value >> 48) & 0xff; - dst[7] = (value >> 56) & 0xff; - } -} - void PutFixed32(std::string* dst, uint32_t value) { char buf[sizeof(value)]; EncodeFixed32(buf, value); diff --git a/util/coding.h b/util/coding.h index d9eeaa31da..92a961ff85 100644 --- a/util/coding.h +++ b/util/coding.h @@ -10,9 +10,8 @@ #ifndef STORAGE_LEVELDB_UTIL_CODING_H_ #define STORAGE_LEVELDB_UTIL_CODING_H_ -#include -#include - +#include +#include #include #include "leveldb/slice.h" @@ -43,45 +42,106 @@ const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v); // Returns the length of the varint32 or varint64 encoding of "v" int VarintLength(uint64_t v); -// Lower-level versions of Put... that write directly into a character buffer -// REQUIRES: dst has enough space for the value being written -void EncodeFixed32(char* dst, uint32_t value); -void EncodeFixed64(char* dst, uint64_t value); - // Lower-level versions of Put... that write directly into a character buffer // and return a pointer just past the last byte written. // REQUIRES: dst has enough space for the value being written char* EncodeVarint32(char* dst, uint32_t value); char* EncodeVarint64(char* dst, uint64_t value); +// TODO(costan): Remove port::kLittleEndian and the fast paths based on +// std::memcpy when clang learns to optimize the generic code, as +// described in https://bugs.llvm.org/show_bug.cgi?id=41761 +// +// The platform-independent code in DecodeFixed{32,64}() gets optimized to mov +// on x86 and ldr on ARM64, by both clang and gcc. However, only gcc optimizes +// the platform-independent code in EncodeFixed{32,64}() to mov / str. + +// Lower-level versions of Put... that write directly into a character buffer +// REQUIRES: dst has enough space for the value being written + +inline void EncodeFixed32(char* dst, uint32_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + if (port::kLittleEndian) { + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / str (ARM) instruction. + std::memcpy(buffer, &value, sizeof(uint32_t)); + return; + } + + // Platform-independent code. + // Currently, only gcc optimizes this to a single mov / str instruction. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); +} + +inline void EncodeFixed64(char* dst, uint64_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + if (port::kLittleEndian) { + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / str (ARM) instruction. + std::memcpy(buffer, &value, sizeof(uint64_t)); + return; + } + + // Platform-independent code. + // Currently, only gcc optimizes this to a single mov / str instruction. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); + buffer[4] = static_cast(value >> 32); + buffer[5] = static_cast(value >> 40); + buffer[6] = static_cast(value >> 48); + buffer[7] = static_cast(value >> 56); +} + // Lower-level versions of Get... that read directly from a character buffer // without any bounds checking. inline uint32_t DecodeFixed32(const char* ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + if (port::kLittleEndian) { - // Load the raw bytes + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / ldr (ARM) instruction. uint32_t result; - memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + std::memcpy(&result, buffer, sizeof(uint32_t)); return result; - } else { - return ((static_cast(static_cast(ptr[0]))) | - (static_cast(static_cast(ptr[1])) << 8) | - (static_cast(static_cast(ptr[2])) << 16) | - (static_cast(static_cast(ptr[3])) << 24)); } + + // Platform-independent code. + // Clang and gcc optimize this to a single mov / ldr instruction. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24); } inline uint64_t DecodeFixed64(const char* ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + if (port::kLittleEndian) { - // Load the raw bytes + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / ldr (ARM) instruction. uint64_t result; - memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + std::memcpy(&result, buffer, sizeof(uint64_t)); return result; - } else { - uint64_t lo = DecodeFixed32(ptr); - uint64_t hi = DecodeFixed32(ptr + 4); - return (hi << 32) | lo; } + + // Platform-independent code. + // Clang and gcc optimize this to a single mov / ldr instruction. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24) | + (static_cast(buffer[4]) << 32) | + (static_cast(buffer[5]) << 40) | + (static_cast(buffer[6]) << 48) | + (static_cast(buffer[7]) << 56); } // Internal routine for use by fallback path of GetVarint32Ptr From 9521545b062841409cf66eff0655feff09d9fd82 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Mon, 6 May 2019 15:20:42 -0700 Subject: [PATCH 168/174] Formatting changes for prior O_CLOEXEC fix. Two minor corrections to correct the 900f7d37eb322 commit to conform to the Google C++ style guide. PiperOrigin-RevId: 246907647 --- util/env_posix.cc | 22 +++++++++++----------- util/env_posix_test.cc | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index d94be10b01..cd0508bc54 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -50,9 +50,9 @@ int g_mmap_limit = kDefaultMmapLimit; // Common flags defined for all posix open operations #if defined(HAVE_O_CLOEXEC) -constexpr const int O_FLAGS = O_CLOEXEC; +constexpr const int kOpenBaseFlags = O_CLOEXEC; #else -constexpr const int O_FLAGS = 0; +constexpr const int kOpenBaseFlags = 0; #endif // defined(HAVE_O_CLOEXEC) constexpr const size_t kWritableFileBufferSize = 65536; @@ -172,7 +172,7 @@ class PosixRandomAccessFile final : public RandomAccessFile { char* scratch) const override { int fd = fd_; if (!has_permanent_fd_) { - fd = ::open(filename_.c_str(), O_RDONLY | O_FLAGS); + fd = ::open(filename_.c_str(), O_RDONLY | kOpenBaseFlags); if (fd < 0) { return PosixError(filename_, errno); } @@ -350,7 +350,7 @@ class PosixWritableFile final : public WritableFile { return status; } - int fd = ::open(dirname_.c_str(), O_RDONLY | O_FLAGS); + int fd = ::open(dirname_.c_str(), O_RDONLY | kOpenBaseFlags); if (fd < 0) { status = PosixError(dirname_, errno); } else { @@ -498,7 +498,7 @@ class PosixEnv : public Env { Status NewSequentialFile(const std::string& filename, SequentialFile** result) override { - int fd = ::open(filename.c_str(), O_RDONLY | O_FLAGS); + int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -511,7 +511,7 @@ class PosixEnv : public Env { Status NewRandomAccessFile(const std::string& filename, RandomAccessFile** result) override { *result = nullptr; - int fd = ::open(filename.c_str(), O_RDONLY | O_FLAGS); + int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags); if (fd < 0) { return PosixError(filename, errno); } @@ -543,8 +543,8 @@ class PosixEnv : public Env { Status NewWritableFile(const std::string& filename, WritableFile** result) override { - int fd = - ::open(filename.c_str(), O_TRUNC | O_WRONLY | O_CREAT | O_FLAGS, 0644); + int fd = ::open(filename.c_str(), + O_TRUNC | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -556,8 +556,8 @@ class PosixEnv : public Env { Status NewAppendableFile(const std::string& filename, WritableFile** result) override { - int fd = - ::open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT | O_FLAGS, 0644); + int fd = ::open(filename.c_str(), + O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); if (fd < 0) { *result = nullptr; return PosixError(filename, errno); @@ -627,7 +627,7 @@ class PosixEnv : public Env { Status LockFile(const std::string& filename, FileLock** lock) override { *lock = nullptr; - int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_FLAGS, 0644); + int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | kOpenBaseFlags, 0644); if (fd < 0) { return PosixError(filename, errno); } diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index 3f7bfdaae1..54d43f0c3c 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -117,7 +117,7 @@ TEST(EnvPosixTest, TestCloseOnExec) { #endif // defined(HAVE_O_CLOEXEC) -int cloexecChild() { +int CloexecChild() { // Checks for open file descriptors in the range 3..FD_SETSIZE. for (int i = 3; i < FD_SETSIZE; i++) { int dup_result = dup2(i, i); @@ -156,7 +156,7 @@ int cloexecChild() { int main(int argc, char** argv) { // Check if this is the child process for TestCloseOnExec if (argc > 1 && strcmp(argv[1], "-cloexec-child") == 0) { - return leveldb::cloexecChild(); + return leveldb::CloexecChild(); } // All tests currently run with the same read-only file limits. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, From 27dc99fb2642cadc87c9aaec82c54a2c725ee0d6 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Tue, 7 May 2019 14:19:08 -0700 Subject: [PATCH 169/174] Fix EnvPosix tests on Travis CI. The previous attempt of having EnvPosix use O_CLOEXEC (close-on-exec()) when opening file descriptors added tests that relied on procfs, which is Linux-specific. These tests failed on macOS. Unfortunately, the test failures were not caught due to a (since fixed) error in our Travis CI configuration. This CL re-structures the tests to only rely on POSIX features. Since there is no POSIX-compliant way to get a file name/path out of a file descriptor, this CL breaks up the O_CLOEXEC test into multiple tests, where each Env method that creates an FD gets its own test. This is intended to make it easier to find and fix errors in Env implementations. This CL also fixes the implementation of NewLogger() to use O_CLOEXEC on macOS. The current implementation passes "we" to fopen(), but the macOS standard C library does not implement the "e" flag yet. PiperOrigin-RevId: 247088953 --- util/env_posix.cc | 10 +- util/env_posix_test.cc | 357 +++++++++++++++++++++++++++++++---------- 2 files changed, 280 insertions(+), 87 deletions(-) diff --git a/util/env_posix.cc b/util/env_posix.cc index cd0508bc54..0cfb069c5d 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -683,8 +683,16 @@ class PosixEnv : public Env { } Status NewLogger(const std::string& filename, Logger** result) override { - std::FILE* fp = std::fopen(filename.c_str(), "we"); + int fd = ::open(filename.c_str(), + O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); + if (fd < 0) { + *result = nullptr; + return PosixError(filename, errno); + } + + std::FILE* fp = ::fdopen(fd, "w"); if (fp == nullptr) { + ::close(fd); *result = nullptr; return PosixError(filename, errno); } else { diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc index 54d43f0c3c..9675d739ad 100644 --- a/util/env_posix_test.cc +++ b/util/env_posix_test.cc @@ -2,14 +2,167 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include #include #include +#include +#include +#include +#include +#include +#include + #include "leveldb/env.h" #include "port/port.h" #include "util/env_posix_test_helper.h" #include "util/testharness.h" +#if HAVE_O_CLOEXEC + +namespace { + +// Exit codes for the helper process spawned by TestCloseOnExec* tests. +// Useful for debugging test failures. +constexpr int kTextCloseOnExecHelperExecFailedCode = 61; +constexpr int kTextCloseOnExecHelperDup2FailedCode = 62; +constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63; + +// Global set by main() and read in TestCloseOnExec. +// +// The argv[0] value is stored in a std::vector instead of a std::string because +// std::string does not return a mutable pointer to its buffer until C++17. +// +// The vector stores the string pointed to by argv[0], plus the trailing null. +std::vector* GetArgvZero() { + static std::vector program_name; + return &program_name; +} + +// Command-line switch used to run this test as the CloseOnExecSwitch helper. +static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper"; + +// Executed in a separate process by TestCloseOnExec* tests. +// +// main() delegates to this function when the test executable is launched with +// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test +// executable and pass the special command-line switch. +// + +// main() delegates to this function when the test executable is launched with +// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test +// executable and pass the special command-line switch. +// +// When main() delegates to this function, the process probes whether a given +// file descriptor is open, and communicates the result via its exit code. +int TestCloseOnExecHelperMain(char* pid_arg) { + int fd = std::atoi(pid_arg); + // When given the same file descriptor twice, dup2() returns -1 if the + // file descriptor is closed, or the given file descriptor if it is open. + if (::dup2(fd, fd) == fd) { + std::fprintf(stderr, "Unexpected open fd %d\n", fd); + return kTextCloseOnExecHelperFoundOpenFdCode; + } + // Double-check that dup2() is saying the file descriptor is closed. + if (errno != EBADF) { + std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n", + fd, std::strerror(errno)); + return kTextCloseOnExecHelperDup2FailedCode; + } + return 0; +} + +// File descriptors are small non-negative integers. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetMaxFileDescriptor(int* result_fd) { + // Get the maximum file descriptor number. + ::rlimit fd_rlimit; + ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit)); + *result_fd = fd_rlimit.rlim_cur; +} + +// Iterates through all possible FDs and returns the currently open ones. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetOpenFileDescriptors(std::unordered_set* open_fds) { + int max_fd = 0; + GetMaxFileDescriptor(&max_fd); + + for (int fd = 0; fd < max_fd; ++fd) { + if (::dup2(fd, fd) != fd) { + // When given the same file descriptor twice, dup2() returns -1 if the + // file descriptor is closed, or the given file descriptor if it is open. + // + // Double-check that dup2() is saying the fd is closed. + ASSERT_EQ(EBADF, errno) + << "dup2() should set errno to EBADF on closed file descriptors"; + continue; + } + open_fds->insert(fd); + } +} + +// Finds an FD open since a previous call to GetOpenFileDescriptors(). +// +// |baseline_open_fds| is the result of a previous GetOpenFileDescriptors() +// call. Assumes that exactly one FD was opened since that call. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetNewlyOpenedFileDescriptor( + const std::unordered_set& baseline_open_fds, int* result_fd) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + for (int fd : baseline_open_fds) { + ASSERT_EQ(1, open_fds.count(fd)) + << "Previously opened file descriptor was closed during test setup"; + open_fds.erase(fd); + } + ASSERT_EQ(1, open_fds.size()) + << "Expected exactly one newly opened file descriptor during test setup"; + *result_fd = *open_fds.begin(); +} + +// Check that a fork()+exec()-ed child process does not have an extra open FD. +void CheckCloseOnExecDoesNotLeakFDs( + const std::unordered_set& baseline_open_fds) { + // Prepare the argument list for the child process. + // execv() wants mutable buffers. + char switch_buffer[sizeof(kTestCloseOnExecSwitch)]; + std::memcpy(switch_buffer, kTestCloseOnExecSwitch, + sizeof(kTestCloseOnExecSwitch)); + + int probed_fd; + GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd); + std::string fd_string = std::to_string(probed_fd); + std::vector fd_buffer(fd_string.begin(), fd_string.end()); + fd_buffer.emplace_back('\0'); + + // The helper process is launched with the command below. + // env_posix_tests --test-close-on-exec-helper 3 + char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(), + nullptr}; + + constexpr int kForkInChildProcessReturnValue = 0; + int child_pid = fork(); + if (child_pid == kForkInChildProcessReturnValue) { + ::execv(child_argv[0], child_argv); + std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno)); + std::exit(kTextCloseOnExecHelperExecFailedCode); + } + + int child_status = 0; + ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0)); + ASSERT_TRUE(WIFEXITED(child_status)) + << "The helper process did not exit with an exit code"; + ASSERT_EQ(0, WEXITSTATUS(child_status)) + << "The helper process encountered an error"; +} + +} // namespace + +#endif // HAVE_O_CLOEXEC + namespace leveldb { static const int kReadOnlyFileLimit = 4; @@ -58,106 +211,138 @@ TEST(EnvPosixTest, TestOpenOnRead) { ASSERT_OK(env_->DeleteFile(test_file)); } -#if defined(HAVE_O_CLOEXEC) +#if HAVE_O_CLOEXEC -TEST(EnvPosixTest, TestCloseOnExec) { - // Test that file handles are not inherited by child processes. +TEST(EnvPosixTest, TestCloseOnExecSequentialFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); - // Open file handles with each of the open methods. std::string test_dir; ASSERT_OK(env_->GetTestDirectory(&test_dir)); - std::vector test_files = { - test_dir + "/close_on_exec_seq.txt", - test_dir + "/close_on_exec_rand.txt", - test_dir + "/close_on_exec_write.txt", - test_dir + "/close_on_exec_append.txt", - test_dir + "/close_on_exec_lock.txt", - test_dir + "/close_on_exec_log.txt", - }; - for (const std::string& test_file : test_files) { - const char kFileData[] = "0123456789"; - ASSERT_OK(WriteStringToFile(env_, kFileData, test_file)); - } - leveldb::SequentialFile* seqFile = nullptr; - leveldb::RandomAccessFile* randFile = nullptr; - leveldb::WritableFile* writeFile = nullptr; - leveldb::WritableFile* appendFile = nullptr; - leveldb::FileLock* lockFile = nullptr; - leveldb::Logger* logFile = nullptr; - ASSERT_OK(env_->NewSequentialFile(test_files[0], &seqFile)); - ASSERT_OK(env_->NewRandomAccessFile(test_files[1], &randFile)); - ASSERT_OK(env_->NewWritableFile(test_files[2], &writeFile)); - ASSERT_OK(env_->NewAppendableFile(test_files[3], &appendFile)); - ASSERT_OK(env_->LockFile(test_files[4], &lockFile)); - ASSERT_OK(env_->NewLogger(test_files[5], &logFile)); - - // Fork a child process and wait for it to complete. - int pid = fork(); - if (pid == 0) { - const char* const child[] = {"/proc/self/exe", "-cloexec-child", nullptr}; - execv(child[0], const_cast(child)); - printf("Error spawning child process: %s\n", strerror(errno)); - exit(6); + std::string file_path = test_dir + "/close_on_exec_sequential.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::SequentialFile* file = nullptr; + ASSERT_OK(env_->NewSequentialFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecRandomAccessFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_random_access.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + // Exhaust the RandomAccessFile mmap limit. This way, the test + // RandomAccessFile instance below is backed by a file descriptor, not by an + // mmap region. + leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr}; + for (int i = 0; i < kReadOnlyFileLimit; i++) { + ASSERT_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i])); } - int status; - waitpid(pid, &status, 0); - ASSERT_EQ(0, WEXITSTATUS(status)); - - // cleanup - ASSERT_OK(env_->UnlockFile(lockFile)); - delete seqFile; - delete randFile; - delete writeFile; - delete appendFile; - delete logFile; - for (const std::string& test_file : test_files) { - ASSERT_OK(env_->DeleteFile(test_file)); + + leveldb::RandomAccessFile* file = nullptr; + ASSERT_OK(env_->NewRandomAccessFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + for (int i = 0; i < kReadOnlyFileLimit; i++) { + delete mmapped_files[i]; } + ASSERT_OK(env_->DeleteFile(file_path)); } -#endif // defined(HAVE_O_CLOEXEC) - -int CloexecChild() { - // Checks for open file descriptors in the range 3..FD_SETSIZE. - for (int i = 3; i < FD_SETSIZE; i++) { - int dup_result = dup2(i, i); - if (dup_result != -1) { - printf("Unexpected open file %d\n", i); - char nbuf[28]; - snprintf(nbuf, 28, "/proc/self/fd/%d", i); - char dbuf[1024]; - int result = readlink(nbuf, dbuf, 1024); - if (0 < result && result < 1024) { - dbuf[result] = 0; - printf("File descriptor %d is %s\n", i, dbuf); - if (strstr(dbuf, "close_on_exec_") == nullptr) { - continue; - } - } else if (result >= 1024) { - printf("(file name length is too long)\n"); - } else { - printf("Couldn't get file name: %s\n", strerror(errno)); - } - return 3; - } else { - int e = errno; - if (e != EBADF) { - printf("Unexpected result reading file handle %d: %s\n", i, - strerror(errno)); - return 4; - } - } - } - return 0; +TEST(EnvPosixTest, TestCloseOnExecWritableFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_writable.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::WritableFile* file = nullptr; + ASSERT_OK(env_->NewWritableFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecAppendableFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_appendable.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::WritableFile* file = nullptr; + ASSERT_OK(env_->NewAppendableFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecLockFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_lock.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::FileLock* lock = nullptr; + ASSERT_OK(env_->LockFile(file_path, &lock)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + ASSERT_OK(env_->UnlockFile(lock)); + + ASSERT_OK(env_->DeleteFile(file_path)); } +TEST(EnvPosixTest, TestCloseOnExecLogger) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_logger.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::Logger* file = nullptr; + ASSERT_OK(env_->NewLogger(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +#endif // HAVE_O_CLOEXEC + } // namespace leveldb int main(int argc, char** argv) { - // Check if this is the child process for TestCloseOnExec - if (argc > 1 && strcmp(argv[1], "-cloexec-child") == 0) { - return leveldb::CloexecChild(); +#if HAVE_O_CLOEXEC + // Check if we're invoked as a helper program, or as the test suite. + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) { + return TestCloseOnExecHelperMain(argv[i + 1]); + } } + + // Save argv[0] early, because googletest may modify argv. + GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1); +#endif // HAVE_O_CLOEXEC + // All tests currently run with the same read-only file limits. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, leveldb::kMMapLimit); From b7b86baec9ce47569affc5db54a20a6cc520e0f0 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Wed, 8 May 2019 17:28:28 -0700 Subject: [PATCH 170/174] Using std::ostringstream in key DebugString. Switching from snprintf to std::ostringstream eliminates cast warning for (unsigned long long). PiperOrigin-RevId: 247326681 --- db/dbformat.cc | 23 ++++++++++------------- db/dbformat_test.cc | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/db/dbformat.cc b/db/dbformat.cc index 69e8dc6aa8..459eddf5b1 100644 --- a/db/dbformat.cc +++ b/db/dbformat.cc @@ -6,6 +6,8 @@ #include +#include + #include "port/port.h" #include "util/coding.h" @@ -23,25 +25,20 @@ void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { } std::string ParsedInternalKey::DebugString() const { - char buf[50]; - snprintf(buf, sizeof(buf), "' @ %llu : %d", (unsigned long long)sequence, - int(type)); - std::string result = "'"; - result += EscapeString(user_key.ToString()); - result += buf; - return result; + std::ostringstream ss; + ss << '\'' << EscapeString(user_key.ToString()) << "' @ " << sequence << " : " + << static_cast(type); + return ss.str(); } std::string InternalKey::DebugString() const { - std::string result; ParsedInternalKey parsed; if (ParseInternalKey(rep_, &parsed)) { - result = parsed.DebugString(); - } else { - result = "(bad)"; - result.append(EscapeString(rep_)); + return parsed.DebugString(); } - return result; + std::ostringstream ss; + ss << "(bad)" << EscapeString(rep_); + return ss.str(); } const char* InternalKeyComparator::Name() const { diff --git a/db/dbformat_test.cc b/db/dbformat_test.cc index 87e6aae50d..f75d850ae3 100644 --- a/db/dbformat_test.cc +++ b/db/dbformat_test.cc @@ -106,6 +106,20 @@ TEST(FormatTest, InternalKeyShortestSuccessor) { ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); } +TEST(FormatTest, ParsedInternalKeyDebugString) { + ParsedInternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue); + + ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString()); +} + +TEST(FormatTest, InternalKeyDebugString) { + InternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue); + ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString()); + + InternalKey invalid_key; + ASSERT_EQ("(bad)", invalid_key.DebugString()); +} + } // namespace leveldb int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } From 85cd40d108d8f8d91f58fd263c0f8428d11c34d5 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 9 May 2019 13:17:39 -0700 Subject: [PATCH 171/174] Added unit test for InternalKey::DecodeFrom with empty string. PiperOrigin-RevId: 247483339 --- db/dbformat_test.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/db/dbformat_test.cc b/db/dbformat_test.cc index f75d850ae3..1209369c31 100644 --- a/db/dbformat_test.cc +++ b/db/dbformat_test.cc @@ -65,6 +65,12 @@ TEST(FormatTest, InternalKey_EncodeDecode) { } } +TEST(FormatTest, InternalKey_DecodeFromEmpty) { + InternalKey internal_key; + + ASSERT_TRUE(!internal_key.DecodeFrom("")); +} + TEST(FormatTest, InternalKeyShortSeparator) { // When user keys are same ASSERT_EQ(IKey("foo", 100, kTypeValue), From 28e6d238be73e743c963fc0a26395b783a7565e2 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Thu, 9 May 2019 14:00:07 -0700 Subject: [PATCH 172/174] Switch to using C++ 11 override specifier. PiperOrigin-RevId: 247491163 --- db/c.cc | 22 +++++++++--------- db/db_impl.cc | 2 +- db/db_impl.h | 25 ++++++++++---------- db/db_iter.cc | 20 ++++++++-------- db/db_test.cc | 46 ++++++++++++++++++------------------- db/dbformat.h | 16 ++++++------- db/dumpfile.cc | 6 ++--- db/leveldbutil.cc | 8 +++---- db/log_test.cc | 14 +++++------ db/repair.cc | 2 +- db/version_set.cc | 20 ++++++++-------- helpers/memenv/memenv.cc | 24 +++++++++---------- table/block.cc | 18 +++++++-------- table/filter_block_test.cc | 6 ++--- table/merger.cc | 20 ++++++++-------- table/table_test.cc | 26 ++++++++++----------- table/two_level_iterator.cc | 20 ++++++++-------- util/bloom.cc | 6 ++--- util/cache.cc | 20 ++++++++-------- 19 files changed, 161 insertions(+), 160 deletions(-) diff --git a/db/c.cc b/db/c.cc index e0f3367dcb..1f6fd64f3b 100644 --- a/db/c.cc +++ b/db/c.cc @@ -84,17 +84,17 @@ struct leveldb_filelock_t { }; struct leveldb_comparator_t : public Comparator { - virtual ~leveldb_comparator_t() { (*destructor_)(state_); } + ~leveldb_comparator_t() override { (*destructor_)(state_); } - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); } - virtual const char* Name() const { return (*name_)(state_); } + const char* Name() const override { return (*name_)(state_); } // No-ops since the C binding does not support key shortening methods. - virtual void FindShortestSeparator(std::string*, const Slice&) const {} - virtual void FindShortSuccessor(std::string* key) const {} + void FindShortestSeparator(std::string*, const Slice&) const override {} + void FindShortSuccessor(std::string* key) const override {} void* state_; void (*destructor_)(void*); @@ -104,11 +104,11 @@ struct leveldb_comparator_t : public Comparator { }; struct leveldb_filterpolicy_t : public FilterPolicy { - virtual ~leveldb_filterpolicy_t() { (*destructor_)(state_); } + ~leveldb_filterpolicy_t() override { (*destructor_)(state_); } - virtual const char* Name() const { return (*name_)(state_); } + const char* Name() const override { return (*name_)(state_); } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { std::vector key_pointers(n); std::vector key_sizes(n); for (int i = 0; i < n; i++) { @@ -121,7 +121,7 @@ struct leveldb_filterpolicy_t : public FilterPolicy { free(filter); } - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + bool KeyMayMatch(const Slice& key, const Slice& filter) const override { return (*key_match_)(state_, key.data(), key.size(), filter.data(), filter.size()); } @@ -345,10 +345,10 @@ void leveldb_writebatch_iterate(const leveldb_writebatch_t* b, void* state, void* state_; void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); void (*deleted_)(void*, const char* k, size_t klen); - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { (*put_)(state_, key.data(), key.size(), value.data(), value.size()); } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { (*deleted_)(state_, key.data(), key.size()); } }; diff --git a/db/db_impl.cc b/db/db_impl.cc index 82be594976..22c0d53d36 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -376,7 +376,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, Logger* info_log; const char* fname; Status* status; // null if options_.paranoid_checks==false - virtual void Corruption(size_t bytes, const Status& s) { + void Corruption(size_t bytes, const Status& s) override { Log(info_log, "%s%s: dropping %d bytes; %s", (this->status == nullptr ? "(ignoring error) " : ""), fname, static_cast(bytes), s.ToString().c_str()); diff --git a/db/db_impl.h b/db/db_impl.h index ae87d6e334..a3f1ed1664 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -33,20 +33,21 @@ class DBImpl : public DB { DBImpl(const DBImpl&) = delete; DBImpl& operator=(const DBImpl&) = delete; - virtual ~DBImpl(); + ~DBImpl() override; // Implementations of the DB interface - virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); - virtual Status Delete(const WriteOptions&, const Slice& key); - virtual Status Write(const WriteOptions& options, WriteBatch* updates); - virtual Status Get(const ReadOptions& options, const Slice& key, - std::string* value); - virtual Iterator* NewIterator(const ReadOptions&); - virtual const Snapshot* GetSnapshot(); - virtual void ReleaseSnapshot(const Snapshot* snapshot); - virtual bool GetProperty(const Slice& property, std::string* value); - virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); - virtual void CompactRange(const Slice* begin, const Slice* end); + Status Put(const WriteOptions&, const Slice& key, + const Slice& value) override; + Status Delete(const WriteOptions&, const Slice& key) override; + Status Write(const WriteOptions& options, WriteBatch* updates) override; + Status Get(const ReadOptions& options, const Slice& key, + std::string* value) override; + Iterator* NewIterator(const ReadOptions&) override; + const Snapshot* GetSnapshot() override; + void ReleaseSnapshot(const Snapshot* snapshot) override; + bool GetProperty(const Slice& property, std::string* value) override; + void GetApproximateSizes(const Range* range, int n, uint64_t* sizes) override; + void CompactRange(const Slice* begin, const Slice* end) override; // Extra methods (for testing) that are not in the public DB interface diff --git a/db/db_iter.cc b/db/db_iter.cc index 6e52550e52..98715a9502 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -59,17 +59,17 @@ class DBIter : public Iterator { DBIter(const DBIter&) = delete; DBIter& operator=(const DBIter&) = delete; - virtual ~DBIter() { delete iter_; } - virtual bool Valid() const { return valid_; } - virtual Slice key() const { + ~DBIter() override { delete iter_; } + bool Valid() const override { return valid_; } + Slice key() const override { assert(valid_); return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; } - virtual Slice value() const { + Slice value() const override { assert(valid_); return (direction_ == kForward) ? iter_->value() : saved_value_; } - virtual Status status() const { + Status status() const override { if (status_.ok()) { return iter_->status(); } else { @@ -77,11 +77,11 @@ class DBIter : public Iterator { } } - virtual void Next(); - virtual void Prev(); - virtual void Seek(const Slice& target); - virtual void SeekToFirst(); - virtual void SeekToLast(); + void Next() override; + void Prev() override; + void Seek(const Slice& target) override; + void SeekToFirst() override; + void SeekToLast() override; private: void FindNextUserEntry(bool skipping, std::string* skip); diff --git a/db/db_test.cc b/db/db_test.cc index 1da8db26f7..9a8faf1006 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -210,9 +210,9 @@ class SpecialEnv : public EnvWrapper { public: CountingFile(RandomAccessFile* target, AtomicCounter* counter) : target_(target), counter_(counter) {} - virtual ~CountingFile() { delete target_; } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + ~CountingFile() override { delete target_; } + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { counter_->Increment(); return target_->Read(offset, n, result, scratch); } @@ -1504,14 +1504,14 @@ TEST(DBTest, Fflush_Issue474) { TEST(DBTest, ComparatorCheck) { class NewComparator : public Comparator { public: - virtual const char* Name() const { return "leveldb.NewComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + const char* Name() const override { return "leveldb.NewComparator"; } + int Compare(const Slice& a, const Slice& b) const override { return BytewiseComparator()->Compare(a, b); } - virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + void FindShortestSeparator(std::string* s, const Slice& l) const override { BytewiseComparator()->FindShortestSeparator(s, l); } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { BytewiseComparator()->FindShortSuccessor(key); } }; @@ -1527,15 +1527,15 @@ TEST(DBTest, ComparatorCheck) { TEST(DBTest, CustomComparator) { class NumberComparator : public Comparator { public: - virtual const char* Name() const { return "test.NumberComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + const char* Name() const override { return "test.NumberComparator"; } + int Compare(const Slice& a, const Slice& b) const override { return ToNumber(a) - ToNumber(b); } - virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + void FindShortestSeparator(std::string* s, const Slice& l) const override { ToNumber(*s); // Check format ToNumber(l); // Check format } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { ToNumber(*key); // Check format } @@ -2060,10 +2060,10 @@ class ModelDB : public DB { class Handler : public WriteBatch::Handler { public: KVMap* map_; - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { (*map_)[key.ToString()] = value.ToString(); } - virtual void Delete(const Slice& key) { map_->erase(key.ToString()); } + void Delete(const Slice& key) override { map_->erase(key.ToString()); } }; Handler handler; handler.map_ = &map_; @@ -2085,26 +2085,26 @@ class ModelDB : public DB { public: ModelIter(const KVMap* map, bool owned) : map_(map), owned_(owned), iter_(map_->end()) {} - ~ModelIter() { + ~ModelIter() override { if (owned_) delete map_; } - virtual bool Valid() const { return iter_ != map_->end(); } - virtual void SeekToFirst() { iter_ = map_->begin(); } - virtual void SeekToLast() { + 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); } } - virtual void Seek(const Slice& k) { + void Seek(const Slice& k) override { iter_ = map_->lower_bound(k.ToString()); } - virtual void Next() { ++iter_; } - virtual void Prev() { --iter_; } - virtual Slice key() const { return iter_->first; } - virtual Slice value() const { return iter_->second; } - virtual Status status() const { return Status::OK(); } + void Next() override { ++iter_; } + void Prev() override { --iter_; } + Slice key() const override { return iter_->first; } + Slice value() const override { return iter_->second; } + Status status() const override { return Status::OK(); } private: const KVMap* const map_; diff --git a/db/dbformat.h b/db/dbformat.h index f990040511..abcb489f53 100644 --- a/db/dbformat.h +++ b/db/dbformat.h @@ -103,11 +103,11 @@ class InternalKeyComparator : public Comparator { public: explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) {} - virtual const char* Name() const; - virtual int Compare(const Slice& a, const Slice& b) const; - virtual void FindShortestSeparator(std::string* start, - const Slice& limit) const; - virtual void FindShortSuccessor(std::string* key) const; + const char* Name() const override; + int Compare(const Slice& a, const Slice& b) const override; + void FindShortestSeparator(std::string* start, + const Slice& limit) const override; + void FindShortSuccessor(std::string* key) const override; const Comparator* user_comparator() const { return user_comparator_; } @@ -121,9 +121,9 @@ class InternalFilterPolicy : public FilterPolicy { public: explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) {} - virtual const char* Name() const; - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; + const char* Name() const override; + void CreateFilter(const Slice* keys, int n, std::string* dst) const override; + bool KeyMayMatch(const Slice& key, const Slice& filter) const override; }; // Modules in this directory should keep internal keys wrapped inside diff --git a/db/dumpfile.cc b/db/dumpfile.cc index 9d22d58d1b..77d59003cf 100644 --- a/db/dumpfile.cc +++ b/db/dumpfile.cc @@ -38,7 +38,7 @@ bool GuessType(const std::string& fname, FileType* type) { // Notified when log reader encounters corruption. class CorruptionReporter : public log::Reader::Reporter { public: - virtual void Corruption(size_t bytes, const Status& status) { + void Corruption(size_t bytes, const Status& status) override { std::string r = "corruption: "; AppendNumberTo(&r, bytes); r += " bytes; "; @@ -74,7 +74,7 @@ Status PrintLogContents(Env* env, const std::string& fname, // Called on every item found in a WriteBatch. class WriteBatchItemPrinter : public WriteBatch::Handler { public: - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { std::string r = " put '"; AppendEscapedStringTo(&r, key); r += "' '"; @@ -82,7 +82,7 @@ class WriteBatchItemPrinter : public WriteBatch::Handler { r += "'\n"; dst_->Append(r); } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { std::string r = " del '"; AppendEscapedStringTo(&r, key); r += "'\n"; diff --git a/db/leveldbutil.cc b/db/leveldbutil.cc index b21cf8efa4..55cdcc5772 100644 --- a/db/leveldbutil.cc +++ b/db/leveldbutil.cc @@ -13,13 +13,13 @@ namespace { class StdoutPrinter : public WritableFile { public: - virtual Status Append(const Slice& data) { + Status Append(const Slice& data) override { fwrite(data.data(), 1, data.size(), stdout); return Status::OK(); } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } }; bool HandleDumpCommand(Env* env, char** files, int num) { diff --git a/db/log_test.cc b/db/log_test.cc index 809c4183b4..0e31648cc8 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -161,10 +161,10 @@ class LogTest { private: class StringDest : public WritableFile { public: - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } - virtual Status Append(const Slice& slice) { + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } + Status Append(const Slice& slice) override { contents_.append(slice.data(), slice.size()); return Status::OK(); } @@ -176,7 +176,7 @@ class LogTest { public: StringSource() : force_error_(false), returned_partial_(false) {} - virtual Status Read(size_t n, Slice* result, char* scratch) { + Status Read(size_t n, Slice* result, char* scratch) override { ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; if (force_error_) { @@ -194,7 +194,7 @@ class LogTest { return Status::OK(); } - virtual Status Skip(uint64_t n) { + Status Skip(uint64_t n) override { if (n > contents_.size()) { contents_.clear(); return Status::NotFound("in-memory file skipped past end"); @@ -213,7 +213,7 @@ class LogTest { class ReportCollector : public Reader::Reporter { public: ReportCollector() : dropped_bytes_(0) {} - virtual void Corruption(size_t bytes, const Status& status) { + void Corruption(size_t bytes, const Status& status) override { dropped_bytes_ += bytes; message_.append(status.ToString()); } diff --git a/db/repair.cc b/db/repair.cc index 3c676ca34f..d9d12ba556 100644 --- a/db/repair.cc +++ b/db/repair.cc @@ -145,7 +145,7 @@ class Repairer { Env* env; Logger* info_log; uint64_t lognum; - virtual void Corruption(size_t bytes, const Status& s) { + void Corruption(size_t bytes, const Status& s) override { // We print error messages for corruption, but continue repairing. Log(info_log, "Log #%llu: dropping %d bytes; %s", (unsigned long long)lognum, static_cast(bytes), diff --git a/db/version_set.cc b/db/version_set.cc index 099fa5763d..0f8bec1b5f 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -167,19 +167,19 @@ class Version::LevelFileNumIterator : public Iterator { const std::vector* flist) : icmp_(icmp), flist_(flist), index_(flist->size()) { // Marks as invalid } - virtual bool Valid() const { return index_ < flist_->size(); } - virtual void Seek(const Slice& target) { + bool Valid() const override { return index_ < flist_->size(); } + void Seek(const Slice& target) override { index_ = FindFile(icmp_, *flist_, target); } - virtual void SeekToFirst() { index_ = 0; } - virtual void SeekToLast() { + void SeekToFirst() override { index_ = 0; } + void SeekToLast() override { index_ = flist_->empty() ? 0 : flist_->size() - 1; } - virtual void Next() { + void Next() override { assert(Valid()); index_++; } - virtual void Prev() { + void Prev() override { assert(Valid()); if (index_ == 0) { index_ = flist_->size(); // Marks as invalid @@ -187,17 +187,17 @@ class Version::LevelFileNumIterator : public Iterator { index_--; } } - Slice key() const { + Slice key() const override { assert(Valid()); return (*flist_)[index_]->largest.Encode(); } - Slice value() const { + Slice value() const override { assert(Valid()); EncodeFixed64(value_buf_, (*flist_)[index_]->number); EncodeFixed64(value_buf_ + 8, (*flist_)[index_]->file_size); return Slice(value_buf_, sizeof(value_buf_)); } - virtual Status status() const { return Status::OK(); } + Status status() const override { return Status::OK(); } private: const InternalKeyComparator icmp_; @@ -883,7 +883,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { Status VersionSet::Recover(bool* save_manifest) { struct LogReporter : public log::Reader::Reporter { Status* status; - virtual void Corruption(size_t bytes, const Status& s) { + void Corruption(size_t bytes, const Status& s) override { if (this->status->ok()) *this->status = s; } }; diff --git a/helpers/memenv/memenv.cc b/helpers/memenv/memenv.cc index 2d4fbaa09a..31d2bc0f6f 100644 --- a/helpers/memenv/memenv.cc +++ b/helpers/memenv/memenv.cc @@ -156,9 +156,9 @@ class SequentialFileImpl : public SequentialFile { file_->Ref(); } - ~SequentialFileImpl() { file_->Unref(); } + ~SequentialFileImpl() override { file_->Unref(); } - virtual Status Read(size_t n, Slice* result, char* scratch) { + Status Read(size_t n, Slice* result, char* scratch) override { Status s = file_->Read(pos_, n, result, scratch); if (s.ok()) { pos_ += result->size(); @@ -166,7 +166,7 @@ class SequentialFileImpl : public SequentialFile { return s; } - virtual Status Skip(uint64_t n) { + Status Skip(uint64_t n) override { if (pos_ > file_->Size()) { return Status::IOError("pos_ > file_->Size()"); } @@ -187,10 +187,10 @@ class RandomAccessFileImpl : public RandomAccessFile { public: explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~RandomAccessFileImpl() { file_->Unref(); } + ~RandomAccessFileImpl() override { file_->Unref(); } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { return file_->Read(offset, n, result, scratch); } @@ -202,13 +202,13 @@ class WritableFileImpl : public WritableFile { public: WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~WritableFileImpl() { file_->Unref(); } + ~WritableFileImpl() override { file_->Unref(); } - virtual Status Append(const Slice& data) { return file_->Append(data); } + Status Append(const Slice& data) override { return file_->Append(data); } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } private: FileState* file_; @@ -216,7 +216,7 @@ class WritableFileImpl : public WritableFile { class NoOpLogger : public Logger { public: - virtual void Logv(const char* format, va_list ap) {} + void Logv(const char* format, va_list ap) override {} }; class InMemoryEnv : public EnvWrapper { diff --git a/table/block.cc b/table/block.cc index ad0ee98aa7..05c600fab0 100644 --- a/table/block.cc +++ b/table/block.cc @@ -123,23 +123,23 @@ class Block::Iter : public Iterator { assert(num_restarts_ > 0); } - virtual bool Valid() const { return current_ < restarts_; } - virtual Status status() const { return status_; } - virtual Slice key() const { + bool Valid() const override { return current_ < restarts_; } + Status status() const override { return status_; } + Slice key() const override { assert(Valid()); return key_; } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return value_; } - virtual void Next() { + void Next() override { assert(Valid()); ParseNextKey(); } - virtual void Prev() { + void Prev() override { assert(Valid()); // Scan backwards to a restart point before current_ @@ -160,7 +160,7 @@ class Block::Iter : public Iterator { } while (ParseNextKey() && NextEntryOffset() < original); } - virtual void Seek(const Slice& target) { + void Seek(const Slice& target) override { // Binary search in restart array to find the last restart point // with a key < target uint32_t left = 0; @@ -200,12 +200,12 @@ class Block::Iter : public Iterator { } } - virtual void SeekToFirst() { + void SeekToFirst() override { SeekToRestartPoint(0); ParseNextKey(); } - virtual void SeekToLast() { + void SeekToLast() override { SeekToRestartPoint(num_restarts_ - 1); while (ParseNextKey() && NextEntryOffset() < restarts_) { // Keep skipping diff --git a/table/filter_block_test.cc b/table/filter_block_test.cc index 6cdd435cf9..8b33bbdd18 100644 --- a/table/filter_block_test.cc +++ b/table/filter_block_test.cc @@ -16,16 +16,16 @@ namespace leveldb { // For testing: emit an array with one hash value per key class TestHashFilter : public FilterPolicy { public: - virtual const char* Name() const { return "TestHashFilter"; } + const char* Name() const override { return "TestHashFilter"; } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { for (int i = 0; i < n; i++) { uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); PutFixed32(dst, h); } } - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + bool KeyMayMatch(const Slice& key, const Slice& filter) const override { uint32_t h = Hash(key.data(), key.size(), 1); for (size_t i = 0; i + 4 <= filter.size(); i += 4) { if (h == DecodeFixed32(filter.data() + i)) { diff --git a/table/merger.cc b/table/merger.cc index 1bbc6cfd0a..76441b1cc2 100644 --- a/table/merger.cc +++ b/table/merger.cc @@ -24,11 +24,11 @@ class MergingIterator : public Iterator { } } - virtual ~MergingIterator() { delete[] children_; } + ~MergingIterator() override { delete[] children_; } - virtual bool Valid() const { return (current_ != nullptr); } + bool Valid() const override { return (current_ != nullptr); } - virtual void SeekToFirst() { + void SeekToFirst() override { for (int i = 0; i < n_; i++) { children_[i].SeekToFirst(); } @@ -36,7 +36,7 @@ class MergingIterator : public Iterator { direction_ = kForward; } - virtual void SeekToLast() { + void SeekToLast() override { for (int i = 0; i < n_; i++) { children_[i].SeekToLast(); } @@ -44,7 +44,7 @@ class MergingIterator : public Iterator { direction_ = kReverse; } - virtual void Seek(const Slice& target) { + void Seek(const Slice& target) override { for (int i = 0; i < n_; i++) { children_[i].Seek(target); } @@ -52,7 +52,7 @@ class MergingIterator : public Iterator { direction_ = kForward; } - virtual void Next() { + void Next() override { assert(Valid()); // Ensure that all children are positioned after key(). @@ -78,7 +78,7 @@ class MergingIterator : public Iterator { FindSmallest(); } - virtual void Prev() { + void Prev() override { assert(Valid()); // Ensure that all children are positioned before key(). @@ -107,17 +107,17 @@ class MergingIterator : public Iterator { FindLargest(); } - virtual Slice key() const { + Slice key() const override { assert(Valid()); return current_->key(); } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return current_->value(); } - virtual Status status() const { + Status status() const override { Status status; for (int i = 0; i < n_; i++) { status = children_[i].status(); diff --git a/table/table_test.cc b/table/table_test.cc index 3c63e32a94..f689a27657 100644 --- a/table/table_test.cc +++ b/table/table_test.cc @@ -38,23 +38,23 @@ static std::string Reverse(const Slice& key) { namespace { class ReverseKeyComparator : public Comparator { public: - virtual const char* Name() const { + const char* Name() const override { return "leveldb.ReverseBytewiseComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); } - virtual void FindShortestSeparator(std::string* start, - const Slice& limit) const { + void FindShortestSeparator(std::string* start, + const Slice& limit) const override { std::string s = Reverse(*start); std::string l = Reverse(limit); BytewiseComparator()->FindShortestSeparator(&s, l); *start = Reverse(s); } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { std::string s = Reverse(*key); BytewiseComparator()->FindShortSuccessor(&s); *key = Reverse(s); @@ -89,15 +89,15 @@ struct STLLessThan { class StringSink : public WritableFile { public: - ~StringSink() = default; + ~StringSink() override = default; const std::string& contents() const { return contents_; } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } - virtual Status Append(const Slice& data) { + Status Append(const Slice& data) override { contents_.append(data.data(), data.size()); return Status::OK(); } @@ -111,12 +111,12 @@ class StringSource : public RandomAccessFile { StringSource(const Slice& contents) : contents_(contents.data(), contents.size()) {} - virtual ~StringSource() = default; + ~StringSource() override = default; uint64_t Size() const { return contents_.size(); } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { if (offset >= contents_.size()) { return Status::InvalidArgument("invalid Read offset"); } diff --git a/table/two_level_iterator.cc b/table/two_level_iterator.cc index 1fc46268c9..144790dd97 100644 --- a/table/two_level_iterator.cc +++ b/table/two_level_iterator.cc @@ -20,24 +20,24 @@ class TwoLevelIterator : public Iterator { TwoLevelIterator(Iterator* index_iter, BlockFunction block_function, void* arg, const ReadOptions& options); - virtual ~TwoLevelIterator(); + ~TwoLevelIterator() override; - virtual void Seek(const Slice& target); - virtual void SeekToFirst(); - virtual void SeekToLast(); - virtual void Next(); - virtual void Prev(); + void Seek(const Slice& target) override; + void SeekToFirst() override; + void SeekToLast() override; + void Next() override; + void Prev() override; - virtual bool Valid() const { return data_iter_.Valid(); } - virtual Slice key() const { + bool Valid() const override { return data_iter_.Valid(); } + Slice key() const override { assert(Valid()); return data_iter_.key(); } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return data_iter_.value(); } - virtual Status status() const { + Status status() const override { // It'd be nice if status() returned a const Status& instead of a Status if (!index_iter_.status().ok()) { return index_iter_.status(); diff --git a/util/bloom.cc b/util/bloom.cc index 7f974649ed..87547a7e62 100644 --- a/util/bloom.cc +++ b/util/bloom.cc @@ -23,9 +23,9 @@ class BloomFilterPolicy : public FilterPolicy { if (k_ > 30) k_ = 30; } - virtual const char* Name() const { return "leveldb.BuiltinBloomFilter2"; } + const char* Name() const override { return "leveldb.BuiltinBloomFilter2"; } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { // Compute bloom filter size (in both bits and bytes) size_t bits = n * bits_per_key_; @@ -53,7 +53,7 @@ class BloomFilterPolicy : public FilterPolicy { } } - virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const override { const size_t len = bloom_filter.size(); if (len < 2) return false; diff --git a/util/cache.cc b/util/cache.cc index 0f801ccc8e..12de306cad 100644 --- a/util/cache.cc +++ b/util/cache.cc @@ -354,37 +354,37 @@ class ShardedLRUCache : public Cache { shard_[s].SetCapacity(per_shard); } } - virtual ~ShardedLRUCache() {} - virtual Handle* Insert(const Slice& key, void* value, size_t charge, - void (*deleter)(const Slice& key, void* value)) { + ~ShardedLRUCache() override {} + Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) override { const uint32_t hash = HashSlice(key); return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); } - virtual Handle* Lookup(const Slice& key) { + Handle* Lookup(const Slice& key) override { const uint32_t hash = HashSlice(key); return shard_[Shard(hash)].Lookup(key, hash); } - virtual void Release(Handle* handle) { + void Release(Handle* handle) override { LRUHandle* h = reinterpret_cast(handle); shard_[Shard(h->hash)].Release(handle); } - virtual void Erase(const Slice& key) { + void Erase(const Slice& key) override { const uint32_t hash = HashSlice(key); shard_[Shard(hash)].Erase(key, hash); } - virtual void* Value(Handle* handle) { + void* Value(Handle* handle) override { return reinterpret_cast(handle)->value; } - virtual uint64_t NewId() { + uint64_t NewId() override { MutexLock l(&id_mutex_); return ++(last_id_); } - virtual void Prune() { + void Prune() override { for (int s = 0; s < kNumShards; s++) { shard_[s].Prune(); } } - virtual size_t TotalCharge() const { + size_t TotalCharge() const override { size_t total = 0; for (int s = 0; s < kNumShards; s++) { total += shard_[s].TotalCharge(); From 1d0b101165ddd34f26cc5c62b76f2a2e0d622483 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Mon, 13 May 2019 09:31:30 -0700 Subject: [PATCH 173/174] Converted two for-loops to while-loops. Converted `for (;;)` to `while ()`. PiperOrigin-RevId: 247950510 --- db/db_impl.cc | 2 +- db/version_set.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 22c0d53d36..067c67d95d 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -903,7 +903,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { std::string current_user_key; bool has_current_user_key = false; SequenceNumber last_sequence_for_key = kMaxSequenceNumber; - for (; input->Valid() && !shutting_down_.load(std::memory_order_acquire);) { + while (input->Valid() && !shutting_down_.load(std::memory_order_acquire)) { // Prioritize immutable compaction work if (has_imm_.load(std::memory_order_relaxed)) { const uint64_t imm_start = env_->NowMicros(); diff --git a/db/version_set.cc b/db/version_set.cc index 0f8bec1b5f..b62a2d0eea 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -1532,7 +1532,7 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) { const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { const std::vector& files = input_version_->files_[lvl]; - for (; level_ptrs_[lvl] < files.size();) { + while (level_ptrs_[lvl] < files.size()) { FileMetaData* f = files[level_ptrs_[lvl]]; if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { // We've advanced far enough From c00e177f3613068eda4bff4abfbd3bd4165a86e8 Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Wed, 15 May 2019 13:13:13 -0700 Subject: [PATCH 174/174] Guard DBImpl::versions_ by mutex_. mutex_ was already acquired before accessing DBImpl::versions_ in all but one place: DBImpl::GetApproximateSizes. This change requires mutex_ to be held before accessing versions_. PiperOrigin-RevId: 248390814 --- db/db_impl.cc | 17 ++++++----------- db/db_impl.h | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/db/db_impl.cc b/db/db_impl.cc index 067c67d95d..94b5d4c35c 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -893,10 +893,11 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { compact->smallest_snapshot = snapshots_.oldest()->sequence_number(); } + Iterator* input = versions_->MakeInputIterator(compact->compaction); + // Release mutex while we're actually doing the compaction work mutex_.Unlock(); - Iterator* input = versions_->MakeInputIterator(compact->compaction); input->SeekToFirst(); Status status; ParsedInternalKey ikey; @@ -1433,12 +1434,9 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { void DBImpl::GetApproximateSizes(const Range* range, int n, uint64_t* sizes) { // TODO(opt): better implementation - Version* v; - { - MutexLock l(&mutex_); - versions_->current()->Ref(); - v = versions_->current(); - } + MutexLock l(&mutex_); + Version* v = versions_->current(); + v->Ref(); for (int i = 0; i < n; i++) { // Convert user_key into a corresponding internal key. @@ -1449,10 +1447,7 @@ void DBImpl::GetApproximateSizes(const Range* range, int n, uint64_t* sizes) { sizes[i] = (limit >= start ? limit - start : 0); } - { - MutexLock l(&mutex_); - v->Unref(); - } + v->Unref(); } // Default implementations of convenience methods that subclasses of DB diff --git a/db/db_impl.h b/db/db_impl.h index a3f1ed1664..685735c733 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -197,7 +197,7 @@ class DBImpl : public DB { ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); - VersionSet* const versions_; + VersionSet* const versions_ GUARDED_BY(mutex_); // Have we encountered a background error in paranoid mode? Status bg_error_ GUARDED_BY(mutex_);