Imported Upstream version 1.23 upstream upstream/1.23
authorTizenOpenSource <tizenopensrc@samsung.com>
Thu, 21 Dec 2023 07:18:21 +0000 (16:18 +0900)
committerTizenOpenSource <tizenopensrc@samsung.com>
Thu, 21 Dec 2023 07:18:21 +0000 (16:18 +0900)
114 files changed:
.appveyor.yml
.clang-format
.gitmodules [new file with mode: 0644]
.travis.yml
CMakeLists.txt
CONTRIBUTING.md
README.md
benchmarks/db_bench.cc [moved from db/db_bench.cc with 75% similarity]
benchmarks/db_bench_sqlite3.cc [moved from doc/bench/db_bench_sqlite3.cc with 87% similarity]
benchmarks/db_bench_tree_db.cc [moved from doc/bench/db_bench_tree_db.cc with 82% similarity]
cmake/leveldbConfig.cmake [deleted file]
cmake/leveldbConfig.cmake.in [new file with mode: 0644]
db/autocompact_test.cc
db/builder.cc
db/c.cc
db/c_test.c
db/corruption_test.cc
db/db_impl.cc
db/db_impl.h
db/db_iter.cc
db/db_iter.h
db/db_test.cc
db/dbformat.cc
db/dbformat.h
db/dbformat_test.cc
db/dumpfile.cc
db/fault_injection_test.cc
db/filename.cc
db/filename.h
db/filename_test.cc
db/leveldbutil.cc
db/log_reader.cc
db/log_reader.h
db/log_test.cc
db/log_writer.cc
db/log_writer.h
db/memtable.cc
db/recovery_test.cc
db/repair.cc
db/skiplist_test.cc
db/table_cache.h
db/version_edit.cc
db/version_edit.h
db/version_edit_test.cc
db/version_set.cc
db/version_set_test.cc
db/write_batch.cc
db/write_batch_test.cc
doc/benchmark.html
doc/impl.md
doc/index.md
helpers/memenv/memenv.cc
helpers/memenv/memenv_test.cc
include/leveldb/c.h
include/leveldb/cache.h
include/leveldb/db.h
include/leveldb/env.h
include/leveldb/options.h
include/leveldb/slice.h
include/leveldb/table.h
include/leveldb/table_builder.h
issues/issue178_test.cc
issues/issue200_test.cc
issues/issue320_test.cc
port/port_config.h.in
port/port_example.h
port/port_stdcxx.h
table/block.cc
table/block.h
table/block_builder.cc
table/block_builder.h
table/filter_block.h
table/filter_block_test.cc
table/format.h
table/merger.cc
table/table.cc
table/table_builder.cc
table/table_test.cc
table/two_level_iterator.cc
util/arena_test.cc
util/bloom.cc
util/bloom_test.cc
util/cache.cc
util/cache_test.cc
util/coding.cc
util/coding.h
util/coding_test.cc
util/comparator.cc
util/crc32c.cc
util/crc32c.h
util/crc32c_test.cc
util/env.cc
util/env_posix.cc
util/env_posix_test.cc
util/env_test.cc
util/env_windows.cc
util/env_windows_test.cc
util/hash.cc
util/hash.h
util/hash_test.cc
util/histogram.cc
util/logging.cc
util/logging.h
util/logging_test.cc
util/no_destructor_test.cc
util/posix_logger.h
util/random.h
util/status.cc
util/status_test.cc
util/testharness.cc [deleted file]
util/testharness.h [deleted file]
util/testutil.cc
util/testutil.h
util/windows_logger.h

index c24b17e..448f183 100644 (file)
@@ -8,9 +8,9 @@ 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
+    - JOB: Visual Studio 2019
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      CMAKE_GENERATOR: Visual Studio 16 2019
 
 platform:
   - x86
@@ -24,9 +24,10 @@ build_script:
   - git submodule update --init --recursive
   - mkdir build
   - cd build
-  - if "%platform%"=="x64" set CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64
+  - if "%platform%"=="x86" (set CMAKE_GENERATOR_PLATFORM="Win32")
+      else (set CMAKE_GENERATOR_PLATFORM="%platform%")
   - cmake --version
-  - cmake .. -G "%CMAKE_GENERATOR%"
+  - cmake .. -G "%CMAKE_GENERATOR%" -A "%CMAKE_GENERATOR_PLATFORM%"
       -DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%"
   - cmake --build . --config "%CONFIGURATION%"
   - cd ..
index 75f3401..f493f75 100644 (file)
@@ -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/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..6e6d3f0
--- /dev/null
@@ -0,0 +1,6 @@
+[submodule "third_party/googletest"]
+       path = third_party/googletest
+       url = https://github.com/google/googletest.git
+[submodule "third_party/benchmark"]
+       path = third_party/benchmark
+       url = https://github.com/google/benchmark
index 436e037..e34a67e 100644 (file)
@@ -3,8 +3,8 @@
 # This file can be validated on: http://lint.travis-ci.org/
 
 language: cpp
-dist: xenial
-osx_image: xcode10.2
+dist: bionic
+osx_image: xcode12.2
 
 compiler:
 - gcc
@@ -17,16 +17,23 @@ env:
 - BUILD_TYPE=Debug
 - BUILD_TYPE=RelWithDebInfo
 
+jobs:
+  allow_failures:
+  # Homebrew's GCC is currently broken on XCode 11.
+  - compiler: gcc
+    os: osx
+
 addons:
   apt:
     sources:
-    - llvm-toolchain-xenial-8
-    - ubuntu-toolchain-r-test
+    - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
+      key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
+    - sourceline: 'ppa:ubuntu-toolchain-r/test'
     packages:
-    - clang-8
+    - clang-10
     - cmake
-    - gcc-8
-    - g++-8
+    - gcc-10
+    - g++-10
     - libgoogle-perftools-dev
     - libkyotocabinet-dev
     - libsnappy-dev
@@ -36,10 +43,10 @@ addons:
     packages:
     - cmake
     - crc32c
-    - gcc@8
+    - gcc@10
     - gperftools
-    - kyotocabinet
-    - llvm@8
+    - kyoto-cabinet
+    - llvm@10
     - ninja
     - snappy
     - sqlite3
@@ -48,18 +55,18 @@ addons:
 install:
 # The following Homebrew packages aren't linked by default, and need to be
 # prepended to the path explicitly.
-- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
+- 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
+- if [ "$CXX" = "g++" ]; then export CXX="g++-10" CC="gcc-10"; 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;
+- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+    if [ "$CXX" = "clang++" ]; then export CXX="clang++-10" CC="clang-10"; fi;
   fi
 - echo ${CC}
 - echo ${CXX}
@@ -69,6 +76,7 @@ install:
 before_script:
 - mkdir -p build && cd build
 - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
+    -DCMAKE_INSTALL_PREFIX=$HOME/.local
 - cmake --build .
 - cd ..
 
@@ -77,3 +85,4 @@ script:
 - "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"
+- cd build && cmake --build . --target install
index 1409c06..f8285b8 100644 (file)
@@ -4,17 +4,23 @@
 
 cmake_minimum_required(VERSION 3.9)
 # Keep the version below in sync with the one in db.h
-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)
-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 ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
+project(leveldb VERSION 1.23.0 LANGUAGES C CXX)
+
+# C standard can be overridden when this is used as a sub-project.
+if(NOT CMAKE_C_STANDARD)
+  # 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)
+endif(NOT CMAKE_C_STANDARD)
+
+# C++ standard can be overridden when this is used as a sub-project.
+if(NOT CMAKE_CXX_STANDARD)
+  # This project requires C++11.
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+  set(CMAKE_CXX_EXTENSIONS OFF)
+endif(NOT CMAKE_CXX_STANDARD)
 
 if (WIN32)
   set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS)
@@ -28,9 +34,6 @@ 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)
 
@@ -47,26 +50,42 @@ 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)
-
-include(CheckCXXSourceCompiles)
+check_cxx_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC)
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+  # Disable C++ exceptions.
+  string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c-")
+  add_definitions(-D_HAS_EXCEPTIONS=0)
+
+  # Disable RTTI.
+  string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
+else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+  # Enable strict prototype warnings for C code in clang and gcc.
+  if(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
+  endif(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes")
+
+  # Disable C++ exceptions.
+  string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
+
+  # Disable RTTI.
+  string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
 
 # 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})
+include(CheckCXXCompilerFlag)
+check_cxx_compiler_flag(-Wthread-safety HAVE_CLANG_THREAD_SAFETY)
+
+# Used by googletest.
+check_cxx_compiler_flag(-Wno-missing-field-initializers
+                        LEVELDB_HAVE_NO_MISSING_FIELD_INITIALIZERS)
+
+include(CheckCXXSourceCompiles)
 
 # Test whether C++17 __has_include is available.
 check_cxx_source_compiles("
@@ -80,13 +99,13 @@ set(LEVELDB_PUBLIC_INCLUDE_DIR "include/leveldb")
 set(LEVELDB_PORT_CONFIG_DIR "include/port")
 
 configure_file(
-  "${PROJECT_SOURCE_DIR}/port/port_config.h.in"
+  "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)
@@ -94,79 +113,82 @@ if(BUILD_SHARED_LIBS)
   add_compile_options(-fvisibility=hidden)
 endif(BUILD_SHARED_LIBS)
 
+# Must be included before CMAKE_INSTALL_INCLUDEDIR is used.
+include(GNUInstallDirs)
+
 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"
-    "${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_stdcxx.h"
-    "${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.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/no_destructor.h"
-    "${PROJECT_SOURCE_DIR}/util/options.cc"
-    "${PROJECT_SOURCE_DIR}/util/random.h"
-    "${PROJECT_SOURCE_DIR}/util/status.cc"
+    "db/builder.cc"
+    "db/builder.h"
+    "db/c.cc"
+    "db/db_impl.cc"
+    "db/db_impl.h"
+    "db/db_iter.cc"
+    "db/db_iter.h"
+    "db/dbformat.cc"
+    "db/dbformat.h"
+    "db/dumpfile.cc"
+    "db/filename.cc"
+    "db/filename.h"
+    "db/log_format.h"
+    "db/log_reader.cc"
+    "db/log_reader.h"
+    "db/log_writer.cc"
+    "db/log_writer.h"
+    "db/memtable.cc"
+    "db/memtable.h"
+    "db/repair.cc"
+    "db/skiplist.h"
+    "db/snapshot.h"
+    "db/table_cache.cc"
+    "db/table_cache.h"
+    "db/version_edit.cc"
+    "db/version_edit.h"
+    "db/version_set.cc"
+    "db/version_set.h"
+    "db/write_batch_internal.h"
+    "db/write_batch.cc"
+    "port/port_stdcxx.h"
+    "port/port.h"
+    "port/thread_annotations.h"
+    "table/block_builder.cc"
+    "table/block_builder.h"
+    "table/block.cc"
+    "table/block.h"
+    "table/filter_block.cc"
+    "table/filter_block.h"
+    "table/format.cc"
+    "table/format.h"
+    "table/iterator_wrapper.h"
+    "table/iterator.cc"
+    "table/merger.cc"
+    "table/merger.h"
+    "table/table_builder.cc"
+    "table/table.cc"
+    "table/two_level_iterator.cc"
+    "table/two_level_iterator.h"
+    "util/arena.cc"
+    "util/arena.h"
+    "util/bloom.cc"
+    "util/cache.cc"
+    "util/coding.cc"
+    "util/coding.h"
+    "util/comparator.cc"
+    "util/crc32c.cc"
+    "util/crc32c.h"
+    "util/env.cc"
+    "util/filter_policy.cc"
+    "util/hash.cc"
+    "util/hash.h"
+    "util/logging.cc"
+    "util/logging.h"
+    "util/mutexlock.h"
+    "util/no_destructor.h"
+    "util/options.cc"
+    "util/random.h"
+    "util/status.cc"
 
   # Only CMake 3.3+ supports PUBLIC sources in targets exported by "install".
   $<$<VERSION_GREATER:CMAKE_VERSION,3.2>:PUBLIC>
@@ -190,22 +212,22 @@ target_sources(leveldb
 if (WIN32)
   target_sources(leveldb
     PRIVATE
-      "${PROJECT_SOURCE_DIR}/util/env_windows.cc"
-      "${PROJECT_SOURCE_DIR}/util/windows_logger.h"
+      "util/env_windows.cc"
+      "util/windows_logger.h"
   )
 else (WIN32)
   target_sources(leveldb
     PRIVATE
-      "${PROJECT_SOURCE_DIR}/util/env_posix.cc"
-      "${PROJECT_SOURCE_DIR}/util/posix_logger.h"
+      "util/env_posix.cc"
+      "util/posix_logger.h"
   )
 endif (WIN32)
 
 # 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"
+    "helpers/memenv/memenv.cc"
+    "helpers/memenv/memenv.h"
 )
 
 target_include_directories(leveldb
@@ -260,13 +282,35 @@ find_package(Threads REQUIRED)
 target_link_libraries(leveldb Threads::Threads)
 
 add_executable(leveldbutil
-  "${PROJECT_SOURCE_DIR}/db/leveldbutil.cc"
+  "db/leveldbutil.cc"
 )
 target_link_libraries(leveldbutil leveldb)
 
 if(LEVELDB_BUILD_TESTS)
   enable_testing()
 
+  # Prevent overriding the parent project's compiler/linker settings on Windows.
+  set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+  set(install_gtest OFF)
+  set(install_gmock OFF)
+  set(build_gmock ON)
+
+  # This project is tested using GoogleTest.
+  add_subdirectory("third_party/googletest")
+
+  # This project uses Google benchmark for benchmarking.
+  set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
+  set(BENCHMARK_ENABLE_EXCEPTIONS OFF CACHE BOOL "" FORCE)
+  add_subdirectory("third_party/benchmark")
+
+  # GoogleTest triggers a missing field initializers warning.
+  if(LEVELDB_HAVE_NO_MISSING_FIELD_INITIALIZERS)
+    set_property(TARGET gtest
+        APPEND PROPERTY COMPILE_OPTIONS -Wno-missing-field-initializers)
+    set_property(TARGET gmock
+        APPEND PROPERTY COMPILE_OPTIONS -Wno-missing-field-initializers)
+  endif(LEVELDB_HAVE_NO_MISSING_FIELD_INITIALIZERS)
+
   function(leveldb_test test_file)
     get_filename_component(test_target_name "${test_file}" NAME_WE)
 
@@ -274,14 +318,12 @@ if(LEVELDB_BUILD_TESTS)
     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"
+        "util/testutil.cc"
+        "util/testutil.h"
 
         "${test_file}"
     )
-    target_link_libraries("${test_target_name}" leveldb)
+    target_link_libraries("${test_target_name}" leveldb gmock gtest benchmark)
     target_compile_definitions("${test_target_name}"
       PRIVATE
         ${LEVELDB_PLATFORM_NAME}=1
@@ -296,49 +338,49 @@ if(LEVELDB_BUILD_TESTS)
     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("db/c_test.c")
+  leveldb_test("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}/issues/issue320_test.cc")
+  leveldb_test("issues/issue178_test.cc")
+  leveldb_test("issues/issue200_test.cc")
+  leveldb_test("issues/issue320_test.cc")
 
-  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")
+  leveldb_test("util/env_test.cc")
+  leveldb_test("util/status_test.cc")
+  leveldb_test("util/no_destructor_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")
-    leveldb_test("${PROJECT_SOURCE_DIR}/util/logging_test.cc")
+    leveldb_test("db/autocompact_test.cc")
+    leveldb_test("db/corruption_test.cc")
+    leveldb_test("db/db_test.cc")
+    leveldb_test("db/dbformat_test.cc")
+    leveldb_test("db/filename_test.cc")
+    leveldb_test("db/log_test.cc")
+    leveldb_test("db/recovery_test.cc")
+    leveldb_test("db/skiplist_test.cc")
+    leveldb_test("db/version_edit_test.cc")
+    leveldb_test("db/version_set_test.cc")
+    leveldb_test("db/write_batch_test.cc")
+
+    leveldb_test("helpers/memenv/memenv_test.cc")
+
+    leveldb_test("table/filter_block_test.cc")
+    leveldb_test("table/table_test.cc")
+
+    leveldb_test("util/arena_test.cc")
+    leveldb_test("util/bloom_test.cc")
+    leveldb_test("util/cache_test.cc")
+    leveldb_test("util/coding_test.cc")
+    leveldb_test("util/crc32c_test.cc")
+    leveldb_test("util/hash_test.cc")
+    leveldb_test("util/logging_test.cc")
 
     # TODO(costan): This test also uses
-    #               "${PROJECT_SOURCE_DIR}/util/env_{posix|windows}_test_helper.h"
+    #               "util/env_{posix|windows}_test_helper.h"
     if (WIN32)
-      leveldb_test("${PROJECT_SOURCE_DIR}/util/env_windows_test.cc")
+      leveldb_test("util/env_windows_test.cc")
     else (WIN32)
-      leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc")
+      leveldb_test("util/env_posix_test.cc")
     endif (WIN32)
   endif(NOT BUILD_SHARED_LIBS)
 endif(LEVELDB_BUILD_TESTS)
@@ -351,16 +393,14 @@ if(LEVELDB_BUILD_BENCHMARKS)
     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"
+        "util/histogram.cc"
+        "util/histogram.h"
+        "util/testutil.cc"
+        "util/testutil.h"
 
         "${bench_file}"
     )
-    target_link_libraries("${bench_target_name}" leveldb)
+    target_link_libraries("${bench_target_name}" leveldb gmock gtest)
     target_compile_definitions("${bench_target_name}"
       PRIVATE
         ${LEVELDB_PLATFORM_NAME}=1
@@ -374,12 +414,12 @@ if(LEVELDB_BUILD_BENCHMARKS)
   endfunction(leveldb_benchmark)
 
   if(NOT BUILD_SHARED_LIBS)
-    leveldb_benchmark("${PROJECT_SOURCE_DIR}/db/db_bench.cc")
+    leveldb_benchmark("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("benchmarks/db_bench_sqlite3.cc")
     target_link_libraries(db_bench_sqlite3 sqlite3)
   endif(HAVE_SQLITE3)
 
@@ -399,13 +439,12 @@ 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("benchmarks/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}
@@ -414,38 +453,43 @@ if(LEVELDB_INSTALL)
   )
   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}/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"
-      "${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
+      "${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}/export.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"
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/leveldb"
   )
 
   include(CMakePackageConfigHelpers)
+  configure_package_config_file(
+    "cmake/${PROJECT_NAME}Config.cmake.in"
+    "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake"
+    INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
+  )
   write_basic_package_version_file(
-      "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake"
-      COMPATIBILITY SameMajorVersion
+    "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake"
+    COMPATIBILITY SameMajorVersion
   )
   install(
     EXPORT leveldbTargets
     NAMESPACE leveldb::
-    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
   )
   install(
     FILES
-      "${PROJECT_SOURCE_DIR}/cmake/leveldbConfig.cmake"
-      "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake"
-    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb"
+      "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake"
+      "${PROJECT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
   )
 endif(LEVELDB_INSTALL)
index a74572a..7ede021 100644 (file)
@@ -32,5 +32,5 @@ the CLA.
 ## Writing Code ##
 
 If your contribution contains code, please make sure that it follows
-[the style guide](http://google.github.io/styleguide/cppguide.html).
+[the style guide](https://google.github.io/styleguide/cppguide.html).
 Otherwise we will have to ask you to make changes, and that's no fun for anyone.
index 0b660ae..81144dd 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,7 +14,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://google.github.io/snappy/).
+  * Data is automatically compressed using the [Snappy compression library](https://google.github.io/snappy/).
   * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.
 
 # Documentation
@@ -27,6 +27,12 @@ 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.
 
+# Getting the Source
+
+```bash
+git clone --recurse-submodules https://github.com/google/leveldb.git
+```
+
 # Building
 
 This project supports [CMake](https://cmake.org/) out of the box.
@@ -189,37 +195,37 @@ uncompressed blocks in memory, the read performance improves again:
 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
+The public interface is in include/leveldb/*.h.  Callers should not include or
 rely on the details of any other header files in this package.  Those
 internal APIs may be changed without warning.
 
 Guide to header files:
 
-* **include/db.h**: Main interface to the DB: Start here
+* **include/leveldb/db.h**: Main interface to the DB: Start here.
 
-* **include/options.h**: Control over the behavior of an entire database,
+* **include/leveldb/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/leveldb/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.)
+want custom ordering (e.g. to handle different character encodings, etc.).
 
-* **include/iterator.h**: Interface for iterating over data. You can get
+* **include/leveldb/iterator.h**: Interface for iterating over data. You can get
 an iterator from a DB object.
 
-* **include/write_batch.h**: Interface for atomically applying multiple
+* **include/leveldb/write_batch.h**: Interface for atomically applying multiple
 updates to a database.
 
-* **include/slice.h**: A simple module for maintaining a pointer and a
+* **include/leveldb/slice.h**: A simple module for maintaining a pointer and a
 length into some other byte array.
 
-* **include/status.h**: Status is returned from many of the public interfaces
+* **include/leveldb/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/leveldb/env.h**:
 Abstraction of the OS environment.  A posix implementation of this interface is
-in util/env_posix.cc
+in util/env_posix.cc.
 
-* **include/table.h, include/table_builder.h**: Lower-level modules that most
-clients probably won't use directly
+* **include/leveldb/table.h, include/leveldb/table_builder.h**: Lower-level modules that most
+clients probably won't use directly.
similarity index 75%
rename from db/db_bench.cc
rename to benchmarks/db_bench.cc
index 3090b43..429a61a 100644 (file)
@@ -2,11 +2,14 @@
 // 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 <stdio.h>
-#include <stdlib.h>
 #include <sys/types.h>
 
+#include <atomic>
+#include <cstdio>
+#include <cstdlib>
+
 #include "leveldb/cache.h"
+#include "leveldb/comparator.h"
 #include "leveldb/db.h"
 #include "leveldb/env.h"
 #include "leveldb/filter_policy.h"
@@ -33,6 +36,7 @@
 //      readmissing   -- read N missing keys in random order
 //      readhot       -- read N times in random order from 1% section of DB
 //      seekrandom    -- N random seeks
+//      seekordered   -- N ordered seeks
 //      open          -- cost of opening a DB
 //      crc32c        -- repeated crc32c of 4K of data
 //   Meta operations:
@@ -77,6 +81,9 @@ static double FLAGS_compression_ratio = 0.5;
 // Print histogram of operation timings
 static bool FLAGS_histogram = false;
 
+// Count the number of string comparisons performed
+static bool FLAGS_comparisons = false;
+
 // Number of bytes to buffer in memtable before compacting
 // (initialized to default value by "main")
 static int FLAGS_write_buffer_size = 0;
@@ -100,6 +107,9 @@ static int FLAGS_open_files = 0;
 // Negative means use default settings.
 static int FLAGS_bloom_bits = -1;
 
+// Common key prefix length.
+static int FLAGS_key_prefix = 0;
+
 // If true, do not destroy the existing database.  If you set this
 // flag and also specify a benchmark that wants a fresh database, that
 // benchmark will fail.
@@ -116,6 +126,33 @@ namespace leveldb {
 namespace {
 leveldb::Env* g_env = nullptr;
 
+class CountComparator : public Comparator {
+ public:
+  CountComparator(const Comparator* wrapped) : wrapped_(wrapped) {}
+  ~CountComparator() override {}
+  int Compare(const Slice& a, const Slice& b) const override {
+    count_.fetch_add(1, std::memory_order_relaxed);
+    return wrapped_->Compare(a, b);
+  }
+  const char* Name() const override { return wrapped_->Name(); }
+  void FindShortestSeparator(std::string* start,
+                             const Slice& limit) const override {
+    wrapped_->FindShortestSeparator(start, limit);
+  }
+
+  void FindShortSuccessor(std::string* key) const override {
+    return wrapped_->FindShortSuccessor(key);
+  }
+
+  size_t comparisons() const { return count_.load(std::memory_order_relaxed); }
+
+  void reset() { count_.store(0, std::memory_order_relaxed); }
+
+ private:
+  mutable std::atomic<size_t> count_{0};
+  const Comparator* const wrapped_;
+};
+
 // Helper for quickly generating random data.
 class RandomGenerator {
  private:
@@ -148,6 +185,26 @@ class RandomGenerator {
   }
 };
 
+class KeyBuffer {
+ public:
+  KeyBuffer() {
+    assert(FLAGS_key_prefix < sizeof(buffer_));
+    memset(buffer_, 'a', FLAGS_key_prefix);
+  }
+  KeyBuffer& operator=(KeyBuffer& other) = delete;
+  KeyBuffer(KeyBuffer& other) = delete;
+
+  void Set(int k) {
+    std::snprintf(buffer_ + FLAGS_key_prefix,
+                  sizeof(buffer_) - FLAGS_key_prefix, "%016d", k);
+  }
+
+  Slice slice() const { return Slice(buffer_, FLAGS_key_prefix + 16); }
+
+ private:
+  char buffer_[1024];
+};
+
 #if defined(__linux)
 static Slice TrimSpace(Slice s) {
   size_t start = 0;
@@ -187,14 +244,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) {
@@ -222,8 +277,8 @@ class Stats {
       double micros = now - last_op_finish_;
       hist_.Add(micros);
       if (micros > 20000) {
-        fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
-        fflush(stderr);
+        std::fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
+        std::fflush(stderr);
       }
       last_op_finish_ = now;
     }
@@ -244,8 +299,8 @@ class Stats {
         next_report_ += 50000;
       else
         next_report_ += 100000;
-      fprintf(stderr, "... finished %d ops%30s\r", done_, "");
-      fflush(stderr);
+      std::fprintf(stderr, "... finished %d ops%30s\r", done_, "");
+      std::fflush(stderr);
     }
   }
 
@@ -262,18 +317,20 @@ class Stats {
       // elapsed times.
       double elapsed = (finish_ - start_) * 1e-6;
       char rate[100];
-      snprintf(rate, sizeof(rate), "%6.1f MB/s",
-               (bytes_ / 1048576.0) / elapsed);
+      std::snprintf(rate, sizeof(rate), "%6.1f MB/s",
+                    (bytes_ / 1048576.0) / elapsed);
       extra = rate;
     }
     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());
+    std::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());
+      std::fprintf(stdout, "Microseconds per op:\n%s\n",
+                   hist_.ToString().c_str());
     }
-    fflush(stdout);
+    std::fflush(stdout);
   }
 };
 
@@ -304,7 +361,7 @@ struct ThreadState {
   Stats stats;
   SharedState* shared;
 
-  ThreadState(int index) : tid(index), rand(1000 + index), shared(nullptr) {}
+  ThreadState(int index, int seed) : tid(index), rand(seed), shared(nullptr) {}
 };
 
 }  // namespace
@@ -320,55 +377,61 @@ class Benchmark {
   WriteOptions write_options_;
   int reads_;
   int heap_counter_;
+  CountComparator count_comparator_;
+  int total_thread_count_;
 
   void PrintHeader() {
-    const int kKeySize = 16;
+    const int kKeySize = 16 + FLAGS_key_prefix;
     PrintEnvironment();
-    fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
-    fprintf(stdout, "Values:     %d bytes each (%d bytes after compression)\n",
-            FLAGS_value_size,
-            static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
-    fprintf(stdout, "Entries:    %d\n", num_);
-    fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
-            ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
-             1048576.0));
-    fprintf(stdout, "FileSize:   %.1f MB (estimated)\n",
-            (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
-             1048576.0));
+    std::fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
+    std::fprintf(
+        stdout, "Values:     %d bytes each (%d bytes after compression)\n",
+        FLAGS_value_size,
+        static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
+    std::fprintf(stdout, "Entries:    %d\n", num_);
+    std::fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
+                 ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
+                  1048576.0));
+    std::fprintf(
+        stdout, "FileSize:   %.1f MB (estimated)\n",
+        (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
+         1048576.0));
     PrintWarnings();
-    fprintf(stdout, "------------------------------------------------\n");
+    std::fprintf(stdout, "------------------------------------------------\n");
   }
 
   void PrintWarnings() {
 #if defined(__GNUC__) && !defined(__OPTIMIZE__)
-    fprintf(
+    std::fprintf(
         stdout,
         "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
 #endif
 #ifndef NDEBUG
-    fprintf(stdout,
-            "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
+    std::fprintf(
+        stdout,
+        "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
 #endif
 
     // See if snappy is working by attempting to compress a compressible string
     const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
     std::string compressed;
     if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {
-      fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
+      std::fprintf(stdout, "WARNING: Snappy compression is not enabled\n");
     } else if (compressed.size() >= sizeof(text)) {
-      fprintf(stdout, "WARNING: Snappy compression is not effective\n");
+      std::fprintf(stdout, "WARNING: Snappy compression is not effective\n");
     }
   }
 
   void PrintEnvironment() {
-    fprintf(stderr, "LevelDB:    version %d.%d\n", kMajorVersion,
-            kMinorVersion);
+    std::fprintf(stderr, "LevelDB:    version %d.%d\n", kMajorVersion,
+                 kMinorVersion);
 
 #if defined(__linux)
     time_t now = time(nullptr);
-    fprintf(stderr, "Date:       %s", ctime(&now));  // ctime() adds newline
+    std::fprintf(stderr, "Date:       %s",
+                 ctime(&now));  // ctime() adds newline
 
-    FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
+    FILE* cpuinfo = std::fopen("/proc/cpuinfo", "r");
     if (cpuinfo != nullptr) {
       char line[1000];
       int num_cpus = 0;
@@ -388,9 +451,9 @@ class Benchmark {
           cache_size = val.ToString();
         }
       }
-      fclose(cpuinfo);
-      fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
-      fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
+      std::fclose(cpuinfo);
+      std::fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
+      std::fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
     }
 #endif
   }
@@ -406,12 +469,14 @@ class Benchmark {
         value_size_(FLAGS_value_size),
         entries_per_batch_(1),
         reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
-        heap_counter_(0) {
+        heap_counter_(0),
+        count_comparator_(BytewiseComparator()),
+        total_thread_count_(0) {
     std::vector<std::string> files;
     g_env->GetChildren(FLAGS_db, &files);
     for (size_t i = 0; i < files.size(); i++) {
       if (Slice(files[i]).starts_with("heap-")) {
-        g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);
+        g_env->RemoveFile(std::string(FLAGS_db) + "/" + files[i]);
       }
     }
     if (!FLAGS_use_existing_db) {
@@ -489,6 +554,8 @@ class Benchmark {
         method = &Benchmark::ReadMissing;
       } else if (name == Slice("seekrandom")) {
         method = &Benchmark::SeekRandom;
+      } else if (name == Slice("seekordered")) {
+        method = &Benchmark::SeekOrdered;
       } else if (name == Slice("readhot")) {
         method = &Benchmark::ReadHot;
       } else if (name == Slice("readrandomsmall")) {
@@ -517,14 +584,15 @@ class Benchmark {
         PrintStats("leveldb.sstables");
       } else {
         if (!name.empty()) {  // No error message for empty name
-          fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
+          std::fprintf(stderr, "unknown benchmark '%s'\n",
+                       name.ToString().c_str());
         }
       }
 
       if (fresh_db) {
         if (FLAGS_use_existing_db) {
-          fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
-                  name.ToString().c_str());
+          std::fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
+                       name.ToString().c_str());
           method = nullptr;
         } else {
           delete db_;
@@ -585,7 +653,11 @@ class Benchmark {
       arg[i].bm = this;
       arg[i].method = method;
       arg[i].shared = &shared;
-      arg[i].thread = new ThreadState(i);
+      ++total_thread_count_;
+      // Seed the thread's random state deterministically based upon thread
+      // creation across all benchmarks. This ensures that the seeds are unique
+      // but reproducible when rerunning the same set of benchmarks.
+      arg[i].thread = new ThreadState(i, /*seed=*/1000 + total_thread_count_);
       arg[i].thread->shared = &shared;
       g_env->StartThread(ThreadBody, &arg[i]);
     }
@@ -606,6 +678,11 @@ class Benchmark {
       arg[0].thread->stats.Merge(arg[i].thread->stats);
     }
     arg[0].thread->stats.Report(name);
+    if (FLAGS_comparisons) {
+      fprintf(stdout, "Comparisons: %zu\n", count_comparator_.comparisons());
+      count_comparator_.reset();
+      fflush(stdout);
+    }
 
     for (int i = 0; i < n; i++) {
       delete arg[i].thread;
@@ -626,7 +703,7 @@ class Benchmark {
       bytes += size;
     }
     // Print so result is not dead
-    fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
+    std::fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
 
     thread->stats.AddBytes(bytes);
     thread->stats.AddMessage(label);
@@ -650,8 +727,8 @@ class Benchmark {
       thread->stats.AddMessage("(snappy failure)");
     } else {
       char buf[100];
-      snprintf(buf, sizeof(buf), "(output: %.1f%%)",
-               (produced * 100.0) / bytes);
+      std::snprintf(buf, sizeof(buf), "(output: %.1f%%)",
+                    (produced * 100.0) / bytes);
       thread->stats.AddMessage(buf);
       thread->stats.AddBytes(bytes);
     }
@@ -688,13 +765,16 @@ class Benchmark {
     options.write_buffer_size = FLAGS_write_buffer_size;
     options.max_file_size = FLAGS_max_file_size;
     options.block_size = FLAGS_block_size;
+    if (FLAGS_comparisons) {
+      options.comparator = &count_comparator_;
+    }
     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());
-      exit(1);
+      std::fprintf(stderr, "open error: %s\n", s.ToString().c_str());
+      std::exit(1);
     }
   }
 
@@ -713,7 +793,7 @@ class Benchmark {
   void DoWrite(ThreadState* thread, bool seq) {
     if (num_ != FLAGS_num) {
       char msg[100];
-      snprintf(msg, sizeof(msg), "(%d ops)", num_);
+      std::snprintf(msg, sizeof(msg), "(%d ops)", num_);
       thread->stats.AddMessage(msg);
     }
 
@@ -721,20 +801,20 @@ class Benchmark {
     WriteBatch batch;
     Status s;
     int64_t bytes = 0;
+    KeyBuffer key;
     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);
-        char key[100];
-        snprintf(key, sizeof(key), "%016d", k);
-        batch.Put(key, gen.Generate(value_size_));
-        bytes += value_size_ + strlen(key);
+        const int k = seq ? i + j : thread->rand.Uniform(FLAGS_num);
+        key.Set(k);
+        batch.Put(key.slice(), gen.Generate(value_size_));
+        bytes += value_size_ + key.slice().size();
         thread->stats.FinishedSingleOp();
       }
       s = db_->Write(write_options_, &batch);
       if (!s.ok()) {
-        fprintf(stderr, "put error: %s\n", s.ToString().c_str());
-        exit(1);
+        std::fprintf(stderr, "put error: %s\n", s.ToString().c_str());
+        std::exit(1);
       }
     }
     thread->stats.AddBytes(bytes);
@@ -770,28 +850,29 @@ class Benchmark {
     ReadOptions options;
     std::string value;
     int found = 0;
+    KeyBuffer key;
     for (int i = 0; i < reads_; i++) {
-      char key[100];
-      const int k = thread->rand.Next() % FLAGS_num;
-      snprintf(key, sizeof(key), "%016d", k);
-      if (db_->Get(options, key, &value).ok()) {
+      const int k = thread->rand.Uniform(FLAGS_num);
+      key.Set(k);
+      if (db_->Get(options, key.slice(), &value).ok()) {
         found++;
       }
       thread->stats.FinishedSingleOp();
     }
     char msg[100];
-    snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
+    std::snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
     thread->stats.AddMessage(msg);
   }
 
   void ReadMissing(ThreadState* thread) {
     ReadOptions options;
     std::string value;
+    KeyBuffer key;
     for (int i = 0; i < reads_; i++) {
-      char key[100];
-      const int k = thread->rand.Next() % FLAGS_num;
-      snprintf(key, sizeof(key), "%016d.", k);
-      db_->Get(options, key, &value);
+      const int k = thread->rand.Uniform(FLAGS_num);
+      key.Set(k);
+      Slice s = Slice(key.slice().data(), key.slice().size() - 1);
+      db_->Get(options, s, &value);
       thread->stats.FinishedSingleOp();
     }
   }
@@ -800,11 +881,11 @@ class Benchmark {
     ReadOptions options;
     std::string value;
     const int range = (FLAGS_num + 99) / 100;
+    KeyBuffer key;
     for (int i = 0; i < reads_; i++) {
-      char key[100];
-      const int k = thread->rand.Next() % range;
-      snprintf(key, sizeof(key), "%016d", k);
-      db_->Get(options, key, &value);
+      const int k = thread->rand.Uniform(range);
+      key.Set(k);
+      db_->Get(options, key.slice(), &value);
       thread->stats.FinishedSingleOp();
     }
   }
@@ -812,13 +893,13 @@ class Benchmark {
   void SeekRandom(ThreadState* thread) {
     ReadOptions options;
     int found = 0;
+    KeyBuffer key;
     for (int i = 0; i < reads_; i++) {
       Iterator* iter = db_->NewIterator(options);
-      char key[100];
-      const int k = thread->rand.Next() % FLAGS_num;
-      snprintf(key, sizeof(key), "%016d", k);
-      iter->Seek(key);
-      if (iter->Valid() && iter->key() == key) found++;
+      const int k = thread->rand.Uniform(FLAGS_num);
+      key.Set(k);
+      iter->Seek(key.slice());
+      if (iter->Valid() && iter->key() == key.slice()) found++;
       delete iter;
       thread->stats.FinishedSingleOp();
     }
@@ -827,23 +908,42 @@ class Benchmark {
     thread->stats.AddMessage(msg);
   }
 
+  void SeekOrdered(ThreadState* thread) {
+    ReadOptions options;
+    Iterator* iter = db_->NewIterator(options);
+    int found = 0;
+    int k = 0;
+    KeyBuffer key;
+    for (int i = 0; i < reads_; i++) {
+      k = (k + (thread->rand.Uniform(100))) % FLAGS_num;
+      key.Set(k);
+      iter->Seek(key.slice());
+      if (iter->Valid() && iter->key() == key.slice()) found++;
+      thread->stats.FinishedSingleOp();
+    }
+    delete iter;
+    char msg[100];
+    std::snprintf(msg, sizeof(msg), "(%d of %d found)", found, num_);
+    thread->stats.AddMessage(msg);
+  }
+
   void DoDelete(ThreadState* thread, bool seq) {
     RandomGenerator gen;
     WriteBatch batch;
     Status s;
+    KeyBuffer key;
     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);
-        char key[100];
-        snprintf(key, sizeof(key), "%016d", k);
-        batch.Delete(key);
+        const int k = seq ? i + j : (thread->rand.Uniform(FLAGS_num));
+        key.Set(k);
+        batch.Delete(key.slice());
         thread->stats.FinishedSingleOp();
       }
       s = db_->Write(write_options_, &batch);
       if (!s.ok()) {
-        fprintf(stderr, "del error: %s\n", s.ToString().c_str());
-        exit(1);
+        std::fprintf(stderr, "del error: %s\n", s.ToString().c_str());
+        std::exit(1);
       }
     }
   }
@@ -858,6 +958,7 @@ class Benchmark {
     } else {
       // Special thread that keeps writing until other threads are done.
       RandomGenerator gen;
+      KeyBuffer key;
       while (true) {
         {
           MutexLock l(&thread->shared->mu);
@@ -867,13 +968,13 @@ class Benchmark {
           }
         }
 
-        const int k = thread->rand.Next() % FLAGS_num;
-        char key[100];
-        snprintf(key, sizeof(key), "%016d", k);
-        Status s = db_->Put(write_options_, key, gen.Generate(value_size_));
+        const int k = thread->rand.Uniform(FLAGS_num);
+        key.Set(k);
+        Status s =
+            db_->Put(write_options_, key.slice(), gen.Generate(value_size_));
         if (!s.ok()) {
-          fprintf(stderr, "put error: %s\n", s.ToString().c_str());
-          exit(1);
+          std::fprintf(stderr, "put error: %s\n", s.ToString().c_str());
+          std::exit(1);
         }
       }
 
@@ -889,7 +990,7 @@ class Benchmark {
     if (!db_->GetProperty(key, &stats)) {
       stats = "(failed)";
     }
-    fprintf(stdout, "\n%s\n", stats.c_str());
+    std::fprintf(stdout, "\n%s\n", stats.c_str());
   }
 
   static void WriteToFile(void* arg, const char* buf, int n) {
@@ -898,18 +999,19 @@ class Benchmark {
 
   void HeapProfile() {
     char fname[100];
-    snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);
+    std::snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db,
+                  ++heap_counter_);
     WritableFile* file;
     Status s = g_env->NewWritableFile(fname, &file);
     if (!s.ok()) {
-      fprintf(stderr, "%s\n", s.ToString().c_str());
+      std::fprintf(stderr, "%s\n", s.ToString().c_str());
       return;
     }
     bool ok = port::GetHeapProfile(WriteToFile, file);
     delete file;
     if (!ok) {
-      fprintf(stderr, "heap profiling not supported\n");
-      g_env->DeleteFile(fname);
+      std::fprintf(stderr, "heap profiling not supported\n");
+      g_env->RemoveFile(fname);
     }
   }
 };
@@ -934,6 +1036,9 @@ int main(int argc, char** argv) {
     } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
                (n == 0 || n == 1)) {
       FLAGS_histogram = n;
+    } else if (sscanf(argv[i], "--comparisons=%d%c", &n, &junk) == 1 &&
+               (n == 0 || n == 1)) {
+      FLAGS_comparisons = n;
     } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&
                (n == 0 || n == 1)) {
       FLAGS_use_existing_db = n;
@@ -954,6 +1059,8 @@ int main(int argc, char** argv) {
       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], "--key_prefix=%d%c", &n, &junk) == 1) {
+      FLAGS_key_prefix = 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) {
@@ -963,8 +1070,8 @@ int main(int argc, char** argv) {
     } else if (strncmp(argv[i], "--db=", 5) == 0) {
       FLAGS_db = argv[i] + 5;
     } else {
-      fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
-      exit(1);
+      std::fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
+      std::exit(1);
     }
   }
 
similarity index 87%
rename from doc/bench/db_bench_sqlite3.cc
rename to benchmarks/db_bench_sqlite3.cc
index f183f4f..c9be652 100644 (file)
@@ -3,8 +3,9 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include <sqlite3.h>
-#include <stdio.h>
-#include <stdlib.h>
+
+#include <cstdio>
+#include <cstdlib>
 
 #include "util/histogram.h"
 #include "util/random.h"
@@ -69,6 +70,9 @@ static int FLAGS_num_pages = 4096;
 // benchmark will fail.
 static bool FLAGS_use_existing_db = false;
 
+// If true, the SQLite table has ROWIDs.
+static bool FLAGS_use_rowids = false;
+
 // If true, we allow batch writes to occur
 static bool FLAGS_transaction = true;
 
@@ -80,23 +84,23 @@ static const char* FLAGS_db = nullptr;
 
 inline static void ExecErrorCheck(int status, char* err_msg) {
   if (status != SQLITE_OK) {
-    fprintf(stderr, "SQL error: %s\n", err_msg);
+    std::fprintf(stderr, "SQL error: %s\n", err_msg);
     sqlite3_free(err_msg);
-    exit(1);
+    std::exit(1);
   }
 }
 
 inline static void StepErrorCheck(int status) {
   if (status != SQLITE_DONE) {
-    fprintf(stderr, "SQL step error: status = %d\n", status);
-    exit(1);
+    std::fprintf(stderr, "SQL step error: status = %d\n", status);
+    std::exit(1);
   }
 }
 
 inline static void ErrorCheck(int status) {
   if (status != SQLITE_OK) {
-    fprintf(stderr, "sqlite3 error: status = %d\n", status);
-    exit(1);
+    std::fprintf(stderr, "sqlite3 error: status = %d\n", status);
+    std::exit(1);
   }
 }
 
@@ -178,36 +182,38 @@ class Benchmark {
   void PrintHeader() {
     const int kKeySize = 16;
     PrintEnvironment();
-    fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
-    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<int64_t>(kKeySize + FLAGS_value_size) * num_) /
-             1048576.0));
+    std::fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
+    std::fprintf(stdout, "Values:     %d bytes each\n", FLAGS_value_size);
+    std::fprintf(stdout, "Entries:    %d\n", num_);
+    std::fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
+                 ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
+                  1048576.0));
     PrintWarnings();
-    fprintf(stdout, "------------------------------------------------\n");
+    std::fprintf(stdout, "------------------------------------------------\n");
   }
 
   void PrintWarnings() {
 #if defined(__GNUC__) && !defined(__OPTIMIZE__)
-    fprintf(
+    std::fprintf(
         stdout,
         "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
 #endif
 #ifndef NDEBUG
-    fprintf(stdout,
-            "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
+    std::fprintf(
+        stdout,
+        "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
 #endif
   }
 
   void PrintEnvironment() {
-    fprintf(stderr, "SQLite:     version %s\n", SQLITE_VERSION);
+    std::fprintf(stderr, "SQLite:     version %s\n", SQLITE_VERSION);
 
 #if defined(__linux)
     time_t now = time(nullptr);
-    fprintf(stderr, "Date:       %s", ctime(&now));  // ctime() adds newline
+    std::fprintf(stderr, "Date:       %s",
+                 ctime(&now));  // ctime() adds newline
 
-    FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
+    FILE* cpuinfo = std::fopen("/proc/cpuinfo", "r");
     if (cpuinfo != nullptr) {
       char line[1000];
       int num_cpus = 0;
@@ -227,9 +233,9 @@ class Benchmark {
           cache_size = val.ToString();
         }
       }
-      fclose(cpuinfo);
-      fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
-      fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
+      std::fclose(cpuinfo);
+      std::fprintf(stderr, "CPU:        %d * %s\n", num_cpus, cpu_type.c_str());
+      std::fprintf(stderr, "CPUCache:   %s\n", cache_size.c_str());
     }
 #endif
   }
@@ -250,8 +256,8 @@ class Benchmark {
       double micros = (now - last_op_finish_) * 1e6;
       hist_.Add(micros);
       if (micros > 20000) {
-        fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
-        fflush(stderr);
+        std::fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
+        std::fflush(stderr);
       }
       last_op_finish_ = now;
     }
@@ -272,8 +278,8 @@ class Benchmark {
         next_report_ += 50000;
       else
         next_report_ += 100000;
-      fprintf(stderr, "... finished %d ops%30s\r", done_, "");
-      fflush(stderr);
+      std::fprintf(stderr, "... finished %d ops%30s\r", done_, "");
+      std::fflush(stderr);
     }
   }
 
@@ -286,8 +292,8 @@ class Benchmark {
 
     if (bytes_ > 0) {
       char rate[100];
-      snprintf(rate, sizeof(rate), "%6.1f MB/s",
-               (bytes_ / 1048576.0) / (finish - start_));
+      std::snprintf(rate, sizeof(rate), "%6.1f MB/s",
+                    (bytes_ / 1048576.0) / (finish - start_));
       if (!message_.empty()) {
         message_ = std::string(rate) + " " + message_;
       } else {
@@ -295,13 +301,14 @@ class Benchmark {
       }
     }
 
-    fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
-            (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "),
-            message_.c_str());
+    std::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());
+      std::fprintf(stdout, "Microseconds per op:\n%s\n",
+                   hist_.ToString().c_str());
     }
-    fflush(stdout);
+    std::fflush(stdout);
   }
 
  public:
@@ -325,7 +332,7 @@ class Benchmark {
           std::string file_name(test_dir);
           file_name += "/";
           file_name += files[i];
-          Env::Default()->DeleteFile(file_name.c_str());
+          Env::Default()->RemoveFile(file_name.c_str());
         }
       }
     }
@@ -401,7 +408,8 @@ class Benchmark {
       } else {
         known = false;
         if (name != Slice()) {  // No error message for empty name
-          fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
+          std::fprintf(stderr, "unknown benchmark '%s'\n",
+                       name.ToString().c_str());
         }
       }
       if (known) {
@@ -421,26 +429,26 @@ 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_);
+    std::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_));
-      exit(1);
+      std::fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_));
+      std::exit(1);
     }
 
     // Change SQLite cache size
     char cache_size[100];
-    snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
-             FLAGS_num_pages);
+    std::snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
+                  FLAGS_num_pages);
     status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg);
     ExecErrorCheck(status, err_msg);
 
     // FLAGS_page_size is defaulted to 1024
     if (FLAGS_page_size != 1024) {
       char page_size[100];
-      snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
-               FLAGS_page_size);
+      std::snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
+                    FLAGS_page_size);
       status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg);
       ExecErrorCheck(status, err_msg);
     }
@@ -462,6 +470,7 @@ class Benchmark {
     std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE";
     std::string create_stmt =
         "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
+    if (!FLAGS_use_rowids) create_stmt += " WITHOUT ROWID";
     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++) {
@@ -487,7 +496,7 @@ class Benchmark {
 
     if (num_entries != num_) {
       char msg[100];
-      snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
+      std::snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
       message_ = msg;
     }
 
@@ -534,7 +543,7 @@ class Benchmark {
         const int k =
             (order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries);
         char key[100];
-        snprintf(key, sizeof(key), "%016d", k);
+        std::snprintf(key, sizeof(key), "%016d", k);
 
         // Bind KV values into replace_stmt
         status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC);
@@ -607,7 +616,7 @@ class Benchmark {
         // Create key value
         char key[100];
         int k = (order == SEQUENTIAL) ? i + j : (rand_.Next() % reads_);
-        snprintf(key, sizeof(key), "%016d", k);
+        std::snprintf(key, sizeof(key), "%016d", k);
 
         // Bind key value into read_stmt
         status = sqlite3_bind_blob(read_stmt, 1, key, 16, SQLITE_STATIC);
@@ -678,6 +687,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], "--use_rowids=%d%c", &n, &junk) == 1 &&
+               (n == 0 || n == 1)) {
+      FLAGS_use_rowids = 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) {
@@ -696,8 +708,8 @@ int main(int argc, char** argv) {
     } else if (strncmp(argv[i], "--db=", 5) == 0) {
       FLAGS_db = argv[i] + 5;
     } else {
-      fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
-      exit(1);
+      std::fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
+      std::exit(1);
     }
   }
 
similarity index 82%
rename from doc/bench/db_bench_tree_db.cc
rename to benchmarks/db_bench_tree_db.cc
index b2f6646..533600b 100644 (file)
@@ -3,8 +3,9 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include <kcpolydb.h>
-#include <stdio.h>
-#include <stdlib.h>
+
+#include <cstdio>
+#include <cstdlib>
 
 #include "util/histogram.h"
 #include "util/random.h"
@@ -74,7 +75,7 @@ static const char* FLAGS_db = nullptr;
 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());
+    std::fprintf(stderr, "synchronize error: %s\n", db_->error().name());
   }
 }
 
@@ -149,42 +150,47 @@ class Benchmark {
   void PrintHeader() {
     const int kKeySize = 16;
     PrintEnvironment();
-    fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
-    fprintf(stdout, "Values:     %d bytes each (%d bytes after compression)\n",
-            FLAGS_value_size,
-            static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
-    fprintf(stdout, "Entries:    %d\n", num_);
-    fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
-            ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
-             1048576.0));
-    fprintf(stdout, "FileSize:   %.1f MB (estimated)\n",
-            (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
-             1048576.0));
+    std::fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
+    std::fprintf(
+        stdout, "Values:     %d bytes each (%d bytes after compression)\n",
+        FLAGS_value_size,
+        static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
+    std::fprintf(stdout, "Entries:    %d\n", num_);
+    std::fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
+                 ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
+                  1048576.0));
+    std::fprintf(
+        stdout, "FileSize:   %.1f MB (estimated)\n",
+        (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
+         1048576.0));
     PrintWarnings();
-    fprintf(stdout, "------------------------------------------------\n");
+    std::fprintf(stdout, "------------------------------------------------\n");
   }
 
   void PrintWarnings() {
 #if defined(__GNUC__) && !defined(__OPTIMIZE__)
-    fprintf(
+    std::fprintf(
         stdout,
         "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
 #endif
 #ifndef NDEBUG
-    fprintf(stdout,
-            "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
+    std::fprintf(
+        stdout,
+        "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
 #endif
   }
 
   void PrintEnvironment() {
-    fprintf(stderr, "Kyoto Cabinet:    version %s, lib ver %d, lib rev %d\n",
-            kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
+    std::fprintf(
+        stderr, "Kyoto Cabinet:    version %s, lib ver %d, lib rev %d\n",
+        kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
 
 #if defined(__linux)
     time_t now = time(nullptr);
-    fprintf(stderr, "Date:           %s", ctime(&now));  // ctime() adds newline
+    std::fprintf(stderr, "Date:           %s",
+                 ctime(&now));  // ctime() adds newline
 
-    FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
+    FILE* cpuinfo = std::fopen("/proc/cpuinfo", "r");
     if (cpuinfo != nullptr) {
       char line[1000];
       int num_cpus = 0;
@@ -204,9 +210,10 @@ class Benchmark {
           cache_size = val.ToString();
         }
       }
-      fclose(cpuinfo);
-      fprintf(stderr, "CPU:            %d * %s\n", num_cpus, cpu_type.c_str());
-      fprintf(stderr, "CPUCache:       %s\n", cache_size.c_str());
+      std::fclose(cpuinfo);
+      std::fprintf(stderr, "CPU:            %d * %s\n", num_cpus,
+                   cpu_type.c_str());
+      std::fprintf(stderr, "CPUCache:       %s\n", cache_size.c_str());
     }
 #endif
   }
@@ -227,8 +234,8 @@ class Benchmark {
       double micros = (now - last_op_finish_) * 1e6;
       hist_.Add(micros);
       if (micros > 20000) {
-        fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
-        fflush(stderr);
+        std::fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
+        std::fflush(stderr);
       }
       last_op_finish_ = now;
     }
@@ -249,8 +256,8 @@ class Benchmark {
         next_report_ += 50000;
       else
         next_report_ += 100000;
-      fprintf(stderr, "... finished %d ops%30s\r", done_, "");
-      fflush(stderr);
+      std::fprintf(stderr, "... finished %d ops%30s\r", done_, "");
+      std::fflush(stderr);
     }
   }
 
@@ -263,8 +270,8 @@ class Benchmark {
 
     if (bytes_ > 0) {
       char rate[100];
-      snprintf(rate, sizeof(rate), "%6.1f MB/s",
-               (bytes_ / 1048576.0) / (finish - start_));
+      std::snprintf(rate, sizeof(rate), "%6.1f MB/s",
+                    (bytes_ / 1048576.0) / (finish - start_));
       if (!message_.empty()) {
         message_ = std::string(rate) + " " + message_;
       } else {
@@ -272,13 +279,14 @@ class Benchmark {
       }
     }
 
-    fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
-            (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "),
-            message_.c_str());
+    std::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());
+      std::fprintf(stdout, "Microseconds per op:\n%s\n",
+                   hist_.ToString().c_str());
     }
-    fflush(stdout);
+    std::fflush(stdout);
   }
 
  public:
@@ -301,7 +309,7 @@ class Benchmark {
           std::string file_name(test_dir);
           file_name += "/";
           file_name += files[i];
-          Env::Default()->DeleteFile(file_name.c_str());
+          Env::Default()->RemoveFile(file_name.c_str());
         }
       }
     }
@@ -309,7 +317,7 @@ class Benchmark {
 
   ~Benchmark() {
     if (!db_->close()) {
-      fprintf(stderr, "close error: %s\n", db_->error().name());
+      std::fprintf(stderr, "close error: %s\n", db_->error().name());
     }
   }
 
@@ -373,7 +381,8 @@ class Benchmark {
       } else {
         known = false;
         if (name != Slice()) {  // No error message for empty name
-          fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
+          std::fprintf(stderr, "unknown benchmark '%s'\n",
+                       name.ToString().c_str());
         }
       }
       if (known) {
@@ -392,8 +401,8 @@ 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_);
+    std::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 =
@@ -412,7 +421,7 @@ class Benchmark {
       open_options |= kyotocabinet::PolyDB::OAUTOSYNC;
     }
     if (!db_->open(file_name, open_options)) {
-      fprintf(stderr, "open error: %s\n", db_->error().name());
+      std::fprintf(stderr, "open error: %s\n", db_->error().name());
     }
   }
 
@@ -432,7 +441,7 @@ class Benchmark {
 
     if (num_entries != num_) {
       char msg[100];
-      snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
+      std::snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
       message_ = msg;
     }
 
@@ -440,11 +449,11 @@ class Benchmark {
     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);
+      std::snprintf(key, sizeof(key), "%016d", k);
       bytes_ += value_size + strlen(key);
       std::string cpp_key = key;
       if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) {
-        fprintf(stderr, "set error: %s\n", db_->error().name());
+        std::fprintf(stderr, "set error: %s\n", db_->error().name());
       }
       FinishedSingleOp();
     }
@@ -466,7 +475,7 @@ class Benchmark {
     for (int i = 0; i < reads_; i++) {
       char key[100];
       const int k = rand_.Next() % reads_;
-      snprintf(key, sizeof(key), "%016d", k);
+      std::snprintf(key, sizeof(key), "%016d", k);
       db_->get(key, &value);
       FinishedSingleOp();
     }
@@ -504,8 +513,8 @@ int main(int argc, char** argv) {
     } else if (strncmp(argv[i], "--db=", 5) == 0) {
       FLAGS_db = argv[i] + 5;
     } else {
-      fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
-      exit(1);
+      std::fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
+      std::exit(1);
     }
   }
 
diff --git a/cmake/leveldbConfig.cmake b/cmake/leveldbConfig.cmake
deleted file mode 100644 (file)
index eea6e5c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake")
diff --git a/cmake/leveldbConfig.cmake.in b/cmake/leveldbConfig.cmake.in
new file mode 100644 (file)
index 0000000..2572728
--- /dev/null
@@ -0,0 +1,9 @@
+# Copyright 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.
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake")
+
+check_required_components(leveldb)
\ No newline at end of file
index e6c97a0..3b7241b 100644 (file)
@@ -2,24 +2,24 @@
 // 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 "gtest/gtest.h"
 #include "db/db_impl.h"
 #include "leveldb/cache.h"
 #include "leveldb/db.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
 
-class AutoCompactTest {
+class AutoCompactTest : public testing::Test {
  public:
   AutoCompactTest() {
-    dbname_ = test::TmpDir() + "/autocompact_test";
+    dbname_ = testing::TempDir() + "autocompact_test";
     tiny_cache_ = NewLRUCache(100);
     options_.block_cache = tiny_cache_;
     DestroyDB(dbname_, options_);
     options_.create_if_missing = true;
     options_.compression = kNoCompression;
-    ASSERT_OK(DB::Open(options_, dbname_, &db_));
+    EXPECT_LEVELDB_OK(DB::Open(options_, dbname_, &db_));
   }
 
   ~AutoCompactTest() {
@@ -30,7 +30,7 @@ class AutoCompactTest {
 
   std::string Key(int i) {
     char buf[100];
-    snprintf(buf, sizeof(buf), "key%06d", i);
+    std::snprintf(buf, sizeof(buf), "key%06d", i);
     return std::string(buf);
   }
 
@@ -62,15 +62,15 @@ void AutoCompactTest::DoReads(int n) {
 
   // Fill database
   for (int i = 0; i < kCount; i++) {
-    ASSERT_OK(db_->Put(WriteOptions(), Key(i), value));
+    ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), Key(i), value));
   }
-  ASSERT_OK(dbi->TEST_CompactMemTable());
+  ASSERT_LEVELDB_OK(dbi->TEST_CompactMemTable());
 
   // Delete everything
   for (int i = 0; i < kCount; i++) {
-    ASSERT_OK(db_->Delete(WriteOptions(), Key(i)));
+    ASSERT_LEVELDB_OK(db_->Delete(WriteOptions(), Key(i)));
   }
-  ASSERT_OK(dbi->TEST_CompactMemTable());
+  ASSERT_LEVELDB_OK(dbi->TEST_CompactMemTable());
 
   // Get initial measurement of the space we will be reading.
   const int64_t initial_size = Size(Key(0), Key(n));
@@ -89,8 +89,8 @@ void AutoCompactTest::DoReads(int n) {
     // 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);
+    std::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;
     }
@@ -103,10 +103,13 @@ void AutoCompactTest::DoReads(int n) {
   ASSERT_GE(final_other_size, initial_other_size / 5 - 1048576);
 }
 
-TEST(AutoCompactTest, ReadAll) { DoReads(kCount); }
+TEST_F(AutoCompactTest, ReadAll) { DoReads(kCount); }
 
-TEST(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); }
+TEST_F(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); }
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 9520ee4..e6329e0 100644 (file)
@@ -30,11 +30,14 @@ Status BuildTable(const std::string& dbname, Env* env, const Options& options,
 
     TableBuilder* builder = new TableBuilder(options, file);
     meta->smallest.DecodeFrom(iter->key());
+    Slice key;
     for (; iter->Valid(); iter->Next()) {
-      Slice key = iter->key();
-      meta->largest.DecodeFrom(key);
+      key = iter->key();
       builder->Add(key, iter->value());
     }
+    if (!key.empty()) {
+      meta->largest.DecodeFrom(key);
+    }
 
     // Finish and check for builder errors
     s = builder->Finish();
@@ -71,7 +74,7 @@ Status BuildTable(const std::string& dbname, Env* env, const Options& options,
   if (s.ok() && meta->file_size > 0) {
     // Keep it
   } else {
-    env->DeleteFile(fname);
+    env->RemoveFile(fname);
   }
   return s;
 }
diff --git a/db/c.cc b/db/c.cc
index e0f3367..8bdde38 100644 (file)
--- a/db/c.cc
+++ b/db/c.cc
@@ -4,7 +4,10 @@
 
 #include "leveldb/c.h"
 
-#include <stdlib.h>
+#include <string.h>
+
+#include <cstdint>
+#include <cstdlib>
 
 #include "leveldb/cache.h"
 #include "leveldb/comparator.h"
@@ -84,17 +87,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 +107,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<const char*> key_pointers(n);
     std::vector<size_t> key_sizes(n);
     for (int i = 0; i < n; i++) {
@@ -118,10 +121,10 @@ struct leveldb_filterpolicy_t : public FilterPolicy {
     size_t len;
     char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len);
     dst->append(filter, len);
-    free(filter);
+    std::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());
   }
@@ -132,8 +135,8 @@ struct leveldb_filterpolicy_t : public FilterPolicy {
   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);
+  uint8_t (*key_match_)(void*, const char* key, size_t length,
+                        const char* filter, size_t filter_length);
 };
 
 struct leveldb_env_t {
@@ -149,15 +152,16 @@ static bool SaveError(char** errptr, const Status& s) {
     *errptr = strdup(s.ToString().c_str());
   } else {
     // TODO(sanjay): Merge with existing error?
-    free(*errptr);
+    std::free(*errptr);
     *errptr = strdup(s.ToString().c_str());
   }
   return true;
 }
 
 static char* CopyString(const std::string& str) {
-  char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
-  memcpy(result, str.data(), sizeof(char) * str.size());
+  char* result =
+      reinterpret_cast<char*>(std::malloc(sizeof(char) * str.size()));
+  std::memcpy(result, str.data(), sizeof(char) * str.size());
   return result;
 }
 
@@ -281,7 +285,7 @@ void leveldb_iter_destroy(leveldb_iterator_t* iter) {
   delete iter;
 }
 
-unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) {
+uint8_t leveldb_iter_valid(const leveldb_iterator_t* iter) {
   return iter->rep->Valid();
 }
 
@@ -345,10 +349,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());
     }
   };
@@ -378,18 +382,15 @@ void leveldb_options_set_filter_policy(leveldb_options_t* opt,
   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, uint8_t 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, uint8_t 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, uint8_t v) {
   opt->rep.paranoid_checks = v;
 }
 
@@ -449,8 +450,8 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create(
     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),
+    uint8_t (*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;
@@ -497,12 +498,11 @@ leveldb_readoptions_t* leveldb_readoptions_create() {
 void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { delete opt; }
 
 void leveldb_readoptions_set_verify_checksums(leveldb_readoptions_t* opt,
-                                              unsigned char v) {
+                                              uint8_t 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, uint8_t v) {
   opt->rep.fill_cache = v;
 }
 
@@ -517,8 +517,7 @@ leveldb_writeoptions_t* leveldb_writeoptions_create() {
 
 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, uint8_t v) {
   opt->rep.sync = v;
 }
 
@@ -551,13 +550,13 @@ char* leveldb_env_get_test_directory(leveldb_env_t* env) {
     return nullptr;
   }
 
-  char* buffer = static_cast<char*>(malloc(result.size() + 1));
-  memcpy(buffer, result.data(), result.size());
+  char* buffer = static_cast<char*>(std::malloc(result.size() + 1));
+  std::memcpy(buffer, result.data(), result.size());
   buffer[result.size()] = '\0';
   return buffer;
 }
 
-void leveldb_free(void* ptr) { free(ptr); }
+void leveldb_free(void* ptr) { std::free(ptr); }
 
 int leveldb_major_version() { return kMajorVersion; }
 
index ae14b99..16c77ee 100644 (file)
@@ -120,7 +120,7 @@ static const char* CmpName(void* arg) {
 }
 
 // Custom filter policy
-static unsigned char fake_filter_result = 1;
+static uint8_t fake_filter_result = 1;
 static void FilterDestroy(void* arg) { }
 static const char* FilterName(void* arg) {
   return "TestFilter";
@@ -135,10 +135,8 @@ static char* FilterCreate(
   memcpy(result, "fake", 4);
   return result;
 }
-unsigned char FilterKeyMatch(
-    void* arg,
-    const char* key, size_t length,
-    const char* filter, size_t filter_length) {
+uint8_t FilterKeyMatch(void* arg, const char* key, size_t length,
+                       const char* filter, size_t filter_length) {
   CheckCondition(filter_length == 4);
   CheckCondition(memcmp(filter, "fake", 4) == 0);
   return fake_filter_result;
index 42f5237..a31f448 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <sys/types.h>
 
+#include "gtest/gtest.h"
 #include "db/db_impl.h"
 #include "db/filename.h"
 #include "db/log_format.h"
 #include "leveldb/table.h"
 #include "leveldb/write_batch.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
 
 static const int kValueSize = 1000;
 
-class CorruptionTest {
+class CorruptionTest : public testing::Test {
  public:
   CorruptionTest()
       : db_(nullptr),
@@ -46,19 +46,19 @@ class CorruptionTest {
     return DB::Open(options_, dbname_, &db_);
   }
 
-  void Reopen() { ASSERT_OK(TryReopen()); }
+  void Reopen() { ASSERT_LEVELDB_OK(TryReopen()); }
 
   void RepairDB() {
     delete db_;
     db_ = nullptr;
-    ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
+    ASSERT_LEVELDB_OK(::leveldb::RepairDB(dbname_, options_));
   }
 
   void Build(int n) {
     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) std::fprintf(stderr, "@ %d of %d\n", i, n);
       Slice key = Key(i, &key_space);
       batch.Clear();
       batch.Put(key, Value(i, &value_space));
@@ -68,7 +68,7 @@ class CorruptionTest {
       if (i == n - 1) {
         options.sync = true;
       }
-      ASSERT_OK(db_->Write(options, &batch));
+      ASSERT_LEVELDB_OK(db_->Write(options, &batch));
     }
   }
 
@@ -102,9 +102,10 @@ class CorruptionTest {
     }
     delete iter;
 
-    fprintf(stderr,
-            "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
-            min_expected, max_expected, correct, bad_keys, bad_values, missed);
+    std::fprintf(
+        stderr,
+        "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
+        min_expected, max_expected, correct, bad_keys, bad_values, missed);
     ASSERT_LE(min_expected, correct);
     ASSERT_GE(max_expected, correct);
   }
@@ -112,7 +113,7 @@ class CorruptionTest {
   void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
     // Pick file to corrupt
     std::vector<std::string> filenames;
-    ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames));
+    ASSERT_LEVELDB_OK(env_.target()->GetChildren(dbname_, &filenames));
     uint64_t number;
     FileType type;
     std::string fname;
@@ -127,7 +128,7 @@ class CorruptionTest {
     ASSERT_TRUE(!fname.empty()) << filetype;
 
     uint64_t file_size;
-    ASSERT_OK(env_.target()->GetFileSize(fname, &file_size));
+    ASSERT_LEVELDB_OK(env_.target()->GetFileSize(fname, &file_size));
 
     if (offset < 0) {
       // Relative to end of file; make it absolute
@@ -169,7 +170,7 @@ class CorruptionTest {
   // Return the ith key
   Slice Key(int i, std::string* storage) {
     char buf[100];
-    snprintf(buf, sizeof(buf), "%016d", i);
+    std::snprintf(buf, sizeof(buf), "%016d", i);
     storage->assign(buf, strlen(buf));
     return Slice(*storage);
   }
@@ -189,7 +190,7 @@ class CorruptionTest {
   Cache* tiny_cache_;
 };
 
-TEST(CorruptionTest, Recovery) {
+TEST_F(CorruptionTest, Recovery) {
   Build(100);
   Check(100, 100);
   Corrupt(kLogFile, 19, 1);  // WriteBatch tag for first record
@@ -200,13 +201,13 @@ TEST(CorruptionTest, Recovery) {
   Check(36, 36);
 }
 
-TEST(CorruptionTest, RecoverWriteError) {
+TEST_F(CorruptionTest, RecoverWriteError) {
   env_.writable_file_error_ = true;
   Status s = TryReopen();
   ASSERT_TRUE(!s.ok());
 }
 
-TEST(CorruptionTest, NewFileErrorDuringWrite) {
+TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
   // Do enough writing to force minor compaction
   env_.writable_file_error_ = true;
   const int num = 3 + (Options().write_buffer_size / kValueSize);
@@ -223,7 +224,7 @@ TEST(CorruptionTest, NewFileErrorDuringWrite) {
   Reopen();
 }
 
-TEST(CorruptionTest, TableFile) {
+TEST_F(CorruptionTest, TableFile) {
   Build(100);
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
   dbi->TEST_CompactMemTable();
@@ -234,7 +235,7 @@ TEST(CorruptionTest, TableFile) {
   Check(90, 99);
 }
 
-TEST(CorruptionTest, TableFileRepair) {
+TEST_F(CorruptionTest, TableFileRepair) {
   options_.block_size = 2 * kValueSize;  // Limit scope of corruption
   options_.paranoid_checks = true;
   Reopen();
@@ -250,7 +251,7 @@ TEST(CorruptionTest, TableFileRepair) {
   Check(95, 99);
 }
 
-TEST(CorruptionTest, TableFileIndexData) {
+TEST_F(CorruptionTest, TableFileIndexData) {
   Build(10000);  // Enough to build multiple Tables
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
   dbi->TEST_CompactMemTable();
@@ -260,36 +261,36 @@ TEST(CorruptionTest, TableFileIndexData) {
   Check(5000, 9999);
 }
 
-TEST(CorruptionTest, MissingDescriptor) {
+TEST_F(CorruptionTest, MissingDescriptor) {
   Build(1000);
   RepairDB();
   Reopen();
   Check(1000, 1000);
 }
 
-TEST(CorruptionTest, SequenceNumberRecovery) {
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
+TEST_F(CorruptionTest, SequenceNumberRecovery) {
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v1"));
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v2"));
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v3"));
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v4"));
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v5"));
   RepairDB();
   Reopen();
   std::string v;
-  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), "foo", &v));
   ASSERT_EQ("v5", v);
   // Write something.  If sequence number was not recovered properly,
   // it will be hidden by an earlier write.
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
-  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v6"));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), "foo", &v));
   ASSERT_EQ("v6", v);
   Reopen();
-  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), "foo", &v));
   ASSERT_EQ("v6", v);
 }
 
-TEST(CorruptionTest, CorruptedDescriptor) {
-  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
+TEST_F(CorruptionTest, CorruptedDescriptor) {
+  ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "hello"));
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
   dbi->TEST_CompactMemTable();
   dbi->TEST_CompactRange(0, nullptr, nullptr);
@@ -301,11 +302,11 @@ TEST(CorruptionTest, CorruptedDescriptor) {
   RepairDB();
   Reopen();
   std::string v;
-  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), "foo", &v));
   ASSERT_EQ("hello", v);
 }
 
-TEST(CorruptionTest, CompactionInputError) {
+TEST_F(CorruptionTest, CompactionInputError) {
   Build(10);
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
   dbi->TEST_CompactMemTable();
@@ -320,7 +321,7 @@ TEST(CorruptionTest, CompactionInputError) {
   Check(10000, 10000);
 }
 
-TEST(CorruptionTest, CompactionInputErrorParanoid) {
+TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
   options_.paranoid_checks = true;
   options_.write_buffer_size = 512 << 10;
   Reopen();
@@ -341,22 +342,26 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) {
   ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
 }
 
-TEST(CorruptionTest, UnrelatedKeys) {
+TEST_F(CorruptionTest, UnrelatedKeys) {
   Build(10);
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
   dbi->TEST_CompactMemTable();
   Corrupt(kTableFile, 100, 1);
 
   std::string tmp1, tmp2;
-  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
+  ASSERT_LEVELDB_OK(
+      db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
   std::string v;
-  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
   ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
   dbi->TEST_CompactMemTable();
-  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
+  ASSERT_LEVELDB_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
   ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
 }
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 761ebf6..1a4e459 100644 (file)
@@ -4,11 +4,10 @@
 
 #include "db/db_impl.h"
 
-#include <stdint.h>
-#include <stdio.h>
-
 #include <algorithm>
 #include <atomic>
+#include <cstdint>
+#include <cstdio>
 #include <set>
 #include <string>
 #include <vector>
@@ -198,6 +197,9 @@ Status DBImpl::NewDB() {
     new_db.EncodeTo(&record);
     s = log.AddRecord(record);
     if (s.ok()) {
+      s = file->Sync();
+    }
+    if (s.ok()) {
       s = file->Close();
     }
   }
@@ -206,7 +208,7 @@ Status DBImpl::NewDB() {
     // Make "CURRENT" file that points to the new manifest file.
     s = SetCurrentFile(env_, dbname_, 1);
   } else {
-    env_->DeleteFile(manifest);
+    env_->RemoveFile(manifest);
   }
   return s;
 }
@@ -220,7 +222,7 @@ void DBImpl::MaybeIgnoreError(Status* s) const {
   }
 }
 
-void DBImpl::DeleteObsoleteFiles() {
+void DBImpl::RemoveObsoleteFiles() {
   mutex_.AssertHeld();
 
   if (!bg_error_.ok()) {
@@ -237,8 +239,9 @@ void DBImpl::DeleteObsoleteFiles() {
   env_->GetChildren(dbname_, &filenames);  // Ignoring errors on purpose
   uint64_t number;
   FileType type;
-  for (size_t i = 0; i < filenames.size(); i++) {
-    if (ParseFileName(filenames[i], &number, &type)) {
+  std::vector<std::string> files_to_delete;
+  for (std::string& filename : filenames) {
+    if (ParseFileName(filename, &number, &type)) {
       bool keep = true;
       switch (type) {
         case kLogFile:
@@ -266,15 +269,24 @@ void DBImpl::DeleteObsoleteFiles() {
       }
 
       if (!keep) {
+        files_to_delete.push_back(std::move(filename));
         if (type == kTableFile) {
           table_cache_->Evict(number);
         }
         Log(options_.info_log, "Delete type=%d #%lld\n", static_cast<int>(type),
             static_cast<unsigned long long>(number));
-        env_->DeleteFile(dbname_ + "/" + filenames[i]);
       }
     }
   }
+
+  // While deleting all files unblock other threads. All files being deleted
+  // have unique names which will not collide with newly created files and
+  // are therefore safe to delete while allowing other threads to proceed.
+  mutex_.Unlock();
+  for (const std::string& filename : files_to_delete) {
+    env_->RemoveFile(dbname_ + "/" + filename);
+  }
+  mutex_.Lock();
 }
 
 Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) {
@@ -292,6 +304,8 @@ Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) {
 
   if (!env_->FileExists(CurrentFileName(dbname_))) {
     if (options_.create_if_missing) {
+      Log(options_.info_log, "Creating DB %s since it was missing.",
+          dbname_.c_str());
       s = NewDB();
       if (!s.ok()) {
         return s;
@@ -341,8 +355,8 @@ Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) {
   }
   if (!expected.empty()) {
     char buf[50];
-    snprintf(buf, sizeof(buf), "%d missing files; e.g.",
-             static_cast<int>(expected.size()));
+    std::snprintf(buf, sizeof(buf), "%d missing files; e.g.",
+                  static_cast<int>(expected.size()));
     return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
   }
 
@@ -376,7 +390,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<int>(bytes), s.ToString().c_str());
@@ -559,7 +573,7 @@ void DBImpl::CompactMemTable() {
     imm_->Unref();
     imm_ = nullptr;
     has_imm_.store(false, std::memory_order_release);
-    DeleteObsoleteFiles();
+    RemoveObsoleteFiles();
   } else {
     RecordBackgroundError(s);
   }
@@ -719,7 +733,7 @@ void DBImpl::BackgroundCompaction() {
     // Move file to next level
     assert(c->num_input_files(0) == 1);
     FileMetaData* f = c->input(0, 0);
-    c->edit()->DeleteFile(c->level(), f->number);
+    c->edit()->RemoveFile(c->level(), f->number);
     c->edit()->AddFile(c->level() + 1, f->number, f->file_size, f->smallest,
                        f->largest);
     status = versions_->LogAndApply(c->edit(), &mutex_);
@@ -739,7 +753,7 @@ void DBImpl::BackgroundCompaction() {
     }
     CleanupCompaction(compact);
     c->ReleaseInputs();
-    DeleteObsoleteFiles();
+    RemoveObsoleteFiles();
   }
   delete c;
 
@@ -893,17 +907,18 @@ 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;
   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();
@@ -1202,9 +1217,9 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
   uint64_t last_sequence = versions_->LastSequence();
   Writer* last_writer = &w;
   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);
+    WriteBatch* write_batch = BuildBatchGroup(&last_writer);
+    WriteBatchInternal::SetSequence(write_batch, last_sequence + 1);
+    last_sequence += WriteBatchInternal::Count(write_batch);
 
     // Add to log and apply to memtable.  We can release the lock
     // during this phase since &w is currently responsible for logging
@@ -1212,7 +1227,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
     // into mem_.
     {
       mutex_.Unlock();
-      status = log_->AddRecord(WriteBatchInternal::Contents(updates));
+      status = log_->AddRecord(WriteBatchInternal::Contents(write_batch));
       bool sync_error = false;
       if (status.ok() && options.sync) {
         status = logfile_->Sync();
@@ -1221,7 +1236,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
         }
       }
       if (status.ok()) {
-        status = WriteBatchInternal::InsertInto(updates, mem_);
+        status = WriteBatchInternal::InsertInto(write_batch, mem_);
       }
       mutex_.Lock();
       if (sync_error) {
@@ -1231,7 +1246,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
         RecordBackgroundError(status);
       }
     }
-    if (updates == tmp_batch_) tmp_batch_->Clear();
+    if (write_batch == tmp_batch_) tmp_batch_->Clear();
 
     versions_->SetLastSequence(last_sequence);
   }
@@ -1386,26 +1401,26 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) {
       return false;
     } else {
       char buf[100];
-      snprintf(buf, sizeof(buf), "%d",
-               versions_->NumLevelFiles(static_cast<int>(level)));
+      std::snprintf(buf, sizeof(buf), "%d",
+                    versions_->NumLevelFiles(static_cast<int>(level)));
       *value = buf;
       return true;
     }
   } else if (in == "stats") {
     char buf[200];
-    snprintf(buf, sizeof(buf),
-             "                               Compactions\n"
-             "Level  Files Size(MB) Time(sec) Read(MB) Write(MB)\n"
-             "--------------------------------------------------\n");
+    std::snprintf(buf, sizeof(buf),
+                  "                               Compactions\n"
+                  "Level  Files Size(MB) Time(sec) Read(MB) Write(MB)\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);
+        std::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);
       }
     }
@@ -1422,8 +1437,8 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) {
       total_usage += imm_->ApproximateMemoryUsage();
     }
     char buf[50];
-    snprintf(buf, sizeof(buf), "%llu",
-             static_cast<unsigned long long>(total_usage));
+    std::snprintf(buf, sizeof(buf), "%llu",
+                  static_cast<unsigned long long>(total_usage));
     value->append(buf);
     return true;
   }
@@ -1433,12 +1448,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 +1461,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
@@ -1469,7 +1478,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;
@@ -1501,7 +1510,7 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
     s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
   }
   if (s.ok()) {
-    impl->DeleteObsoleteFiles();
+    impl->RemoveObsoleteFiles();
     impl->MaybeScheduleCompaction();
   }
   impl->mutex_.Unlock();
@@ -1514,7 +1523,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;
@@ -1534,15 +1543,15 @@ Status DestroyDB(const std::string& dbname, const Options& options) {
     for (size_t i = 0; i < filenames.size(); i++) {
       if (ParseFileName(filenames[i], &number, &type) &&
           type != kDBLockFile) {  // Lock file will be deleted at end
-        Status del = env->DeleteFile(dbname + "/" + filenames[i]);
+        Status del = env->RemoveFile(dbname + "/" + filenames[i]);
         if (result.ok() && !del.ok()) {
           result = del;
         }
       }
     }
     env->UnlockFile(lock);  // Ignore error since state is already gone
-    env->DeleteFile(lockname);
-    env->DeleteDir(dbname);  // Ignore error in case dir contains other files
+    env->RemoveFile(lockname);
+    env->RemoveDir(dbname);  // Ignore error in case dir contains other files
   }
   return result;
 }
index ae87d6e..c7b0172 100644 (file)
@@ -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
 
@@ -115,7 +116,7 @@ class DBImpl : public DB {
   void MaybeIgnoreError(Status* s) const;
 
   // Delete any unneeded files and stale in-memory entries.
-  void DeleteObsoleteFiles() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  void RemoveObsoleteFiles() 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.
@@ -196,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_);
index 8ff288e..532c2db 100644 (file)
@@ -21,9 +21,9 @@ static void DumpInternalIter(Iterator* iter) {
   for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
     ParsedInternalKey k;
     if (!ParseInternalKey(iter->key(), &k)) {
-      fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str());
+      std::fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str());
     } else {
-      fprintf(stderr, "@ '%s'\n", k.DebugString().c_str());
+      std::fprintf(stderr, "@ '%s'\n", k.DebugString().c_str());
     }
   }
 }
@@ -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);
@@ -160,6 +160,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_);
index fd93e91..5977fc8 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_DB_DB_ITER_H_
 #define STORAGE_LEVELDB_DB_DB_ITER_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "db/dbformat.h"
 #include "leveldb/db.h"
index 78296d5..908b41d 100644 (file)
@@ -5,8 +5,11 @@
 #include "leveldb/db.h"
 
 #include <atomic>
+#include <cinttypes>
 #include <string>
 
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
 #include "db/db_impl.h"
 #include "db/filename.h"
 #include "db/version_set.h"
@@ -20,7 +23,6 @@
 #include "util/hash.h"
 #include "util/logging.h"
 #include "util/mutexlock.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
@@ -210,9 +212,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);
       }
@@ -226,7 +228,7 @@ class SpecialEnv : public EnvWrapper {
   }
 };
 
-class DBTest {
+class DBTest : public testing::Test {
  public:
   std::string dbname_;
   SpecialEnv* env_;
@@ -236,7 +238,7 @@ class DBTest {
 
   DBTest() : env_(new SpecialEnv(Env::Default())), option_config_(kDefault) {
     filter_policy_ = NewBloomFilterPolicy(10);
-    dbname_ = test::TmpDir() + "/db_test";
+    dbname_ = testing::TempDir() + "db_test";
     DestroyDB(dbname_, Options());
     db_ = nullptr;
     Reopen();
@@ -283,7 +285,9 @@ class DBTest {
 
   DBImpl* dbfull() { return reinterpret_cast<DBImpl*>(db_); }
 
-  void Reopen(Options* options = nullptr) { ASSERT_OK(TryReopen(options)); }
+  void Reopen(Options* options = nullptr) {
+    ASSERT_LEVELDB_OK(TryReopen(options));
+  }
 
   void Close() {
     delete db_;
@@ -294,7 +298,7 @@ class DBTest {
     delete db_;
     db_ = nullptr;
     DestroyDB(dbname_, Options());
-    ASSERT_OK(TryReopen(options));
+    ASSERT_LEVELDB_OK(TryReopen(options));
   }
 
   Status TryReopen(Options* options) {
@@ -348,11 +352,11 @@ class DBTest {
     // Check reverse iteration results are the reverse of forward results
     size_t matched = 0;
     for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
-      ASSERT_LT(matched, forward.size());
-      ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]);
+      EXPECT_LT(matched, forward.size());
+      EXPECT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]);
       matched++;
     }
-    ASSERT_EQ(matched, forward.size());
+    EXPECT_EQ(matched, forward.size());
 
     delete iter;
     return result;
@@ -402,7 +406,7 @@ class DBTest {
 
   int NumTableFilesAtLevel(int level) {
     std::string property;
-    ASSERT_TRUE(db_->GetProperty(
+    EXPECT_TRUE(db_->GetProperty(
         "leveldb.num-files-at-level" + NumberToString(level), &property));
     return std::stoi(property);
   }
@@ -422,7 +426,7 @@ class DBTest {
     for (int level = 0; level < config::kNumLevels; level++) {
       int f = NumTableFilesAtLevel(level);
       char buf[100];
-      snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);
+      std::snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);
       result += buf;
       if (f > 0) {
         last_non_zero_offset = result.size();
@@ -467,14 +471,14 @@ class DBTest {
   }
 
   void DumpFileCounts(const char* label) {
-    fprintf(stderr, "---\n%s:\n", label);
-    fprintf(
+    std::fprintf(stderr, "---\n%s:\n", label);
+    std::fprintf(
         stderr, "maxoverlap: %lld\n",
         static_cast<long long>(dbfull()->TEST_MaxNextLevelOverlappingBytes()));
     for (int level = 0; level < config::kNumLevels; level++) {
       int num = NumTableFilesAtLevel(level);
       if (num > 0) {
-        fprintf(stderr, "  level %3d : %d files\n", level, num);
+        std::fprintf(stderr, "  level %3d : %d files\n", level, num);
       }
     }
   }
@@ -497,12 +501,12 @@ class DBTest {
 
   bool DeleteAnSSTFile() {
     std::vector<std::string> filenames;
-    ASSERT_OK(env_->GetChildren(dbname_, &filenames));
+    EXPECT_LEVELDB_OK(env_->GetChildren(dbname_, &filenames));
     uint64_t number;
     FileType type;
     for (size_t i = 0; i < filenames.size(); i++) {
       if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) {
-        ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number)));
+        EXPECT_LEVELDB_OK(env_->RemoveFile(TableFileName(dbname_, number)));
         return true;
       }
     }
@@ -512,7 +516,7 @@ class DBTest {
   // Returns number of files renamed.
   int RenameLDBToSST() {
     std::vector<std::string> filenames;
-    ASSERT_OK(env_->GetChildren(dbname_, &filenames));
+    EXPECT_LEVELDB_OK(env_->GetChildren(dbname_, &filenames));
     uint64_t number;
     FileType type;
     int files_renamed = 0;
@@ -520,7 +524,7 @@ class DBTest {
       if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) {
         const std::string from = TableFileName(dbname_, number);
         const std::string to = SSTTableFileName(dbname_, number);
-        ASSERT_OK(env_->RenameFile(from, to));
+        EXPECT_LEVELDB_OK(env_->RenameFile(from, to));
         files_renamed++;
       }
     }
@@ -535,63 +539,63 @@ class DBTest {
   int option_config_;
 };
 
-TEST(DBTest, Empty) {
+TEST_F(DBTest, Empty) {
   do {
     ASSERT_TRUE(db_ != nullptr);
     ASSERT_EQ("NOT_FOUND", Get("foo"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, EmptyKey) {
+TEST_F(DBTest, EmptyKey) {
   do {
-    ASSERT_OK(Put("", "v1"));
+    ASSERT_LEVELDB_OK(Put("", "v1"));
     ASSERT_EQ("v1", Get(""));
-    ASSERT_OK(Put("", "v2"));
+    ASSERT_LEVELDB_OK(Put("", "v2"));
     ASSERT_EQ("v2", Get(""));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, EmptyValue) {
+TEST_F(DBTest, EmptyValue) {
   do {
-    ASSERT_OK(Put("key", "v1"));
+    ASSERT_LEVELDB_OK(Put("key", "v1"));
     ASSERT_EQ("v1", Get("key"));
-    ASSERT_OK(Put("key", ""));
+    ASSERT_LEVELDB_OK(Put("key", ""));
     ASSERT_EQ("", Get("key"));
-    ASSERT_OK(Put("key", "v2"));
+    ASSERT_LEVELDB_OK(Put("key", "v2"));
     ASSERT_EQ("v2", Get("key"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, ReadWrite) {
+TEST_F(DBTest, ReadWrite) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     ASSERT_EQ("v1", Get("foo"));
-    ASSERT_OK(Put("bar", "v2"));
-    ASSERT_OK(Put("foo", "v3"));
+    ASSERT_LEVELDB_OK(Put("bar", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v3"));
     ASSERT_EQ("v3", Get("foo"));
     ASSERT_EQ("v2", Get("bar"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, PutDeleteGet) {
+TEST_F(DBTest, PutDeleteGet) {
   do {
-    ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
+    ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v1"));
     ASSERT_EQ("v1", Get("foo"));
-    ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
+    ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), "foo", "v2"));
     ASSERT_EQ("v2", Get("foo"));
-    ASSERT_OK(db_->Delete(WriteOptions(), "foo"));
+    ASSERT_LEVELDB_OK(db_->Delete(WriteOptions(), "foo"));
     ASSERT_EQ("NOT_FOUND", Get("foo"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetFromImmutableLayer) {
+TEST_F(DBTest, GetFromImmutableLayer) {
   do {
     Options options = CurrentOptions();
     options.env = env_;
     options.write_buffer_size = 100000;  // Small write buffer
     Reopen(&options);
 
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     ASSERT_EQ("v1", Get("foo"));
 
     // Block sync calls.
@@ -604,17 +608,17 @@ TEST(DBTest, GetFromImmutableLayer) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetFromVersions) {
+TEST_F(DBTest, GetFromVersions) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("v1", Get("foo"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetMemUsage) {
+TEST_F(DBTest, GetMemUsage) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     std::string val;
     ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val));
     int mem_usage = std::stoi(val);
@@ -623,14 +627,14 @@ TEST(DBTest, GetMemUsage) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetSnapshot) {
+TEST_F(DBTest, GetSnapshot) {
   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"));
+      ASSERT_LEVELDB_OK(Put(key, "v1"));
       const Snapshot* s1 = db_->GetSnapshot();
-      ASSERT_OK(Put(key, "v2"));
+      ASSERT_LEVELDB_OK(Put(key, "v2"));
       ASSERT_EQ("v2", Get(key));
       ASSERT_EQ("v1", Get(key, s1));
       dbfull()->TEST_CompactMemTable();
@@ -641,16 +645,16 @@ TEST(DBTest, GetSnapshot) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetIdenticalSnapshots) {
+TEST_F(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"));
+      ASSERT_LEVELDB_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_LEVELDB_OK(Put(key, "v2"));
       ASSERT_EQ("v2", Get(key));
       ASSERT_EQ("v1", Get(key, s1));
       ASSERT_EQ("v1", Get(key, s2));
@@ -666,13 +670,13 @@ TEST(DBTest, GetIdenticalSnapshots) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, IterateOverEmptySnapshot) {
+TEST_F(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"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v2"));
 
     Iterator* iterator1 = db_->NewIterator(read_options);
     iterator1->SeekToFirst();
@@ -690,41 +694,41 @@ TEST(DBTest, IterateOverEmptySnapshot) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetLevel0Ordering) {
+TEST_F(DBTest, GetLevel0Ordering) {
   do {
     // Check that we process level-0 files in correct order.  The code
     // below generates two level-0 files where the earlier one comes
     // before the later one in the level-0 file list since the earlier
     // one has a smaller "smallest" key.
-    ASSERT_OK(Put("bar", "b"));
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("bar", "b"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     dbfull()->TEST_CompactMemTable();
-    ASSERT_OK(Put("foo", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v2"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("v2", Get("foo"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetOrderedByLevels) {
+TEST_F(DBTest, GetOrderedByLevels) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
     Compact("a", "z");
     ASSERT_EQ("v1", Get("foo"));
-    ASSERT_OK(Put("foo", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v2"));
     ASSERT_EQ("v2", Get("foo"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("v2", Get("foo"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetPicksCorrectFile) {
+TEST_F(DBTest, GetPicksCorrectFile) {
   do {
     // Arrange to have multiple files in a non-level-0 level.
-    ASSERT_OK(Put("a", "va"));
+    ASSERT_LEVELDB_OK(Put("a", "va"));
     Compact("a", "b");
-    ASSERT_OK(Put("x", "vx"));
+    ASSERT_LEVELDB_OK(Put("x", "vx"));
     Compact("x", "y");
-    ASSERT_OK(Put("f", "vf"));
+    ASSERT_LEVELDB_OK(Put("f", "vf"));
     Compact("f", "g");
     ASSERT_EQ("va", Get("a"));
     ASSERT_EQ("vf", Get("f"));
@@ -732,7 +736,7 @@ TEST(DBTest, GetPicksCorrectFile) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, GetEncountersEmptyLevel) {
+TEST_F(DBTest, GetEncountersEmptyLevel) {
   do {
     // Arrange for the following to happen:
     //   * sstable A in level 0
@@ -770,7 +774,7 @@ TEST(DBTest, GetEncountersEmptyLevel) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, IterEmpty) {
+TEST_F(DBTest, IterEmpty) {
   Iterator* iter = db_->NewIterator(ReadOptions());
 
   iter->SeekToFirst();
@@ -785,8 +789,8 @@ TEST(DBTest, IterEmpty) {
   delete iter;
 }
 
-TEST(DBTest, IterSingle) {
-  ASSERT_OK(Put("a", "va"));
+TEST_F(DBTest, IterSingle) {
+  ASSERT_LEVELDB_OK(Put("a", "va"));
   Iterator* iter = db_->NewIterator(ReadOptions());
 
   iter->SeekToFirst();
@@ -823,10 +827,10 @@ TEST(DBTest, IterSingle) {
   delete iter;
 }
 
-TEST(DBTest, IterMulti) {
-  ASSERT_OK(Put("a", "va"));
-  ASSERT_OK(Put("b", "vb"));
-  ASSERT_OK(Put("c", "vc"));
+TEST_F(DBTest, IterMulti) {
+  ASSERT_LEVELDB_OK(Put("a", "va"));
+  ASSERT_LEVELDB_OK(Put("b", "vb"));
+  ASSERT_LEVELDB_OK(Put("c", "vc"));
   Iterator* iter = db_->NewIterator(ReadOptions());
 
   iter->SeekToFirst();
@@ -881,11 +885,11 @@ TEST(DBTest, IterMulti) {
   ASSERT_EQ(IterStatus(iter), "b->vb");
 
   // Make sure iter stays at snapshot
-  ASSERT_OK(Put("a", "va2"));
-  ASSERT_OK(Put("a2", "va3"));
-  ASSERT_OK(Put("b", "vb2"));
-  ASSERT_OK(Put("c", "vc2"));
-  ASSERT_OK(Delete("b"));
+  ASSERT_LEVELDB_OK(Put("a", "va2"));
+  ASSERT_LEVELDB_OK(Put("a2", "va3"));
+  ASSERT_LEVELDB_OK(Put("b", "vb2"));
+  ASSERT_LEVELDB_OK(Put("c", "vc2"));
+  ASSERT_LEVELDB_OK(Delete("b"));
   iter->SeekToFirst();
   ASSERT_EQ(IterStatus(iter), "a->va");
   iter->Next();
@@ -906,12 +910,12 @@ TEST(DBTest, IterMulti) {
   delete iter;
 }
 
-TEST(DBTest, IterSmallAndLargeMix) {
-  ASSERT_OK(Put("a", "va"));
-  ASSERT_OK(Put("b", std::string(100000, 'b')));
-  ASSERT_OK(Put("c", "vc"));
-  ASSERT_OK(Put("d", std::string(100000, 'd')));
-  ASSERT_OK(Put("e", std::string(100000, 'e')));
+TEST_F(DBTest, IterSmallAndLargeMix) {
+  ASSERT_LEVELDB_OK(Put("a", "va"));
+  ASSERT_LEVELDB_OK(Put("b", std::string(100000, 'b')));
+  ASSERT_LEVELDB_OK(Put("c", "vc"));
+  ASSERT_LEVELDB_OK(Put("d", std::string(100000, 'd')));
+  ASSERT_LEVELDB_OK(Put("e", std::string(100000, 'e')));
 
   Iterator* iter = db_->NewIterator(ReadOptions());
 
@@ -944,12 +948,30 @@ TEST(DBTest, IterSmallAndLargeMix) {
   delete iter;
 }
 
-TEST(DBTest, IterMultiWithDelete) {
+TEST_F(DBTest, IterMultiWithDelete) {
+  do {
+    ASSERT_LEVELDB_OK(Put("a", "va"));
+    ASSERT_LEVELDB_OK(Put("b", "vb"));
+    ASSERT_LEVELDB_OK(Put("c", "vc"));
+    ASSERT_LEVELDB_OK(Delete("b"));
+    ASSERT_EQ("NOT_FOUND", Get("b"));
+
+    Iterator* iter = db_->NewIterator(ReadOptions());
+    iter->Seek("c");
+    ASSERT_EQ(IterStatus(iter), "c->vc");
+    iter->Prev();
+    ASSERT_EQ(IterStatus(iter), "a->va");
+    delete iter;
+  } while (ChangeOptions());
+}
+
+TEST_F(DBTest, IterMultiWithDeleteAndCompaction) {
   do {
-    ASSERT_OK(Put("a", "va"));
-    ASSERT_OK(Put("b", "vb"));
-    ASSERT_OK(Put("c", "vc"));
-    ASSERT_OK(Delete("b"));
+    ASSERT_LEVELDB_OK(Put("b", "vb"));
+    ASSERT_LEVELDB_OK(Put("c", "vc"));
+    ASSERT_LEVELDB_OK(Put("a", "va"));
+    dbfull()->TEST_CompactMemTable();
+    ASSERT_LEVELDB_OK(Delete("b"));
     ASSERT_EQ("NOT_FOUND", Get("b"));
 
     Iterator* iter = db_->NewIterator(ReadOptions());
@@ -957,39 +979,41 @@ TEST(DBTest, IterMultiWithDelete) {
     ASSERT_EQ(IterStatus(iter), "c->vc");
     iter->Prev();
     ASSERT_EQ(IterStatus(iter), "a->va");
+    iter->Seek("b");
+    ASSERT_EQ(IterStatus(iter), "c->vc");
     delete iter;
   } while (ChangeOptions());
 }
 
-TEST(DBTest, Recover) {
+TEST_F(DBTest, Recover) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
-    ASSERT_OK(Put("baz", "v5"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("baz", "v5"));
 
     Reopen();
     ASSERT_EQ("v1", Get("foo"));
 
     ASSERT_EQ("v1", Get("foo"));
     ASSERT_EQ("v5", Get("baz"));
-    ASSERT_OK(Put("bar", "v2"));
-    ASSERT_OK(Put("foo", "v3"));
+    ASSERT_LEVELDB_OK(Put("bar", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v3"));
 
     Reopen();
     ASSERT_EQ("v3", Get("foo"));
-    ASSERT_OK(Put("foo", "v4"));
+    ASSERT_LEVELDB_OK(Put("foo", "v4"));
     ASSERT_EQ("v4", Get("foo"));
     ASSERT_EQ("v2", Get("bar"));
     ASSERT_EQ("v5", Get("baz"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, RecoveryWithEmptyLog) {
+TEST_F(DBTest, RecoveryWithEmptyLog) {
   do {
-    ASSERT_OK(Put("foo", "v1"));
-    ASSERT_OK(Put("foo", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));
+    ASSERT_LEVELDB_OK(Put("foo", "v2"));
     Reopen();
     Reopen();
-    ASSERT_OK(Put("foo", "v3"));
+    ASSERT_LEVELDB_OK(Put("foo", "v3"));
     Reopen();
     ASSERT_EQ("v3", Get("foo"));
   } while (ChangeOptions());
@@ -997,7 +1021,7 @@ TEST(DBTest, RecoveryWithEmptyLog) {
 
 // Check that writes done during a memtable compaction are recovered
 // if the database is shutdown during the memtable compaction.
-TEST(DBTest, RecoverDuringMemtableCompaction) {
+TEST_F(DBTest, RecoverDuringMemtableCompaction) {
   do {
     Options options = CurrentOptions();
     options.env = env_;
@@ -1005,10 +1029,12 @@ TEST(DBTest, RecoverDuringMemtableCompaction) {
     Reopen(&options);
 
     // Trigger a long memtable compaction and reopen the database during it
-    ASSERT_OK(Put("foo", "v1"));                         // Goes to 1st log file
-    ASSERT_OK(Put("big1", std::string(10000000, 'x')));  // Fills memtable
-    ASSERT_OK(Put("big2", std::string(1000, 'y')));      // Triggers compaction
-    ASSERT_OK(Put("bar", "v2"));                         // Goes to new log file
+    ASSERT_LEVELDB_OK(Put("foo", "v1"));  // Goes to 1st log file
+    ASSERT_LEVELDB_OK(
+        Put("big1", std::string(10000000, 'x')));  // Fills memtable
+    ASSERT_LEVELDB_OK(
+        Put("big2", std::string(1000, 'y')));  // Triggers compaction
+    ASSERT_LEVELDB_OK(Put("bar", "v2"));       // Goes to new log file
 
     Reopen(&options);
     ASSERT_EQ("v1", Get("foo"));
@@ -1020,11 +1046,11 @@ TEST(DBTest, RecoverDuringMemtableCompaction) {
 
 static std::string Key(int i) {
   char buf[100];
-  snprintf(buf, sizeof(buf), "key%06d", i);
+  std::snprintf(buf, sizeof(buf), "key%06d", i);
   return std::string(buf);
 }
 
-TEST(DBTest, MinorCompactionsHappen) {
+TEST_F(DBTest, MinorCompactionsHappen) {
   Options options = CurrentOptions();
   options.write_buffer_size = 10000;
   Reopen(&options);
@@ -1033,7 +1059,7 @@ TEST(DBTest, MinorCompactionsHappen) {
 
   int starting_num_tables = TotalTableFiles();
   for (int i = 0; i < N; i++) {
-    ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v')));
+    ASSERT_LEVELDB_OK(Put(Key(i), Key(i) + std::string(1000, 'v')));
   }
   int ending_num_tables = TotalTableFiles();
   ASSERT_GT(ending_num_tables, starting_num_tables);
@@ -1049,14 +1075,14 @@ TEST(DBTest, MinorCompactionsHappen) {
   }
 }
 
-TEST(DBTest, RecoverWithLargeLog) {
+TEST_F(DBTest, RecoverWithLargeLog) {
   {
     Options options = CurrentOptions();
     Reopen(&options);
-    ASSERT_OK(Put("big1", std::string(200000, '1')));
-    ASSERT_OK(Put("big2", std::string(200000, '2')));
-    ASSERT_OK(Put("small3", std::string(10, '3')));
-    ASSERT_OK(Put("small4", std::string(10, '4')));
+    ASSERT_LEVELDB_OK(Put("big1", std::string(200000, '1')));
+    ASSERT_LEVELDB_OK(Put("big2", std::string(200000, '2')));
+    ASSERT_LEVELDB_OK(Put("small3", std::string(10, '3')));
+    ASSERT_LEVELDB_OK(Put("small4", std::string(10, '4')));
     ASSERT_EQ(NumTableFilesAtLevel(0), 0);
   }
 
@@ -1073,7 +1099,7 @@ TEST(DBTest, RecoverWithLargeLog) {
   ASSERT_GT(NumTableFilesAtLevel(0), 1);
 }
 
-TEST(DBTest, CompactionsGenerateMultipleFiles) {
+TEST_F(DBTest, CompactionsGenerateMultipleFiles) {
   Options options = CurrentOptions();
   options.write_buffer_size = 100000000;  // Large write buffer
   Reopen(&options);
@@ -1085,7 +1111,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) {
   std::vector<std::string> values;
   for (int i = 0; i < 80; i++) {
     values.push_back(RandomString(&rnd, 100000));
-    ASSERT_OK(Put(Key(i), values[i]));
+    ASSERT_LEVELDB_OK(Put(Key(i), values[i]));
   }
 
   // Reopening moves updates to level-0
@@ -1099,7 +1125,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) {
   }
 }
 
-TEST(DBTest, RepeatedWritesToSameKey) {
+TEST_F(DBTest, RepeatedWritesToSameKey) {
   Options options = CurrentOptions();
   options.env = env_;
   options.write_buffer_size = 100000;  // Small write buffer
@@ -1114,11 +1140,11 @@ 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", i + 1, TotalTableFiles());
+    std::fprintf(stderr, "after %d: %d files\n", i + 1, TotalTableFiles());
   }
 }
 
-TEST(DBTest, SparseMerge) {
+TEST_F(DBTest, SparseMerge) {
   Options options = CurrentOptions();
   options.compression = kNoCompression;
   Reopen(&options);
@@ -1136,7 +1162,7 @@ TEST(DBTest, SparseMerge) {
   // Write approximately 100MB of "B" values
   for (int i = 0; i < 100000; i++) {
     char key[100];
-    snprintf(key, sizeof(key), "B%010d", i);
+    std::snprintf(key, sizeof(key), "B%010d", i);
     Put(key, value);
   }
   Put("C", "vc");
@@ -1161,14 +1187,14 @@ TEST(DBTest, SparseMerge) {
 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)(high));
+    std::fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
+                 (unsigned long long)(val), (unsigned long long)(low),
+                 (unsigned long long)(high));
   }
   return result;
 }
 
-TEST(DBTest, ApproximateSizes) {
+TEST_F(DBTest, ApproximateSizes) {
   do {
     Options options = CurrentOptions();
     options.write_buffer_size = 100000000;  // Large write buffer
@@ -1186,7 +1212,7 @@ TEST(DBTest, ApproximateSizes) {
     static const int S2 = 105000;  // Allow some expansion from metadata
     Random rnd(301);
     for (int i = 0; i < N; i++) {
-      ASSERT_OK(Put(Key(i), RandomString(&rnd, S1)));
+      ASSERT_LEVELDB_OK(Put(Key(i), RandomString(&rnd, S1)));
     }
 
     // 0 because GetApproximateSizes() does not account for memtable space
@@ -1227,7 +1253,7 @@ TEST(DBTest, ApproximateSizes) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
+TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
   do {
     Options options = CurrentOptions();
     options.compression = kNoCompression;
@@ -1235,18 +1261,18 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
 
     Random rnd(301);
     std::string big1 = RandomString(&rnd, 100000);
-    ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000)));
-    ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000)));
-    ASSERT_OK(Put(Key(2), big1));
-    ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000)));
-    ASSERT_OK(Put(Key(4), big1));
-    ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000)));
-    ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));
-    ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));
+    ASSERT_LEVELDB_OK(Put(Key(0), RandomString(&rnd, 10000)));
+    ASSERT_LEVELDB_OK(Put(Key(1), RandomString(&rnd, 10000)));
+    ASSERT_LEVELDB_OK(Put(Key(2), big1));
+    ASSERT_LEVELDB_OK(Put(Key(3), RandomString(&rnd, 10000)));
+    ASSERT_LEVELDB_OK(Put(Key(4), big1));
+    ASSERT_LEVELDB_OK(Put(Key(5), RandomString(&rnd, 10000)));
+    ASSERT_LEVELDB_OK(Put(Key(6), RandomString(&rnd, 300000)));
+    ASSERT_LEVELDB_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());
+      ASSERT_LEVELDB_OK(dbfull()->TEST_CompactMemTable());
     }
 
     // Check sizes across recovery by reopening a few times
@@ -1270,7 +1296,7 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, IteratorPinsRef) {
+TEST_F(DBTest, IteratorPinsRef) {
   Put("foo", "hello");
 
   // Get iterator that will yield the current contents of the DB.
@@ -1279,7 +1305,8 @@ 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_LEVELDB_OK(
+        Put(Key(i), Key(i) + std::string(100000, 'v')));  // 100K values
   }
   Put("foo", "newvalue2");
 
@@ -1292,7 +1319,7 @@ TEST(DBTest, IteratorPinsRef) {
   delete iter;
 }
 
-TEST(DBTest, Snapshot) {
+TEST_F(DBTest, Snapshot) {
   do {
     Put("foo", "v1");
     const Snapshot* s1 = db_->GetSnapshot();
@@ -1321,7 +1348,7 @@ TEST(DBTest, Snapshot) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, HiddenValuesAreRemoved) {
+TEST_F(DBTest, HiddenValuesAreRemoved) {
   do {
     Random rnd(301);
     FillLevels("a", "z");
@@ -1333,7 +1360,7 @@ TEST(DBTest, HiddenValuesAreRemoved) {
     Put("foo", "tiny");
     Put("pastfoo2", "v2");  // Advance sequence number one more
 
-    ASSERT_OK(dbfull()->TEST_CompactMemTable());
+    ASSERT_LEVELDB_OK(dbfull()->TEST_CompactMemTable());
     ASSERT_GT(NumTableFilesAtLevel(0), 0);
 
     ASSERT_EQ(big, Get("foo", snapshot));
@@ -1352,9 +1379,9 @@ TEST(DBTest, HiddenValuesAreRemoved) {
   } while (ChangeOptions());
 }
 
-TEST(DBTest, DeletionMarkers1) {
+TEST_F(DBTest, DeletionMarkers1) {
   Put("foo", "v1");
-  ASSERT_OK(dbfull()->TEST_CompactMemTable());
+  ASSERT_LEVELDB_OK(dbfull()->TEST_CompactMemTable());
   const int last = config::kMaxMemCompactLevel;
   ASSERT_EQ(NumTableFilesAtLevel(last), 1);  // foo => v1 is now in last level
 
@@ -1368,7 +1395,7 @@ TEST(DBTest, DeletionMarkers1) {
   Delete("foo");
   Put("foo", "v2");
   ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");
-  ASSERT_OK(dbfull()->TEST_CompactMemTable());  // Moves to level last-2
+  ASSERT_LEVELDB_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);
@@ -1381,9 +1408,9 @@ TEST(DBTest, DeletionMarkers1) {
   ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]");
 }
 
-TEST(DBTest, DeletionMarkers2) {
+TEST_F(DBTest, DeletionMarkers2) {
   Put("foo", "v1");
-  ASSERT_OK(dbfull()->TEST_CompactMemTable());
+  ASSERT_LEVELDB_OK(dbfull()->TEST_CompactMemTable());
   const int last = config::kMaxMemCompactLevel;
   ASSERT_EQ(NumTableFilesAtLevel(last), 1);  // foo => v1 is now in last level
 
@@ -1396,7 +1423,7 @@ TEST(DBTest, DeletionMarkers2) {
 
   Delete("foo");
   ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
-  ASSERT_OK(dbfull()->TEST_CompactMemTable());  // Moves to level last-2
+  ASSERT_LEVELDB_OK(dbfull()->TEST_CompactMemTable());  // Moves to level last-2
   ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");
   dbfull()->TEST_CompactRange(last - 2, nullptr, nullptr);
   // DEL kept: "last" file overlaps
@@ -1407,17 +1434,17 @@ TEST(DBTest, DeletionMarkers2) {
   ASSERT_EQ(AllEntriesFor("foo"), "[ ]");
 }
 
-TEST(DBTest, OverlapInLevel0) {
+TEST_F(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.
-    ASSERT_OK(Put("100", "v100"));
-    ASSERT_OK(Put("999", "v999"));
+    ASSERT_LEVELDB_OK(Put("100", "v100"));
+    ASSERT_LEVELDB_OK(Put("999", "v999"));
     dbfull()->TEST_CompactMemTable();
-    ASSERT_OK(Delete("100"));
-    ASSERT_OK(Delete("999"));
+    ASSERT_LEVELDB_OK(Delete("100"));
+    ASSERT_LEVELDB_OK(Delete("999"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("0,1,1", FilesPerLevel());
 
@@ -1425,12 +1452,12 @@ TEST(DBTest, OverlapInLevel0) {
     //  files[0]  200 .. 900
     //  files[1]  300 .. 500
     // Note that files are sorted by smallest key.
-    ASSERT_OK(Put("300", "v300"));
-    ASSERT_OK(Put("500", "v500"));
+    ASSERT_LEVELDB_OK(Put("300", "v300"));
+    ASSERT_LEVELDB_OK(Put("500", "v500"));
     dbfull()->TEST_CompactMemTable();
-    ASSERT_OK(Put("200", "v200"));
-    ASSERT_OK(Put("600", "v600"));
-    ASSERT_OK(Put("900", "v900"));
+    ASSERT_LEVELDB_OK(Put("200", "v200"));
+    ASSERT_LEVELDB_OK(Put("600", "v600"));
+    ASSERT_LEVELDB_OK(Put("900", "v900"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("2,1,1", FilesPerLevel());
 
@@ -1442,23 +1469,23 @@ TEST(DBTest, OverlapInLevel0) {
     // Do a memtable compaction.  Before bug-fix, the compaction would
     // not detect the overlap with level-0 files and would incorrectly place
     // the deletion in a deeper level.
-    ASSERT_OK(Delete("600"));
+    ASSERT_LEVELDB_OK(Delete("600"));
     dbfull()->TEST_CompactMemTable();
     ASSERT_EQ("3", FilesPerLevel());
     ASSERT_EQ("NOT_FOUND", Get("600"));
   } while (ChangeOptions());
 }
 
-TEST(DBTest, L0_CompactionBug_Issue44_a) {
+TEST_F(DBTest, L0_CompactionBug_Issue44_a) {
   Reopen();
-  ASSERT_OK(Put("b", "v"));
+  ASSERT_LEVELDB_OK(Put("b", "v"));
   Reopen();
-  ASSERT_OK(Delete("b"));
-  ASSERT_OK(Delete("a"));
+  ASSERT_LEVELDB_OK(Delete("b"));
+  ASSERT_LEVELDB_OK(Delete("a"));
   Reopen();
-  ASSERT_OK(Delete("a"));
+  ASSERT_LEVELDB_OK(Delete("a"));
   Reopen();
-  ASSERT_OK(Put("a", "v"));
+  ASSERT_LEVELDB_OK(Put("a", "v"));
   Reopen();
   Reopen();
   ASSERT_EQ("(a->v)", Contents());
@@ -1466,7 +1493,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) {
   ASSERT_EQ("(a->v)", Contents());
 }
 
-TEST(DBTest, L0_CompactionBug_Issue44_b) {
+TEST_F(DBTest, L0_CompactionBug_Issue44_b) {
   Reopen();
   Put("", "");
   Reopen();
@@ -1492,26 +1519,26 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) {
   ASSERT_EQ("(->)(c->cv)", Contents());
 }
 
-TEST(DBTest, Fflush_Issue474) {
+TEST_F(DBTest, Fflush_Issue474) {
   static const int kNum = 100000;
   Random rnd(test::RandomSeed());
   for (int i = 0; i < kNum; i++) {
-    fflush(nullptr);
-    ASSERT_OK(Put(RandomKey(&rnd), RandomString(&rnd, 100)));
+    std::fflush(nullptr);
+    ASSERT_LEVELDB_OK(Put(RandomKey(&rnd), RandomString(&rnd, 100)));
   }
 }
 
-TEST(DBTest, ComparatorCheck) {
+TEST_F(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);
     }
   };
@@ -1524,29 +1551,29 @@ TEST(DBTest, ComparatorCheck) {
       << s.ToString();
 }
 
-TEST(DBTest, CustomComparator) {
+TEST_F(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
     }
 
    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] == ']')
+      EXPECT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size() - 1] == ']')
           << EscapeString(x);
       int val;
       char ignored;
-      ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1)
+      EXPECT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1)
           << EscapeString(x);
       return val;
     }
@@ -1558,8 +1585,8 @@ TEST(DBTest, CustomComparator) {
   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"));
-  ASSERT_OK(Put("[0x14]", "twenty"));
+  ASSERT_LEVELDB_OK(Put("[10]", "ten"));
+  ASSERT_LEVELDB_OK(Put("[0x14]", "twenty"));
   for (int i = 0; i < 2; i++) {
     ASSERT_EQ("ten", Get("[10]"));
     ASSERT_EQ("ten", Get("[0xa]"));
@@ -1573,14 +1600,14 @@ 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);
-      ASSERT_OK(Put(buf, buf));
+      std::snprintf(buf, sizeof(buf), "[%d]", i * 10);
+      ASSERT_LEVELDB_OK(Put(buf, buf));
     }
     Compact("[0]", "[1000000]");
   }
 }
 
-TEST(DBTest, ManualCompaction) {
+TEST_F(DBTest, ManualCompaction) {
   ASSERT_EQ(config::kMaxMemCompactLevel, 2)
       << "Need to update this test to match kMaxMemCompactLevel";
 
@@ -1614,8 +1641,8 @@ TEST(DBTest, ManualCompaction) {
   ASSERT_EQ("0,0,1", FilesPerLevel());
 }
 
-TEST(DBTest, DBOpen_Options) {
-  std::string dbname = test::TmpDir() + "/db_options_test";
+TEST_F(DBTest, DBOpen_Options) {
+  std::string dbname = testing::TempDir() + "db_options_test";
   DestroyDB(dbname, Options());
 
   // Does not exist, and create_if_missing == false: error
@@ -1629,7 +1656,7 @@ TEST(DBTest, DBOpen_Options) {
   // Does not exist, and create_if_missing == true: OK
   opts.create_if_missing = true;
   s = DB::Open(opts, dbname, &db);
-  ASSERT_OK(s);
+  ASSERT_LEVELDB_OK(s);
   ASSERT_TRUE(db != nullptr);
 
   delete db;
@@ -1646,50 +1673,50 @@ TEST(DBTest, DBOpen_Options) {
   opts.create_if_missing = true;
   opts.error_if_exists = false;
   s = DB::Open(opts, dbname, &db);
-  ASSERT_OK(s);
+  ASSERT_LEVELDB_OK(s);
   ASSERT_TRUE(db != nullptr);
 
   delete db;
   db = nullptr;
 }
 
-TEST(DBTest, DestroyEmptyDir) {
-  std::string dbname = test::TmpDir() + "/db_empty_dir";
+TEST_F(DBTest, DestroyEmptyDir) {
+  std::string dbname = testing::TempDir() + "db_empty_dir";
   TestEnv env(Env::Default());
-  env.DeleteDir(dbname);
+  env.RemoveDir(dbname);
   ASSERT_TRUE(!env.FileExists(dbname));
 
   Options opts;
   opts.env = &env;
 
-  ASSERT_OK(env.CreateDir(dbname));
+  ASSERT_LEVELDB_OK(env.CreateDir(dbname));
   ASSERT_TRUE(env.FileExists(dbname));
   std::vector<std::string> children;
-  ASSERT_OK(env.GetChildren(dbname, &children));
+  ASSERT_LEVELDB_OK(env.GetChildren(dbname, &children));
   // The stock Env's do not filter out '.' and '..' special files.
   ASSERT_EQ(2, children.size());
-  ASSERT_OK(DestroyDB(dbname, opts));
+  ASSERT_LEVELDB_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_LEVELDB_OK(env.CreateDir(dbname));
   ASSERT_TRUE(env.FileExists(dbname));
-  ASSERT_OK(env.GetChildren(dbname, &children));
+  ASSERT_LEVELDB_OK(env.GetChildren(dbname, &children));
   ASSERT_EQ(0, children.size());
-  ASSERT_OK(DestroyDB(dbname, opts));
+  ASSERT_LEVELDB_OK(DestroyDB(dbname, opts));
   ASSERT_TRUE(!env.FileExists(dbname));
 }
 
-TEST(DBTest, DestroyOpenDB) {
-  std::string dbname = test::TmpDir() + "/open_db_dir";
-  env_->DeleteDir(dbname);
+TEST_F(DBTest, DestroyOpenDB) {
+  std::string dbname = testing::TempDir() + "open_db_dir";
+  env_->RemoveDir(dbname);
   ASSERT_TRUE(!env_->FileExists(dbname));
 
   Options opts;
   opts.create_if_missing = true;
   DB* db = nullptr;
-  ASSERT_OK(DB::Open(opts, dbname, &db));
+  ASSERT_LEVELDB_OK(DB::Open(opts, dbname, &db));
   ASSERT_TRUE(db != nullptr);
 
   // Must fail to destroy an open db.
@@ -1701,23 +1728,23 @@ TEST(DBTest, DestroyOpenDB) {
   db = nullptr;
 
   // Should succeed destroying a closed db.
-  ASSERT_OK(DestroyDB(dbname, Options()));
+  ASSERT_LEVELDB_OK(DestroyDB(dbname, Options()));
   ASSERT_TRUE(!env_->FileExists(dbname));
 }
 
-TEST(DBTest, Locking) {
+TEST_F(DBTest, Locking) {
   DB* db2 = nullptr;
   Status s = DB::Open(CurrentOptions(), dbname_, &db2);
   ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db";
 }
 
 // Check that number of files does not grow when we are out of space
-TEST(DBTest, NoSpace) {
+TEST_F(DBTest, NoSpace) {
   Options options = CurrentOptions();
   options.env = env_;
   Reopen(&options);
 
-  ASSERT_OK(Put("foo", "v1"));
+  ASSERT_LEVELDB_OK(Put("foo", "v1"));
   ASSERT_EQ("v1", Get("foo"));
   Compact("a", "z");
   const int num_files = CountFiles();
@@ -1732,18 +1759,18 @@ TEST(DBTest, NoSpace) {
   ASSERT_LT(CountFiles(), num_files + 3);
 }
 
-TEST(DBTest, NonWritableFileSystem) {
+TEST_F(DBTest, NonWritableFileSystem) {
   Options options = CurrentOptions();
   options.write_buffer_size = 1000;
   options.env = env_;
   Reopen(&options);
-  ASSERT_OK(Put("foo", "v1"));
+  ASSERT_LEVELDB_OK(Put("foo", "v1"));
   // 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++) {
-    fprintf(stderr, "iter %d; errors %d\n", i, errors);
+    std::fprintf(stderr, "iter %d; errors %d\n", i, errors);
     if (!Put("foo", big).ok()) {
       errors++;
       DelayMilliseconds(100);
@@ -1753,7 +1780,7 @@ TEST(DBTest, NonWritableFileSystem) {
   env_->non_writable_.store(false, std::memory_order_release);
 }
 
-TEST(DBTest, WriteSyncError) {
+TEST_F(DBTest, WriteSyncError) {
   // Check that log sync errors cause the DB to disallow future writes.
 
   // (a) Cause log sync calls to fail
@@ -1764,7 +1791,7 @@ TEST(DBTest, WriteSyncError) {
 
   // (b) Normal write should succeed
   WriteOptions w;
-  ASSERT_OK(db_->Put(w, "k1", "v1"));
+  ASSERT_LEVELDB_OK(db_->Put(w, "k1", "v1"));
   ASSERT_EQ("v1", Get("k1"));
 
   // (c) Do a sync write; should fail
@@ -1784,7 +1811,7 @@ TEST(DBTest, WriteSyncError) {
   ASSERT_EQ("NOT_FOUND", Get("k3"));
 }
 
-TEST(DBTest, ManifestWriteError) {
+TEST_F(DBTest, ManifestWriteError) {
   // Test for the following problem:
   // (a) Compaction produces file F
   // (b) Log record containing F is written to MANIFEST file, but Sync() fails
@@ -1803,7 +1830,7 @@ TEST(DBTest, ManifestWriteError) {
     options.create_if_missing = true;
     options.error_if_exists = false;
     DestroyAndReopen(&options);
-    ASSERT_OK(Put("foo", "bar"));
+    ASSERT_LEVELDB_OK(Put("foo", "bar"));
     ASSERT_EQ("bar", Get("foo"));
 
     // Memtable compaction (will succeed)
@@ -1824,8 +1851,8 @@ TEST(DBTest, ManifestWriteError) {
   }
 }
 
-TEST(DBTest, MissingSSTFile) {
-  ASSERT_OK(Put("foo", "bar"));
+TEST_F(DBTest, MissingSSTFile) {
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   ASSERT_EQ("bar", Get("foo"));
 
   // Dump the memtable to disk.
@@ -1841,8 +1868,8 @@ TEST(DBTest, MissingSSTFile) {
   ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) << s.ToString();
 }
 
-TEST(DBTest, StillReadSST) {
-  ASSERT_OK(Put("foo", "bar"));
+TEST_F(DBTest, StillReadSST) {
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   ASSERT_EQ("bar", Get("foo"));
 
   // Dump the memtable to disk.
@@ -1857,18 +1884,18 @@ TEST(DBTest, StillReadSST) {
   ASSERT_EQ("bar", Get("foo"));
 }
 
-TEST(DBTest, FilesDeletedAfterCompaction) {
-  ASSERT_OK(Put("foo", "v2"));
+TEST_F(DBTest, FilesDeletedAfterCompaction) {
+  ASSERT_LEVELDB_OK(Put("foo", "v2"));
   Compact("a", "z");
   const int num_files = CountFiles();
   for (int i = 0; i < 10; i++) {
-    ASSERT_OK(Put("foo", "v2"));
+    ASSERT_LEVELDB_OK(Put("foo", "v2"));
     Compact("a", "z");
   }
   ASSERT_EQ(CountFiles(), num_files);
 }
 
-TEST(DBTest, BloomFilter) {
+TEST_F(DBTest, BloomFilter) {
   env_->count_random_reads_ = true;
   Options options = CurrentOptions();
   options.env = env_;
@@ -1879,11 +1906,11 @@ TEST(DBTest, BloomFilter) {
   // Populate multiple layers
   const int N = 10000;
   for (int i = 0; i < N; i++) {
-    ASSERT_OK(Put(Key(i), Key(i)));
+    ASSERT_LEVELDB_OK(Put(Key(i), Key(i)));
   }
   Compact("a", "z");
   for (int i = 0; i < N; i += 100) {
-    ASSERT_OK(Put(Key(i), Key(i)));
+    ASSERT_LEVELDB_OK(Put(Key(i), Key(i)));
   }
   dbfull()->TEST_CompactMemTable();
 
@@ -1896,7 +1923,7 @@ TEST(DBTest, BloomFilter) {
     ASSERT_EQ(Key(i), Get(Key(i)));
   }
   int reads = env_->random_read_counter_.Read();
-  fprintf(stderr, "%d present => %d reads\n", N, reads);
+  std::fprintf(stderr, "%d present => %d reads\n", N, reads);
   ASSERT_GE(reads, N);
   ASSERT_LE(reads, N + 2 * N / 100);
 
@@ -1906,7 +1933,7 @@ TEST(DBTest, BloomFilter) {
     ASSERT_EQ("NOT_FOUND", Get(Key(i) + ".missing"));
   }
   reads = env_->random_read_counter_.Read();
-  fprintf(stderr, "%d missing => %d reads\n", N, reads);
+  std::fprintf(stderr, "%d missing => %d reads\n", N, reads);
   ASSERT_LE(reads, 3 * N / 100);
 
   env_->delay_data_sync_.store(false, std::memory_order_release);
@@ -1939,7 +1966,7 @@ static void MTThreadBody(void* arg) {
   int id = t->id;
   DB* db = t->state->test->db_;
   int counter = 0;
-  fprintf(stderr, "... starting thread %d\n", id);
+  std::fprintf(stderr, "... starting thread %d\n", id);
   Random rnd(1000 + id);
   std::string value;
   char valbuf[1500];
@@ -1948,14 +1975,14 @@ static void MTThreadBody(void* arg) {
 
     int key = rnd.Uniform(kNumKeys);
     char keybuf[20];
-    snprintf(keybuf, sizeof(keybuf), "%016d", key);
+    std::snprintf(keybuf, sizeof(keybuf), "%016d", key);
 
     if (rnd.OneIn(2)) {
       // Write values of the form <key, my id, counter>.
       // We add some padding for force compactions.
-      snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", key, id,
-               static_cast<int>(counter));
-      ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf)));
+      std::snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", key, id,
+                    static_cast<int>(counter));
+      ASSERT_LEVELDB_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf)));
     } else {
       // Read a value and verify that it matches the pattern written above.
       Status s = db->Get(ReadOptions(), Slice(keybuf), &value);
@@ -1963,7 +1990,7 @@ static void MTThreadBody(void* arg) {
         // Key has not yet been written
       } else {
         // Check that the writer thread counter is >= the counter in the value
-        ASSERT_OK(s);
+        ASSERT_LEVELDB_OK(s);
         int k, w, c;
         ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value;
         ASSERT_EQ(k, key);
@@ -1975,12 +2002,12 @@ static void MTThreadBody(void* arg) {
     counter++;
   }
   t->state->thread_done[id].store(true, std::memory_order_release);
-  fprintf(stderr, "... stopping thread %d after %d ops\n", id, counter);
+  std::fprintf(stderr, "... stopping thread %d after %d ops\n", id, counter);
 }
 
 }  // namespace
 
-TEST(DBTest, MultiThreaded) {
+TEST_F(DBTest, MultiThreaded) {
   do {
     // Initialize state
     MTState mt;
@@ -2024,19 +2051,19 @@ class ModelDB : public DB {
   };
 
   explicit ModelDB(const Options& options) : options_(options) {}
-  ~ModelDB() {}
-  virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) {
+  ~ModelDB() override = default;
+  Status Put(const WriteOptions& o, const Slice& k, const Slice& v) override {
     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,64 +2074,64 @@ 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<const ModelSnapshot*>(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_;
-      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_;
     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 {
    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_;
@@ -2125,40 +2152,82 @@ static bool CompareIterators(int step, DB* model, DB* db,
   Iterator* dbiter = db->NewIterator(options);
   bool ok = true;
   int count = 0;
+  std::vector<std::string> seek_keys;
+  // Compare equality of all elements using Next(). Save some of the keys for
+  // comparing Seek equality.
   for (miter->SeekToFirst(), dbiter->SeekToFirst();
        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,
-              EscapeString(miter->key()).c_str(),
-              EscapeString(dbiter->key()).c_str());
+      std::fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", step,
+                   EscapeString(miter->key()).c_str(),
+                   EscapeString(dbiter->key()).c_str());
       ok = false;
       break;
     }
 
     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(),
-              EscapeString(miter->value()).c_str(),
-              EscapeString(miter->value()).c_str());
+      std::fprintf(stderr,
+                   "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n",
+                   step, EscapeString(miter->key()).c_str(),
+                   EscapeString(miter->value()).c_str(),
+                   EscapeString(miter->value()).c_str());
       ok = false;
+      break;
+    }
+
+    if (count % 10 == 0) {
+      seek_keys.push_back(miter->key().ToString());
     }
   }
 
   if (ok) {
     if (miter->Valid() != dbiter->Valid()) {
-      fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n",
-              step, miter->Valid(), dbiter->Valid());
+      std::fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n",
+                   step, miter->Valid(), dbiter->Valid());
       ok = false;
     }
   }
-  fprintf(stderr, "%d entries compared: ok=%d\n", count, ok);
+
+  if (ok) {
+    // Validate iterator equality when performing seeks.
+    for (auto kiter = seek_keys.begin(); ok && kiter != seek_keys.end();
+         ++kiter) {
+      miter->Seek(*kiter);
+      dbiter->Seek(*kiter);
+      if (!miter->Valid() || !dbiter->Valid()) {
+        std::fprintf(stderr, "step %d: Seek iterators invalid: %d vs. %d\n",
+                     step, miter->Valid(), dbiter->Valid());
+        ok = false;
+      }
+      if (miter->key().compare(dbiter->key()) != 0) {
+        std::fprintf(stderr, "step %d: Seek key mismatch: '%s' vs. '%s'\n",
+                     step, EscapeString(miter->key()).c_str(),
+                     EscapeString(dbiter->key()).c_str());
+        ok = false;
+        break;
+      }
+
+      if (miter->value().compare(dbiter->value()) != 0) {
+        std::fprintf(
+            stderr,
+            "step %d: Seek value mismatch for key '%s': '%s' vs. '%s'\n", step,
+            EscapeString(miter->key()).c_str(),
+            EscapeString(miter->value()).c_str(),
+            EscapeString(miter->value()).c_str());
+        ok = false;
+        break;
+      }
+    }
+  }
+
+  std::fprintf(stderr, "%d entries compared: ok=%d\n", count, ok);
   delete miter;
   delete dbiter;
   return ok;
 }
 
-TEST(DBTest, Randomized) {
+TEST_F(DBTest, Randomized) {
   Random rnd(test::RandomSeed());
   do {
     ModelDB model(CurrentOptions());
@@ -2168,7 +2237,7 @@ TEST(DBTest, Randomized) {
     std::string k, v;
     for (int step = 0; step < N; step++) {
       if (step % 100 == 0) {
-        fprintf(stderr, "Step %d of %d\n", step, N);
+        std::fprintf(stderr, "Step %d of %d\n", step, N);
       }
       // TODO(sanjay): Test Get() works
       int p = rnd.Uniform(100);
@@ -2176,13 +2245,13 @@ TEST(DBTest, Randomized) {
         k = RandomKey(&rnd);
         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));
+        ASSERT_LEVELDB_OK(model.Put(WriteOptions(), k, v));
+        ASSERT_LEVELDB_OK(db_->Put(WriteOptions(), k, v));
 
       } else if (p < 90) {  // Delete
         k = RandomKey(&rnd);
-        ASSERT_OK(model.Delete(WriteOptions(), k));
-        ASSERT_OK(db_->Delete(WriteOptions(), k));
+        ASSERT_LEVELDB_OK(model.Delete(WriteOptions(), k));
+        ASSERT_LEVELDB_OK(db_->Delete(WriteOptions(), k));
 
       } else {  // Multi-element batch
         WriteBatch b;
@@ -2201,8 +2270,8 @@ TEST(DBTest, Randomized) {
             b.Delete(k);
           }
         }
-        ASSERT_OK(model.Write(WriteOptions(), &b));
-        ASSERT_OK(db_->Write(WriteOptions(), &b));
+        ASSERT_LEVELDB_OK(model.Write(WriteOptions(), &b));
+        ASSERT_LEVELDB_OK(db_->Write(WriteOptions(), &b));
       }
 
       if ((step % 100) == 0) {
@@ -2228,19 +2297,21 @@ TEST(DBTest, Randomized) {
 
 std::string MakeKey(unsigned int num) {
   char buf[30];
-  snprintf(buf, sizeof(buf), "%016u", num);
+  std::snprintf(buf, sizeof(buf), "%016u", num);
   return std::string(buf);
 }
 
-void BM_LogAndApply(int iters, int num_base_files) {
-  std::string dbname = test::TmpDir() + "/leveldb_test_benchmark";
+static void BM_LogAndApply(benchmark::State& state) {
+  const int num_base_files = state.range(0);
+
+  std::string dbname = testing::TempDir() + "leveldb_test_benchmark";
   DestroyDB(dbname, Options());
 
   DB* db = nullptr;
   Options opts;
   opts.create_if_missing = true;
   Status s = DB::Open(opts, dbname, &db);
-  ASSERT_OK(s);
+  ASSERT_LEVELDB_OK(s);
   ASSERT_TRUE(db != nullptr);
 
   delete db;
@@ -2255,7 +2326,7 @@ void BM_LogAndApply(int iters, int num_base_files) {
   Options options;
   VersionSet vset(dbname, &options, nullptr, &cmp);
   bool save_manifest;
-  ASSERT_OK(vset.Recover(&save_manifest));
+  ASSERT_LEVELDB_OK(vset.Recover(&save_manifest));
   VersionEdit vbase;
   uint64_t fnum = 1;
   for (int i = 0; i < num_base_files; i++) {
@@ -2263,13 +2334,13 @@ void BM_LogAndApply(int iters, int num_base_files) {
     InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
     vbase.AddFile(2, fnum++, 1 /* file size */, start, limit);
   }
-  ASSERT_OK(vset.LogAndApply(&vbase, &mu));
+  ASSERT_LEVELDB_OK(vset.LogAndApply(&vbase, &mu));
 
   uint64_t start_micros = env->NowMicros();
 
-  for (int i = 0; i < iters; i++) {
+  for (auto st : state) {
     VersionEdit vedit;
-    vedit.DeleteFile(2, fnum);
+    vedit.RemoveFile(2, fnum);
     InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
     InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
     vedit.AddFile(2, fnum++, 1 /* file size */, start, limit);
@@ -2278,22 +2349,18 @@ void BM_LogAndApply(int iters, int num_base_files) {
   uint64_t stop_micros = env->NowMicros();
   unsigned int us = stop_micros - start_micros;
   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);
+  std::snprintf(buf, sizeof(buf), "%d", num_base_files);
+  std::fprintf(stderr,
+               "BM_LogAndApply/%-6s   %8" PRIu64
+               " iters : %9u us (%7.0f us / iter)\n",
+               buf, state.iterations(), us, ((float)us) / state.iterations());
 }
 
+BENCHMARK(BM_LogAndApply)->Arg(1)->Arg(100)->Arg(10000)->Arg(100000);
 }  // namespace leveldb
 
 int main(int argc, char** argv) {
-  if (argc > 1 && std::string(argv[1]) == "--benchmark") {
-    leveldb::BM_LogAndApply(1000, 1);
-    leveldb::BM_LogAndApply(1000, 100);
-    leveldb::BM_LogAndApply(1000, 10000);
-    leveldb::BM_LogAndApply(100, 100000);
-    return 0;
-  }
-
-  return leveldb::test::RunAllTests();
+  testing::InitGoogleTest(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  return RUN_ALL_TESTS();
 }
index 69e8dc6..2a5749f 100644 (file)
@@ -4,7 +4,8 @@
 
 #include "db/dbformat.h"
 
-#include <stdio.h>
+#include <cstdio>
+#include <sstream>
 
 #include "port/port.h"
 #include "util/coding.h"
@@ -23,25 +24,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<int>(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 {
@@ -130,7 +126,7 @@ LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
   start_ = dst;
   dst = EncodeVarint32(dst, usize + 8);
   kstart_ = dst;
-  memcpy(dst, user_key.data(), usize);
+  std::memcpy(dst, user_key.data(), usize);
   dst += usize;
   EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
   dst += 8;
index 013028a..a1c30ed 100644 (file)
@@ -5,7 +5,9 @@
 #ifndef STORAGE_LEVELDB_DB_DBFORMAT_H_
 #define STORAGE_LEVELDB_DB_DBFORMAT_H_
 
-#include <stdio.h>
+#include <cstddef>
+#include <cstdint>
+#include <string>
 
 #include "leveldb/comparator.h"
 #include "leveldb/db.h"
@@ -103,11 +105,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 +123,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
@@ -139,7 +141,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_;
@@ -167,11 +173,11 @@ inline bool ParseInternalKey(const Slice& internal_key,
   const size_t n = internal_key.size();
   if (n < 8) return false;
   uint64_t num = DecodeFixed64(internal_key.data() + n - 8);
-  unsigned char c = num & 0xff;
+  uint8_t c = num & 0xff;
   result->sequence = num >> 8;
   result->type = static_cast<ValueType>(c);
   result->user_key = Slice(internal_key.data(), n - 8);
-  return (c <= static_cast<unsigned char>(kTypeValue));
+  return (c <= static_cast<uint8_t>(kTypeValue));
 }
 
 // A helper class useful for DBImpl::Get()
index 87e6aae..4a11c4a 100644 (file)
@@ -3,8 +3,9 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include "db/dbformat.h"
+
+#include "gtest/gtest.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
@@ -41,8 +42,6 @@ static void TestKey(const std::string& key, uint64_t seq, ValueType vt) {
   ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded));
 }
 
-class FormatTest {};
-
 TEST(FormatTest, InternalKey_EncodeDecode) {
   const char* keys[] = {"", "k", "hello", "longggggggggggggggggggggg"};
   const uint64_t seq[] = {1,
@@ -65,6 +64,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),
@@ -106,6 +111,23 @@ 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(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 9d22d58..6085475 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "leveldb/dumpfile.h"
 
-#include <stdio.h>
+#include <cstdio>
 
 #include "db/dbformat.h"
 #include "db/filename.h"
@@ -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";
index 7088ea7..6eebafa 100644 (file)
@@ -9,6 +9,7 @@
 #include <map>
 #include <set>
 
+#include "gtest/gtest.h"
 #include "db/db_impl.h"
 #include "db/filename.h"
 #include "db/log_format.h"
@@ -22,7 +23,6 @@
 #include "port/thread_annotations.h"
 #include "util/logging.h"
 #include "util/mutexlock.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
@@ -72,7 +72,7 @@ Status Truncate(const std::string& filename, uint64_t length) {
       if (s.ok()) {
         s = env->RenameFile(tmp_name, filename);
       } else {
-        env->DeleteFile(tmp_name);
+        env->RemoveFile(tmp_name);
       }
     }
   }
@@ -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,17 +128,17 @@ 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 RemoveFile(const std::string& f) override;
+  Status RenameFile(const std::string& s, const std::string& t) override;
 
   void WritableFileClosed(const FileState& state);
   Status DropUnsyncedFileData();
-  Status DeleteFilesCreatedAfterLastDirSync();
+  Status RemoveFilesCreatedAfterLastDirSync();
   void DirWasSynced();
   bool IsFileCreatedSinceLastDirSync(const std::string& filename);
   void ResetState();
@@ -268,10 +268,11 @@ Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname,
 Status FaultInjectionTestEnv::DropUnsyncedFileData() {
   Status s;
   MutexLock l(&mutex_);
-  for (std::map<std::string, FileState>::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();
     }
@@ -297,9 +298,9 @@ void FaultInjectionTestEnv::UntrackFile(const std::string& f) {
   new_files_since_last_dir_sync_.erase(f);
 }
 
-Status FaultInjectionTestEnv::DeleteFile(const std::string& f) {
-  Status s = EnvWrapper::DeleteFile(f);
-  ASSERT_OK(s);
+Status FaultInjectionTestEnv::RemoveFile(const std::string& f) {
+  Status s = EnvWrapper::RemoveFile(f);
+  EXPECT_LEVELDB_OK(s);
   if (s.ok()) {
     UntrackFile(f);
   }
@@ -334,18 +335,20 @@ void FaultInjectionTestEnv::ResetState() {
   SetFilesystemActive(true);
 }
 
-Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
-  // Because DeleteFile access this container make a copy to avoid deadlock
+Status FaultInjectionTestEnv::RemoveFilesCreatedAfterLastDirSync() {
+  // Because RemoveFile access this container make a copy to avoid deadlock
   mutex_.Lock();
   std::set<std::string> new_files(new_files_since_last_dir_sync_.begin(),
                                   new_files_since_last_dir_sync_.end());
   mutex_.Unlock();
-  Status s;
-  std::set<std::string>::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 remove_status = RemoveFile(new_file);
+    if (!remove_status.ok() && status.ok()) {
+      status = std::move(remove_status);
+    }
   }
-  return s;
+  return status;
 }
 
 void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
@@ -358,7 +361,7 @@ Status FileState::DropUnsyncedData() const {
   return Truncate(filename_, sync_pos);
 }
 
-class FaultInjectionTest {
+class FaultInjectionTest : public testing::Test {
  public:
   enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR };
   enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES };
@@ -373,7 +376,7 @@ class FaultInjectionTest {
       : env_(new FaultInjectionTestEnv),
         tiny_cache_(NewLRUCache(100)),
         db_(nullptr) {
-    dbname_ = test::TmpDir() + "/fault_test";
+    dbname_ = testing::TempDir() + "fault_test";
     DestroyDB(dbname_, Options());  // Destroy any db from earlier run
     options_.reuse_logs = true;
     options_.env = env_;
@@ -399,7 +402,7 @@ class FaultInjectionTest {
       batch.Clear();
       batch.Put(key, Value(i, &value_space));
       WriteOptions options;
-      ASSERT_OK(db_->Write(options, &batch));
+      ASSERT_LEVELDB_OK(db_->Write(options, &batch));
     }
   }
 
@@ -421,10 +424,10 @@ class FaultInjectionTest {
       s = ReadValue(i, &val);
       if (expected == VAL_EXPECT_NO_ERROR) {
         if (s.ok()) {
-          ASSERT_EQ(value_space, val);
+          EXPECT_EQ(value_space, val);
         }
       } else if (s.ok()) {
-        fprintf(stderr, "Expected an error at %d, but was OK\n", i);
+        std::fprintf(stderr, "Expected an error at %d, but was OK\n", i);
         s = Status::IOError(dbname_, "Expected value error:");
       } else {
         s = Status::OK();  // An expected error
@@ -436,7 +439,7 @@ class FaultInjectionTest {
   // Return the ith key
   Slice Key(int i, std::string* storage) const {
     char buf[100];
-    snprintf(buf, sizeof(buf), "%016d", i);
+    std::snprintf(buf, sizeof(buf), "%016d", i);
     storage->assign(buf, strlen(buf));
     return Slice(*storage);
   }
@@ -462,7 +465,7 @@ class FaultInjectionTest {
   void DeleteAllData() {
     Iterator* iter = db_->NewIterator(ReadOptions());
     for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
-      ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
+      ASSERT_LEVELDB_OK(db_->Delete(WriteOptions(), iter->key()));
     }
 
     delete iter;
@@ -471,10 +474,10 @@ class FaultInjectionTest {
   void ResetDBState(ResetMethod reset_method) {
     switch (reset_method) {
       case RESET_DROP_UNSYNCED_DATA:
-        ASSERT_OK(env_->DropUnsyncedFileData());
+        ASSERT_LEVELDB_OK(env_->DropUnsyncedFileData());
         break;
       case RESET_DELETE_UNSYNCED_FILES:
-        ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
+        ASSERT_LEVELDB_OK(env_->RemoveFilesCreatedAfterLastDirSync());
         break;
       default:
         assert(false);
@@ -493,10 +496,11 @@ class FaultInjectionTest {
     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_LEVELDB_OK(OpenDB());
+    ASSERT_LEVELDB_OK(
+        Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR));
+    ASSERT_LEVELDB_OK(Verify(num_pre_sync, num_post_sync,
+                             FaultInjectionTest::VAL_EXPECT_ERROR));
   }
 
   void NoWriteTestPreFault() {}
@@ -504,12 +508,12 @@ class FaultInjectionTest {
   void NoWriteTestReopenWithFault(ResetMethod reset_method) {
     CloseDB();
     ResetDBState(reset_method);
-    ASSERT_OK(OpenDB());
+    ASSERT_LEVELDB_OK(OpenDB());
   }
 
   void DoTest() {
     Random rnd(0);
-    ASSERT_OK(OpenDB());
+    ASSERT_LEVELDB_OK(OpenDB());
     for (size_t idx = 0; idx < kNumIterations; idx++) {
       int num_pre_sync = rnd.Uniform(kMaxNumValues);
       int num_post_sync = rnd.Uniform(kMaxNumValues);
@@ -533,16 +537,19 @@ class FaultInjectionTest {
   }
 };
 
-TEST(FaultInjectionTest, FaultTestNoLogReuse) {
+TEST_F(FaultInjectionTest, FaultTestNoLogReuse) {
   ReuseLogs(false);
   DoTest();
 }
 
-TEST(FaultInjectionTest, FaultTestWithLogReuse) {
+TEST_F(FaultInjectionTest, FaultTestWithLogReuse) {
   ReuseLogs(true);
   DoTest();
 }
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 85de45c..e526249 100644 (file)
@@ -4,8 +4,8 @@
 
 #include "db/filename.h"
 
-#include <ctype.h>
-#include <stdio.h>
+#include <cassert>
+#include <cstdio>
 
 #include "db/dbformat.h"
 #include "leveldb/env.h"
@@ -20,8 +20,8 @@ Status WriteStringToFileSync(Env* env, const Slice& data,
 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<unsigned long long>(number), suffix);
+  std::snprintf(buf, sizeof(buf), "/%06llu.%s",
+                static_cast<unsigned long long>(number), suffix);
   return dbname + buf;
 }
 
@@ -43,8 +43,8 @@ std::string SSTTableFileName(const std::string& dbname, uint64_t number) {
 std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
   assert(number > 0);
   char buf[100];
-  snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",
-           static_cast<unsigned long long>(number));
+  std::snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",
+                static_cast<unsigned long long>(number));
   return dbname + buf;
 }
 
@@ -133,7 +133,7 @@ Status SetCurrentFile(Env* env, const std::string& dbname,
     s = env->RenameFile(tmp, CurrentFileName(dbname));
   }
   if (!s.ok()) {
-    env->DeleteFile(tmp);
+    env->RemoveFile(tmp);
   }
   return s;
 }
index 524e813..563c6d8 100644 (file)
@@ -7,8 +7,7 @@
 #ifndef STORAGE_LEVELDB_DB_FILENAME_H_
 #define STORAGE_LEVELDB_DB_FILENAME_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <string>
 
 #include "leveldb/slice.h"
index 952f320..f291d72 100644 (file)
@@ -4,15 +4,13 @@
 
 #include "db/filename.h"
 
+#include "gtest/gtest.h"
 #include "db/dbformat.h"
 #include "port/port.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
-class FileNameTest {};
-
 TEST(FileNameTest, Parse) {
   Slice db;
   FileType type;
@@ -128,4 +126,7 @@ TEST(FileNameTest, Construction) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index b21cf8e..95ee897 100644 (file)
@@ -2,7 +2,7 @@
 // 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 <stdio.h>
+#include <cstdio>
 
 #include "leveldb/dumpfile.h"
 #include "leveldb/env.h"
@@ -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) {
@@ -28,7 +28,7 @@ bool HandleDumpCommand(Env* env, char** files, int num) {
   for (int i = 0; i < num; i++) {
     Status s = DumpFile(env, files[i], &printer);
     if (!s.ok()) {
-      fprintf(stderr, "%s\n", s.ToString().c_str());
+      std::fprintf(stderr, "%s\n", s.ToString().c_str());
       ok = false;
     }
   }
@@ -39,9 +39,10 @@ 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");
+  std::fprintf(
+      stderr,
+      "Usage: leveldbutil command...\n"
+      "   dump files...         -- dump contents of specified files\n");
 }
 
 int main(int argc, char** argv) {
index f472723..9880279 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "db/log_reader.h"
 
-#include <stdio.h>
+#include <cstdio>
 
 #include "leveldb/env.h"
 #include "util/coding.h"
@@ -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)
@@ -160,7 +160,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
 
       default: {
         char buf[40];
-        snprintf(buf, sizeof(buf), "unknown record type %u", record_type);
+        std::snprintf(buf, sizeof(buf), "unknown record type %u", record_type);
         ReportCorruption(
             (fragment.size() + (in_fragmented_record ? scratch->size() : 0)),
             buf);
index 001da89..75d53f7 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_DB_LOG_READER_H_
 #define STORAGE_LEVELDB_DB_LOG_READER_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "db/log_format.h"
 #include "leveldb/slice.h"
index 809c418..346b19c 100644 (file)
@@ -2,13 +2,13 @@
 // 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 "gtest/gtest.h"
 #include "db/log_reader.h"
 #include "db/log_writer.h"
 #include "leveldb/env.h"
 #include "util/coding.h"
 #include "util/crc32c.h"
 #include "util/random.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 namespace log {
@@ -27,7 +27,7 @@ static std::string BigString(const std::string& partial_string, size_t n) {
 // Construct a string from a number
 static std::string NumberString(int n) {
   char buf[50];
-  snprintf(buf, sizeof(buf), "%d.", n);
+  std::snprintf(buf, sizeof(buf), "%d.", n);
   return std::string(buf);
 }
 
@@ -36,7 +36,7 @@ static std::string RandomSkewedString(int i, Random* rnd) {
   return BigString(NumberString(i), rnd->Skewed(17));
 }
 
-class LogTest {
+class LogTest : public testing::Test {
  public:
   LogTest()
       : reading_(false),
@@ -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,8 +176,8 @@ class LogTest {
    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";
+    Status Read(size_t n, Slice* result, char* scratch) override {
+      EXPECT_TRUE(!returned_partial_) << "must not Read() after eof/error";
 
       if (force_error_) {
         force_error_ = false;
@@ -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());
     }
@@ -258,9 +258,9 @@ uint64_t LogTest::initial_offset_last_record_offsets_[] = {
 int LogTest::num_initial_offset_records_ =
     sizeof(LogTest::initial_offset_last_record_offsets_) / sizeof(uint64_t);
 
-TEST(LogTest, Empty) { ASSERT_EQ("EOF", Read()); }
+TEST_F(LogTest, Empty) { ASSERT_EQ("EOF", Read()); }
 
-TEST(LogTest, ReadWrite) {
+TEST_F(LogTest, ReadWrite) {
   Write("foo");
   Write("bar");
   Write("");
@@ -273,7 +273,7 @@ TEST(LogTest, ReadWrite) {
   ASSERT_EQ("EOF", Read());  // Make sure reads at eof work
 }
 
-TEST(LogTest, ManyBlocks) {
+TEST_F(LogTest, ManyBlocks) {
   for (int i = 0; i < 100000; i++) {
     Write(NumberString(i));
   }
@@ -283,7 +283,7 @@ TEST(LogTest, ManyBlocks) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, Fragmentation) {
+TEST_F(LogTest, Fragmentation) {
   Write("small");
   Write(BigString("medium", 50000));
   Write(BigString("large", 100000));
@@ -293,7 +293,7 @@ TEST(LogTest, Fragmentation) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, MarginalTrailer) {
+TEST_F(LogTest, MarginalTrailer) {
   // Make a trailer that is exactly the same length as an empty record.
   const int n = kBlockSize - 2 * kHeaderSize;
   Write(BigString("foo", n));
@@ -306,7 +306,7 @@ TEST(LogTest, MarginalTrailer) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, MarginalTrailer2) {
+TEST_F(LogTest, MarginalTrailer2) {
   // Make a trailer that is exactly the same length as an empty record.
   const int n = kBlockSize - 2 * kHeaderSize;
   Write(BigString("foo", n));
@@ -319,7 +319,7 @@ TEST(LogTest, MarginalTrailer2) {
   ASSERT_EQ("", ReportMessage());
 }
 
-TEST(LogTest, ShortTrailer) {
+TEST_F(LogTest, ShortTrailer) {
   const int n = kBlockSize - 2 * kHeaderSize + 4;
   Write(BigString("foo", n));
   ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
@@ -331,7 +331,7 @@ TEST(LogTest, ShortTrailer) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, AlignedEof) {
+TEST_F(LogTest, AlignedEof) {
   const int n = kBlockSize - 2 * kHeaderSize + 4;
   Write(BigString("foo", n));
   ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
@@ -339,7 +339,7 @@ TEST(LogTest, AlignedEof) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, OpenForAppend) {
+TEST_F(LogTest, OpenForAppend) {
   Write("hello");
   ReopenForAppend();
   Write("world");
@@ -348,7 +348,7 @@ TEST(LogTest, OpenForAppend) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, RandomRead) {
+TEST_F(LogTest, RandomRead) {
   const int N = 500;
   Random write_rnd(301);
   for (int i = 0; i < N; i++) {
@@ -363,7 +363,7 @@ TEST(LogTest, RandomRead) {
 
 // Tests of all the error paths in log_reader.cc follow:
 
-TEST(LogTest, ReadError) {
+TEST_F(LogTest, ReadError) {
   Write("foo");
   ForceError();
   ASSERT_EQ("EOF", Read());
@@ -371,7 +371,7 @@ TEST(LogTest, ReadError) {
   ASSERT_EQ("OK", MatchError("read error"));
 }
 
-TEST(LogTest, BadRecordType) {
+TEST_F(LogTest, BadRecordType) {
   Write("foo");
   // Type is stored in header[6]
   IncrementByte(6, 100);
@@ -381,7 +381,7 @@ TEST(LogTest, BadRecordType) {
   ASSERT_EQ("OK", MatchError("unknown record type"));
 }
 
-TEST(LogTest, TruncatedTrailingRecordIsIgnored) {
+TEST_F(LogTest, TruncatedTrailingRecordIsIgnored) {
   Write("foo");
   ShrinkSize(4);  // Drop all payload as well as a header byte
   ASSERT_EQ("EOF", Read());
@@ -390,7 +390,7 @@ TEST(LogTest, TruncatedTrailingRecordIsIgnored) {
   ASSERT_EQ("", ReportMessage());
 }
 
-TEST(LogTest, BadLength) {
+TEST_F(LogTest, BadLength) {
   const int kPayloadSize = kBlockSize - kHeaderSize;
   Write(BigString("bar", kPayloadSize));
   Write("foo");
@@ -401,7 +401,7 @@ TEST(LogTest, BadLength) {
   ASSERT_EQ("OK", MatchError("bad record length"));
 }
 
-TEST(LogTest, BadLengthAtEndIsIgnored) {
+TEST_F(LogTest, BadLengthAtEndIsIgnored) {
   Write("foo");
   ShrinkSize(1);
   ASSERT_EQ("EOF", Read());
@@ -409,7 +409,7 @@ TEST(LogTest, BadLengthAtEndIsIgnored) {
   ASSERT_EQ("", ReportMessage());
 }
 
-TEST(LogTest, ChecksumMismatch) {
+TEST_F(LogTest, ChecksumMismatch) {
   Write("foo");
   IncrementByte(0, 10);
   ASSERT_EQ("EOF", Read());
@@ -417,7 +417,7 @@ TEST(LogTest, ChecksumMismatch) {
   ASSERT_EQ("OK", MatchError("checksum mismatch"));
 }
 
-TEST(LogTest, UnexpectedMiddleType) {
+TEST_F(LogTest, UnexpectedMiddleType) {
   Write("foo");
   SetByte(6, kMiddleType);
   FixChecksum(0, 3);
@@ -426,7 +426,7 @@ TEST(LogTest, UnexpectedMiddleType) {
   ASSERT_EQ("OK", MatchError("missing start"));
 }
 
-TEST(LogTest, UnexpectedLastType) {
+TEST_F(LogTest, UnexpectedLastType) {
   Write("foo");
   SetByte(6, kLastType);
   FixChecksum(0, 3);
@@ -435,7 +435,7 @@ TEST(LogTest, UnexpectedLastType) {
   ASSERT_EQ("OK", MatchError("missing start"));
 }
 
-TEST(LogTest, UnexpectedFullType) {
+TEST_F(LogTest, UnexpectedFullType) {
   Write("foo");
   Write("bar");
   SetByte(6, kFirstType);
@@ -446,7 +446,7 @@ TEST(LogTest, UnexpectedFullType) {
   ASSERT_EQ("OK", MatchError("partial record without end"));
 }
 
-TEST(LogTest, UnexpectedFirstType) {
+TEST_F(LogTest, UnexpectedFirstType) {
   Write("foo");
   Write(BigString("bar", 100000));
   SetByte(6, kFirstType);
@@ -457,7 +457,7 @@ TEST(LogTest, UnexpectedFirstType) {
   ASSERT_EQ("OK", MatchError("partial record without end"));
 }
 
-TEST(LogTest, MissingLastIsIgnored) {
+TEST_F(LogTest, MissingLastIsIgnored) {
   Write(BigString("bar", kBlockSize));
   // Remove the LAST block, including header.
   ShrinkSize(14);
@@ -466,7 +466,7 @@ TEST(LogTest, MissingLastIsIgnored) {
   ASSERT_EQ(0, DroppedBytes());
 }
 
-TEST(LogTest, PartialLastIsIgnored) {
+TEST_F(LogTest, PartialLastIsIgnored) {
   Write(BigString("bar", kBlockSize));
   // Cause a bad record length in the LAST block.
   ShrinkSize(1);
@@ -475,7 +475,7 @@ TEST(LogTest, PartialLastIsIgnored) {
   ASSERT_EQ(0, DroppedBytes());
 }
 
-TEST(LogTest, SkipIntoMultiRecord) {
+TEST_F(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)
@@ -491,7 +491,7 @@ TEST(LogTest, SkipIntoMultiRecord) {
   ASSERT_EQ("EOF", Read());
 }
 
-TEST(LogTest, ErrorJoinsRecords) {
+TEST_F(LogTest, ErrorJoinsRecords) {
   // Consider two fragmented records:
   //    first(R1) last(R1) first(R2) last(R2)
   // where the middle two fragments disappear.  We do not want
@@ -514,47 +514,50 @@ TEST(LogTest, ErrorJoinsRecords) {
   ASSERT_GE(dropped, 2 * kBlockSize);
 }
 
-TEST(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); }
+TEST_F(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); }
 
-TEST(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); }
+TEST_F(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); }
 
-TEST(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); }
+TEST_F(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); }
 
-TEST(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); }
+TEST_F(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); }
 
-TEST(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); }
+TEST_F(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); }
 
-TEST(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); }
+TEST_F(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); }
 
-TEST(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); }
+TEST_F(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); }
 
-TEST(LogTest, ReadFourthFirstBlockTrailer) {
+TEST_F(LogTest, ReadFourthFirstBlockTrailer) {
   CheckInitialOffsetRecord(log::kBlockSize - 4, 3);
 }
 
-TEST(LogTest, ReadFourthMiddleBlock) {
+TEST_F(LogTest, ReadFourthMiddleBlock) {
   CheckInitialOffsetRecord(log::kBlockSize + 1, 3);
 }
 
-TEST(LogTest, ReadFourthLastBlock) {
+TEST_F(LogTest, ReadFourthLastBlock) {
   CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3);
 }
 
-TEST(LogTest, ReadFourthStart) {
+TEST_F(LogTest, ReadFourthStart) {
   CheckInitialOffsetRecord(
       2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
       3);
 }
 
-TEST(LogTest, ReadInitialOffsetIntoBlockPadding) {
+TEST_F(LogTest, ReadInitialOffsetIntoBlockPadding) {
   CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5);
 }
 
-TEST(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); }
+TEST_F(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); }
 
-TEST(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); }
+TEST_F(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); }
 
 }  // namespace log
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 5e83f6a..ad66bfb 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "db/log_writer.h"
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "leveldb/env.h"
 #include "util/coding.h"
@@ -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();
index c0a2114..ad36794 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_
 #define STORAGE_LEVELDB_DB_LOG_WRITER_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "db/log_format.h"
 #include "leveldb/slice.h"
index c91405c..f42774d 100644 (file)
@@ -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_); }
@@ -87,12 +88,12 @@ void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key,
                              val_size;
   char* buf = arena_.Allocate(encoded_len);
   char* p = EncodeVarint32(buf, internal_key_size);
-  memcpy(p, key.data(), key_size);
+  std::memcpy(p, key.data(), key_size);
   p += key_size;
   EncodeFixed64(p, (s << 8) | type);
   p += 8;
   p = EncodeVarint32(p, val_size);
-  memcpy(p, value.data(), val_size);
+  std::memcpy(p, value.data(), val_size);
   assert(p + val_size == buf + encoded_len);
   table_.Insert(buf);
 }
index 547a959..3db817e 100644 (file)
@@ -2,6 +2,7 @@
 // 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 "gtest/gtest.h"
 #include "db/db_impl.h"
 #include "db/filename.h"
 #include "db/version_set.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 {
+class RecoveryTest : public testing::Test {
  public:
   RecoveryTest() : env_(Env::Default()), db_(nullptr) {
-    dbname_ = test::TmpDir() + "/recovery_test";
+    dbname_ = testing::TempDir() + "/recovery_test";
     DestroyDB(dbname_, Options());
     Open();
   }
@@ -63,7 +63,7 @@ class RecoveryTest {
   }
 
   void Open(Options* options = nullptr) {
-    ASSERT_OK(OpenWithStatus(options));
+    ASSERT_LEVELDB_OK(OpenWithStatus(options));
     ASSERT_EQ(1, NumLogs());
   }
 
@@ -84,7 +84,8 @@ class RecoveryTest {
 
   std::string ManifestFileName() {
     std::string current;
-    ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), &current));
+    EXPECT_LEVELDB_OK(
+        ReadFileToString(env_, CurrentFileName(dbname_), &current));
     size_t len = current.size();
     if (len > 0 && current[len - 1] == '\n') {
       current.resize(len - 1);
@@ -94,24 +95,26 @@ class RecoveryTest {
 
   std::string LogName(uint64_t number) { return LogFileName(dbname_, number); }
 
-  size_t DeleteLogFiles() {
+  size_t RemoveLogFiles() {
     // Linux allows unlinking open files, but Windows does not.
     // Closing the db allows for file deletion.
     Close();
     std::vector<uint64_t> logs = GetFiles(kLogFile);
     for (size_t i = 0; i < logs.size(); i++) {
-      ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]);
+      EXPECT_LEVELDB_OK(env_->RemoveFile(LogName(logs[i]))) << LogName(logs[i]);
     }
     return logs.size();
   }
 
-  void DeleteManifestFile() { ASSERT_OK(env_->DeleteFile(ManifestFileName())); }
+  void RemoveManifestFile() {
+    ASSERT_LEVELDB_OK(env_->RemoveFile(ManifestFileName()));
+  }
 
   uint64_t FirstLogFile() { return GetFiles(kLogFile)[0]; }
 
   std::vector<uint64_t> GetFiles(FileType t) {
     std::vector<std::string> filenames;
-    ASSERT_OK(env_->GetChildren(dbname_, &filenames));
+    EXPECT_LEVELDB_OK(env_->GetChildren(dbname_, &filenames));
     std::vector<uint64_t> result;
     for (size_t i = 0; i < filenames.size(); i++) {
       uint64_t number;
@@ -129,7 +132,7 @@ class RecoveryTest {
 
   uint64_t FileSize(const std::string& fname) {
     uint64_t result;
-    ASSERT_OK(env_->GetFileSize(fname, &result)) << fname;
+    EXPECT_LEVELDB_OK(env_->GetFileSize(fname, &result)) << fname;
     return result;
   }
 
@@ -139,13 +142,13 @@ class RecoveryTest {
   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));
+    ASSERT_LEVELDB_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());
+    ASSERT_LEVELDB_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch)));
+    ASSERT_LEVELDB_OK(file->Flush());
     delete file;
   }
 
@@ -155,12 +158,13 @@ class RecoveryTest {
   DB* db_;
 };
 
-TEST(RecoveryTest, ManifestReused) {
+TEST_F(RecoveryTest, ManifestReused) {
   if (!CanAppend()) {
-    fprintf(stderr, "skipping test because env does not support appending\n");
+    std::fprintf(stderr,
+                 "skipping test because env does not support appending\n");
     return;
   }
-  ASSERT_OK(Put("foo", "bar"));
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   Close();
   std::string old_manifest = ManifestFileName();
   Open();
@@ -171,12 +175,13 @@ TEST(RecoveryTest, ManifestReused) {
   ASSERT_EQ("bar", Get("foo"));
 }
 
-TEST(RecoveryTest, LargeManifestCompacted) {
+TEST_F(RecoveryTest, LargeManifestCompacted) {
   if (!CanAppend()) {
-    fprintf(stderr, "skipping test because env does not support appending\n");
+    std::fprintf(stderr,
+                 "skipping test because env does not support appending\n");
     return;
   }
-  ASSERT_OK(Put("foo", "bar"));
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   Close();
   std::string old_manifest = ManifestFileName();
 
@@ -184,10 +189,10 @@ TEST(RecoveryTest, LargeManifestCompacted) {
   {
     uint64_t len = FileSize(old_manifest);
     WritableFile* file;
-    ASSERT_OK(env()->NewAppendableFile(old_manifest, &file));
+    ASSERT_LEVELDB_OK(env()->NewAppendableFile(old_manifest, &file));
     std::string zeroes(3 * 1048576 - static_cast<size_t>(len), 0);
-    ASSERT_OK(file->Append(zeroes));
-    ASSERT_OK(file->Flush());
+    ASSERT_LEVELDB_OK(file->Append(zeroes));
+    ASSERT_LEVELDB_OK(file->Flush());
     delete file;
   }
 
@@ -202,22 +207,23 @@ TEST(RecoveryTest, LargeManifestCompacted) {
   ASSERT_EQ("bar", Get("foo"));
 }
 
-TEST(RecoveryTest, NoLogFiles) {
-  ASSERT_OK(Put("foo", "bar"));
-  ASSERT_EQ(1, DeleteLogFiles());
+TEST_F(RecoveryTest, NoLogFiles) {
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
+  ASSERT_EQ(1, RemoveLogFiles());
   Open();
   ASSERT_EQ("NOT_FOUND", Get("foo"));
   Open();
   ASSERT_EQ("NOT_FOUND", Get("foo"));
 }
 
-TEST(RecoveryTest, LogFileReuse) {
+TEST_F(RecoveryTest, LogFileReuse) {
   if (!CanAppend()) {
-    fprintf(stderr, "skipping test because env does not support appending\n");
+    std::fprintf(stderr,
+                 "skipping test because env does not support appending\n");
     return;
   }
   for (int i = 0; i < 2; i++) {
-    ASSERT_OK(Put("foo", "bar"));
+    ASSERT_LEVELDB_OK(Put("foo", "bar"));
     if (i == 0) {
       // Compact to ensure current log is empty
       CompactMemTable();
@@ -241,13 +247,13 @@ TEST(RecoveryTest, LogFileReuse) {
   }
 }
 
-TEST(RecoveryTest, MultipleMemTables) {
+TEST_F(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));
+    std::snprintf(buf, sizeof(buf), "%050d", i);
+    ASSERT_LEVELDB_OK(Put(buf, buf));
   }
   ASSERT_EQ(0, NumTables());
   Close();
@@ -265,13 +271,13 @@ TEST(RecoveryTest, MultipleMemTables) {
   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);
+    std::snprintf(buf, sizeof(buf), "%050d", i);
     ASSERT_EQ(buf, Get(buf));
   }
 }
 
-TEST(RecoveryTest, MultipleLogFiles) {
-  ASSERT_OK(Put("foo", "bar"));
+TEST_F(RecoveryTest, MultipleLogFiles) {
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   Close();
   ASSERT_EQ(1, NumLogs());
 
@@ -316,10 +322,10 @@ TEST(RecoveryTest, MultipleLogFiles) {
   ASSERT_EQ("there", Get("hi"));
 }
 
-TEST(RecoveryTest, ManifestMissing) {
-  ASSERT_OK(Put("foo", "bar"));
+TEST_F(RecoveryTest, ManifestMissing) {
+  ASSERT_LEVELDB_OK(Put("foo", "bar"));
   Close();
-  DeleteManifestFile();
+  RemoveManifestFile();
 
   Status status = OpenWithStatus();
   ASSERT_TRUE(status.IsCorruption());
@@ -327,4 +333,7 @@ TEST(RecoveryTest, ManifestMissing) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 3c676ca..97a27c6 100644 (file)
@@ -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<int>(bytes),
@@ -341,7 +341,7 @@ class Repairer {
       }
     }
     if (!s.ok()) {
-      env_->DeleteFile(copy);
+      env_->RemoveFile(copy);
     }
   }
 
@@ -372,7 +372,8 @@ class Repairer {
                     t.meta.largest);
     }
 
-    // fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
+    // std::fprintf(stderr,
+    //              "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
     {
       log::Writer log(file);
       std::string record;
@@ -386,7 +387,7 @@ class Repairer {
     file = nullptr;
 
     if (!status.ok()) {
-      env_->DeleteFile(tmp);
+      env_->RemoveFile(tmp);
     } else {
       // Discard older manifests
       for (size_t i = 0; i < manifests_.size(); i++) {
@@ -398,7 +399,7 @@ class Repairer {
       if (status.ok()) {
         status = SetCurrentFile(env_, dbname_, 1);
       } else {
-        env_->DeleteFile(tmp);
+        env_->RemoveFile(tmp);
       }
     }
     return status;
index 9fa2d96..79a5b86 100644 (file)
@@ -7,13 +7,14 @@
 #include <atomic>
 #include <set>
 
+#include "gtest/gtest.h"
 #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"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace leveldb {
 
@@ -31,8 +32,6 @@ struct Comparator {
   }
 };
 
-class SkipTest {};
-
 TEST(SkipTest, Empty) {
   Arena arena;
   Comparator cmp;
@@ -152,7 +151,7 @@ TEST(SkipTest, InsertAndLookup) {
 // been concurrently added since the iterator started.
 class ConcurrentTest {
  private:
-  static const uint32_t K = 4;
+  static constexpr uint32_t K = 4;
 
   static uint64_t key(Key key) { return (key >> 40); }
   static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; }
@@ -281,7 +280,9 @@ class ConcurrentTest {
     }
   }
 };
-const uint32_t ConcurrentTest::K;
+
+// Needed when building in C++11 mode.
+constexpr uint32_t ConcurrentTest::K;
 
 // Simple test that does single-threaded testing of the ConcurrentTest
 // scaffolding.
@@ -345,7 +346,7 @@ static void RunConcurrent(int run) {
   const int kSize = 1000;
   for (int i = 0; i < N; i++) {
     if ((i % 100) == 0) {
-      fprintf(stderr, "Run %d of %d\n", i, N);
+      std::fprintf(stderr, "Run %d of %d\n", i, N);
     }
     TestState state(seed + 1);
     Env::Default()->Schedule(ConcurrentReader, &state);
@@ -366,4 +367,7 @@ TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 93069c8..aac9bfc 100644 (file)
@@ -7,8 +7,7 @@
 #ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_
 #define STORAGE_LEVELDB_DB_TABLE_CACHE_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <string>
 
 #include "db/dbformat.h"
index 44a4d02..3e9012f 100644 (file)
@@ -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++) {
@@ -87,8 +86,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;
   }
@@ -233,12 +231,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) {
-    r.append("\n  DeleteFile: ");
-    AppendNumberTo(&r, iter->first);
+  for (const auto& deleted_files_kvp : deleted_files_) {
+    r.append("\n  RemoveFile: ");
+    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;
index 2dadda7..137b4b1 100644 (file)
@@ -29,7 +29,7 @@ struct FileMetaData {
 class VersionEdit {
  public:
   VersionEdit() { Clear(); }
-  ~VersionEdit() {}
+  ~VersionEdit() = default;
 
   void Clear();
 
@@ -71,7 +71,7 @@ class VersionEdit {
   }
 
   // Delete the specified "file" from the specified "level".
-  void DeleteFile(int level, uint64_t file) {
+  void RemoveFile(int level, uint64_t file) {
     deleted_files_.insert(std::make_pair(level, file));
   }
 
@@ -83,7 +83,7 @@ class VersionEdit {
  private:
   friend class VersionSet;
 
-  typedef std::set<std::pair<int, uint64_t> > DeletedFileSet;
+  typedef std::set<std::pair<int, uint64_t>> DeletedFileSet;
 
   std::string comparator_;
   uint64_t log_number_;
@@ -96,9 +96,9 @@ class VersionEdit {
   bool has_next_file_number_;
   bool has_last_sequence_;
 
-  std::vector<std::pair<int, InternalKey> > compact_pointers_;
+  std::vector<std::pair<int, InternalKey>> compact_pointers_;
   DeletedFileSet deleted_files_;
-  std::vector<std::pair<int, FileMetaData> > new_files_;
+  std::vector<std::pair<int, FileMetaData>> new_files_;
 };
 
 }  // namespace leveldb
index 0b7cda8..acafab0 100644 (file)
@@ -3,7 +3,8 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include "db/version_edit.h"
-#include "util/testharness.h"
+
+#include "gtest/gtest.h"
 
 namespace leveldb {
 
@@ -17,8 +18,6 @@ static void TestEncodeDecode(const VersionEdit& edit) {
   ASSERT_EQ(encoded, encoded2);
 }
 
-class VersionEditTest {};
-
 TEST(VersionEditTest, EncodeDecode) {
   static const uint64_t kBig = 1ull << 50;
 
@@ -28,7 +27,7 @@ TEST(VersionEditTest, EncodeDecode) {
     edit.AddFile(3, kBig + 300 + i, kBig + 400 + i,
                  InternalKey("foo", kBig + 500 + i, kTypeValue),
                  InternalKey("zoo", kBig + 600 + i, kTypeDeletion));
-    edit.DeleteFile(4, kBig + 700 + i);
+    edit.RemoveFile(4, kBig + 700 + i);
     edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue));
   }
 
@@ -41,4 +40,7 @@ TEST(VersionEditTest, EncodeDecode) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 96a92cc..1963353 100644 (file)
@@ -4,9 +4,8 @@
 
 #include "db/version_set.h"
 
-#include <stdio.h>
-
 #include <algorithm>
+#include <cstdio>
 
 #include "db/filename.h"
 #include "db/log_reader.h"
@@ -167,19 +166,19 @@ class Version::LevelFileNumIterator : public Iterator {
                        const std::vector<FileMetaData*>* 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 +186,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_;
@@ -281,7 +280,6 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
 
 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();
 
   // Search level-0 in order from newest to oldest.
@@ -325,99 +323,80 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg,
 
 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();
-  Status s;
-
   stats->seek_file = nullptr;
   stats->seek_file_level = -1;
-  FileMetaData* last_file_read = nullptr;
-  int last_file_read_level = -1;
 
-  // We can search level-by-level since entries never hop across
-  // levels.  Therefore we are guaranteed that if we find data
-  // in a smaller level, later levels are irrelevant.
-  std::vector<FileMetaData*> tmp;
-  FileMetaData* tmp2;
-  for (int level = 0; level < config::kNumLevels; level++) {
-    size_t num_files = files_[level].size();
-    if (num_files == 0) continue;
+  struct State {
+    Saver saver;
+    GetStats* stats;
+    const ReadOptions* options;
+    Slice ikey;
+    FileMetaData* last_file_read;
+    int last_file_read_level;
 
-    // Get the list of files to search in this level
-    FileMetaData* const* files = &files_[level][0];
-    if (level == 0) {
-      // Level-0 files may overlap each other.  Find all files that
-      // overlap user_key and process them in order from newest to oldest.
-      tmp.reserve(num_files);
-      for (uint32_t i = 0; i < num_files; i++) {
-        FileMetaData* f = files[i];
-        if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
-            ucmp->Compare(user_key, f->largest.user_key()) <= 0) {
-          tmp.push_back(f);
-        }
-      }
-      if (tmp.empty()) continue;
+    VersionSet* vset;
+    Status s;
+    bool found;
 
-      std::sort(tmp.begin(), tmp.end(), NewestFirst);
-      files = &tmp[0];
-      num_files = tmp.size();
-    } else {
-      // Binary search to find earliest index whose largest key >= ikey.
-      uint32_t index = FindFile(vset_->icmp_, files_[level], ikey);
-      if (index >= num_files) {
-        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 = nullptr;
-          num_files = 0;
-        } else {
-          files = &tmp2;
-          num_files = 1;
-        }
-      }
-    }
+    static bool Match(void* arg, int level, FileMetaData* f) {
+      State* state = reinterpret_cast<State*>(arg);
 
-    for (uint32_t i = 0; i < num_files; ++i) {
-      if (last_file_read != nullptr && stats->seek_file == nullptr) {
+      if (state->stats->seek_file == nullptr &&
+          state->last_file_read != 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;
+        state->stats->seek_file = state->last_file_read;
+        state->stats->seek_file_level = state->last_file_read_level;
       }
 
-      FileMetaData* f = files[i];
-      last_file_read = f;
-      last_file_read_level = level;
-
-      Saver saver;
-      saver.state = kNotFound;
-      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);
-      if (!s.ok()) {
-        return s;
+      state->last_file_read = f;
+      state->last_file_read_level = level;
+
+      state->s = state->vset->table_cache_->Get(*state->options, f->number,
+                                                f->file_size, state->ikey,
+                                                &state->saver, SaveValue);
+      if (!state->s.ok()) {
+        state->found = true;
+        return false;
       }
-      switch (saver.state) {
+      switch (state->saver.state) {
         case kNotFound:
-          break;  // Keep searching in other files
+          return true;  // Keep searching in other files
         case kFound:
-          return s;
+          state->found = true;
+          return false;
         case kDeleted:
-          s = Status::NotFound(Slice());  // Use empty error message for speed
-          return s;
+          return false;
         case kCorrupt:
-          s = Status::Corruption("corrupted key for ", user_key);
-          return s;
+          state->s =
+              Status::Corruption("corrupted key for ", state->saver.user_key);
+          state->found = true;
+          return false;
       }
+
+      // Not reached. Added to avoid false compilation warnings of
+      // "control reaches end of non-void function".
+      return false;
     }
-  }
+  };
+
+  State state;
+  state.found = false;
+  state.stats = stats;
+  state.last_file_read = nullptr;
+  state.last_file_read_level = -1;
+
+  state.options = &options;
+  state.ikey = k.internal_key();
+  state.vset = vset_;
 
-  return Status::NotFound(Slice());  // Use an empty error message for speed
+  state.saver.state = kNotFound;
+  state.saver.ucmp = vset_->icmp_.user_comparator();
+  state.saver.user_key = k.user_key();
+  state.saver.value = value;
+
+  ForEachOverlapping(state.saver.user_key, state.ikey, &state, &State::Match);
+
+  return state.found ? state.s : Status::NotFound(Slice());
 }
 
 bool Version::UpdateStats(const GetStats& stats) {
@@ -656,11 +635,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 +678,17 @@ class VersionSet::Builder {
       const std::vector<FileMetaData*>& base_files = base_->files_[level];
       std::vector<FileMetaData*>::const_iterator base_iter = base_files.begin();
       std::vector<FileMetaData*>::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<FileMetaData*>::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
@@ -727,10 +703,10 @@ class VersionSet::Builder {
           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",
-                    prev_end.DebugString().c_str(),
-                    this_begin.DebugString().c_str());
-            abort();
+            std::fprintf(stderr, "overlapping ranges in same level %s vs. %s\n",
+                         prev_end.DebugString().c_str(),
+                         this_begin.DebugString().c_str());
+            std::abort();
           }
         }
       }
@@ -876,7 +852,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
       delete descriptor_file_;
       descriptor_log_ = nullptr;
       descriptor_file_ = nullptr;
-      env_->DeleteFile(new_manifest_file);
+      env_->RemoveFile(new_manifest_file);
     }
   }
 
@@ -886,7 +862,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;
     }
   };
@@ -922,6 +898,7 @@ Status VersionSet::Recover(bool* save_manifest) {
   uint64_t log_number = 0;
   uint64_t prev_log_number = 0;
   Builder builder(this, current_);
+  int read_records = 0;
 
   {
     LogReporter reporter;
@@ -931,6 +908,7 @@ Status VersionSet::Recover(bool* save_manifest) {
     Slice record;
     std::string scratch;
     while (reader.ReadRecord(&record, &scratch) && s.ok()) {
+      ++read_records;
       VersionEdit edit;
       s = edit.DecodeFrom(record);
       if (s.ok()) {
@@ -1005,6 +983,10 @@ Status VersionSet::Recover(bool* save_manifest) {
     } else {
       *save_manifest = true;
     }
+  } else {
+    std::string error = s.ToString();
+    Log(options_->info_log, "Error recovering version set with %d records: %s",
+        read_records, error.c_str());
   }
 
   return s;
@@ -1124,11 +1106,12 @@ int VersionSet::NumLevelFiles(int level) const {
 const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const {
   // Update code if kNumLevels changes
   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()));
+  std::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()));
   return scratch->buffer;
 }
 
@@ -1525,7 +1508,7 @@ bool Compaction::IsTrivialMove() const {
 void Compaction::AddInputDeletions(VersionEdit* edit) {
   for (int which = 0; which < 2; which++) {
     for (size_t i = 0; i < inputs_[which].size(); i++) {
-      edit->DeleteFile(level_ + which, inputs_[which][i]->number);
+      edit->RemoveFile(level_ + which, inputs_[which][i]->number);
     }
   }
 }
@@ -1535,7 +1518,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<FileMetaData*>& 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
index f7efe2b..dee6b4c 100644 (file)
@@ -3,13 +3,14 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include "db/version_set.h"
+
+#include "gtest/gtest.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
 
-class FindFileTest {
+class FindFileTest : public testing::Test {
  public:
   FindFileTest() : disjoint_sorted_files_(true) {}
 
@@ -50,7 +51,7 @@ class FindFileTest {
   std::vector<FileMetaData*> files_;
 };
 
-TEST(FindFileTest, Empty) {
+TEST_F(FindFileTest, Empty) {
   ASSERT_EQ(0, Find("foo"));
   ASSERT_TRUE(!Overlaps("a", "z"));
   ASSERT_TRUE(!Overlaps(nullptr, "z"));
@@ -58,7 +59,7 @@ TEST(FindFileTest, Empty) {
   ASSERT_TRUE(!Overlaps(nullptr, nullptr));
 }
 
-TEST(FindFileTest, Single) {
+TEST_F(FindFileTest, Single) {
   Add("p", "q");
   ASSERT_EQ(0, Find("a"));
   ASSERT_EQ(0, Find("p"));
@@ -88,7 +89,7 @@ TEST(FindFileTest, Single) {
   ASSERT_TRUE(Overlaps(nullptr, nullptr));
 }
 
-TEST(FindFileTest, Multiple) {
+TEST_F(FindFileTest, Multiple) {
   Add("150", "200");
   Add("200", "250");
   Add("300", "350");
@@ -126,7 +127,7 @@ TEST(FindFileTest, Multiple) {
   ASSERT_TRUE(Overlaps("450", "500"));
 }
 
-TEST(FindFileTest, MultipleNullBoundaries) {
+TEST_F(FindFileTest, MultipleNullBoundaries) {
   Add("150", "200");
   Add("200", "250");
   Add("300", "350");
@@ -146,7 +147,7 @@ TEST(FindFileTest, MultipleNullBoundaries) {
   ASSERT_TRUE(Overlaps("450", nullptr));
 }
 
-TEST(FindFileTest, OverlapSequenceChecks) {
+TEST_F(FindFileTest, OverlapSequenceChecks) {
   Add("200", "200", 5000, 3000);
   ASSERT_TRUE(!Overlaps("199", "199"));
   ASSERT_TRUE(!Overlaps("201", "300"));
@@ -155,7 +156,7 @@ TEST(FindFileTest, OverlapSequenceChecks) {
   ASSERT_TRUE(Overlaps("200", "210"));
 }
 
-TEST(FindFileTest, OverlappingFiles) {
+TEST_F(FindFileTest, OverlappingFiles) {
   Add("150", "600");
   Add("400", "500");
   disjoint_sorted_files_ = false;
@@ -177,21 +178,21 @@ void AddBoundaryInputs(const InternalKeyComparator& icmp,
                        const std::vector<FileMetaData*>& level_files,
                        std::vector<FileMetaData*>* compaction_files);
 
-class AddBoundaryInputsTest {
+class AddBoundaryInputsTest : public testing::Test {
  public:
   std::vector<FileMetaData*> level_files_;
   std::vector<FileMetaData*> compaction_files_;
   std::vector<FileMetaData*> 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) {
@@ -204,13 +205,13 @@ class AddBoundaryInputsTest {
   }
 };
 
-TEST(AddBoundaryInputsTest, TestEmptyFileSets) {
+TEST_F(AddBoundaryInputsTest, TestEmptyFileSets) {
   AddBoundaryInputs(icmp_, level_files_, &compaction_files_);
   ASSERT_TRUE(compaction_files_.empty());
   ASSERT_TRUE(level_files_.empty());
 }
 
-TEST(AddBoundaryInputsTest, TestEmptyLevelFiles) {
+TEST_F(AddBoundaryInputsTest, TestEmptyLevelFiles) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 2, kTypeValue),
                          InternalKey(InternalKey("100", 1, kTypeValue)));
@@ -222,7 +223,7 @@ TEST(AddBoundaryInputsTest, TestEmptyLevelFiles) {
   ASSERT_TRUE(level_files_.empty());
 }
 
-TEST(AddBoundaryInputsTest, TestEmptyCompactionFiles) {
+TEST_F(AddBoundaryInputsTest, TestEmptyCompactionFiles) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 2, kTypeValue),
                          InternalKey(InternalKey("100", 1, kTypeValue)));
@@ -234,7 +235,7 @@ TEST(AddBoundaryInputsTest, TestEmptyCompactionFiles) {
   ASSERT_EQ(f1, level_files_[0]);
 }
 
-TEST(AddBoundaryInputsTest, TestNoBoundaryFiles) {
+TEST_F(AddBoundaryInputsTest, TestNoBoundaryFiles) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 2, kTypeValue),
                          InternalKey(InternalKey("100", 1, kTypeValue)));
@@ -255,7 +256,7 @@ TEST(AddBoundaryInputsTest, TestNoBoundaryFiles) {
   ASSERT_EQ(2, compaction_files_.size());
 }
 
-TEST(AddBoundaryInputsTest, TestOneBoundaryFiles) {
+TEST_F(AddBoundaryInputsTest, TestOneBoundaryFiles) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 3, kTypeValue),
                          InternalKey(InternalKey("100", 2, kTypeValue)));
@@ -277,7 +278,7 @@ TEST(AddBoundaryInputsTest, TestOneBoundaryFiles) {
   ASSERT_EQ(f2, compaction_files_[1]);
 }
 
-TEST(AddBoundaryInputsTest, TestTwoBoundaryFiles) {
+TEST_F(AddBoundaryInputsTest, TestTwoBoundaryFiles) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 6, kTypeValue),
                          InternalKey(InternalKey("100", 5, kTypeValue)));
@@ -300,7 +301,7 @@ TEST(AddBoundaryInputsTest, TestTwoBoundaryFiles) {
   ASSERT_EQ(f2, compaction_files_[2]);
 }
 
-TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) {
+TEST_F(AddBoundaryInputsTest, TestDisjoinFilePointers) {
   FileMetaData* f1 =
       CreateFileMetaData(1, InternalKey("100", 6, kTypeValue),
                          InternalKey(InternalKey("100", 5, kTypeValue)));
@@ -329,4 +330,7 @@ TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 2dec642..b54313c 100644 (file)
@@ -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_++;
   }
index c32317f..64df9b8 100644 (file)
@@ -2,13 +2,12 @@
 // 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 "gtest/gtest.h"
 #include "db/memtable.h"
 #include "db/write_batch_internal.h"
+#include "leveldb/db.h"
 #include "leveldb/env.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
@@ -22,7 +21,7 @@ static std::string PrintContents(WriteBatch* b) {
   Iterator* iter = mem->NewIterator();
   for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
     ParsedInternalKey ikey;
-    ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey));
+    EXPECT_TRUE(ParseInternalKey(iter->key(), &ikey));
     switch (ikey.type) {
       case kTypeValue:
         state.append("Put(");
@@ -52,8 +51,6 @@ static std::string PrintContents(WriteBatch* b) {
   return state;
 }
 
-class WriteBatchTest {};
-
 TEST(WriteBatchTest, Empty) {
   WriteBatch batch;
   ASSERT_EQ("", PrintContents(&batch));
@@ -134,4 +131,7 @@ TEST(WriteBatchTest, ApproximateSize) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index c463977..1e0b4ef 100644 (file)
@@ -83,23 +83,23 @@ div.bsql {
 <p>Google, July 2011</p>
 <hr>
 
-<p>In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against <a href="http://www.sqlite.org/">SQLite3</a> (version 3.7.6.3) and <a href="http://fallabs.com/kyotocabinet/spex.html">Kyoto Cabinet's</a> (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.</p>
+<p>In order to test LevelDB's performance, we benchmark it against other well-established database implementations. We compare LevelDB (revision 39) against <a href="https://www.sqlite.org/">SQLite3</a> (version 3.7.6.3) and <a href="https://dbmx.net/kyotocabinet/spex.html">Kyoto Cabinet's</a> (version 1.2.67) TreeDB (a B+Tree based key-value store). We would like to acknowledge Scott Hess and Mikio Hirabayashi for their suggestions and contributions to the SQLite3 and Kyoto Cabinet benchmarks, respectively.</p>
 
 <p>Benchmarks were all performed on a six-core Intel(R) Xeon(R) CPU X5650 @ 2.67GHz, with 12288 KB of total L3 cache and 12 GB of DDR3 RAM at 1333 MHz. (Note that LevelDB uses at most two CPUs since the benchmarks are single threaded: one to run the benchmark, and one for background compactions.) We ran the benchmarks on two machines (with identical processors), one with an Ext3 file system and one with an Ext4 file system. The machine with the Ext3 file system has a SATA Hitachi HDS721050CLA362 hard drive. The machine with the Ext4 file system has a SATA Samsung HD502HJ hard drive. Both hard drives spin at 7200 RPM and have hard drive write-caching enabled (using `hdparm -W 1 [device]`). The numbers reported below are the median of three measurements.</p>
 
 <h4>Benchmark Source Code</h4>
 <p>We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's <span class="code">db_bench</span>. The code for each of the benchmarks resides here:</p>
 <ul>
-       <li> <b>LevelDB:</b> <a href="http://code.google.com/p/leveldb/source/browse/trunk/db/db_bench.cc">db/db_bench.cc</a>.</li>
-       <li> <b>SQLite:</b> <a href="http://code.google.com/p/leveldb/source/browse/#svn%2Ftrunk%2Fdoc%2Fbench%2Fdb_bench_sqlite3.cc">doc/bench/db_bench_sqlite3.cc</a>.</li>
-       <li> <b>Kyoto TreeDB:</b> <a href="http://code.google.com/p/leveldb/source/browse/#svn%2Ftrunk%2Fdoc%2Fbench%2Fdb_bench_tree_db.cc">doc/bench/db_bench_tree_db.cc</a>.</li>
+       <li> <b>LevelDB:</b> <a href="https://github.com/google/leveldb/blob/master/benchmarks/db_bench.cc">benchmarks/db_bench.cc</a>.</li>
+       <li> <b>SQLite:</b> <a href="https://github.com/google/leveldb/blob/master/benchmarks/db_bench_sqlite3.cc">benchmarks/db_bench_sqlite3.cc</a>.</li>
+       <li> <b>Kyoto TreeDB:</b> <a href="https://github.com/google/leveldb/blob/master/benchmarks/db_bench_tree_db.cc">benchmarks/db_bench_tree_db.cc</a>.</li>
 </ul>
 
 <h4>Custom Build Specifications</h4>
 <ul>
-<li>LevelDB: LevelDB was compiled with the <a href="http://code.google.com/p/google-perftools">tcmalloc</a> library and the <a href="http://code.google.com/p/snappy/">Snappy</a> compression library (revision 33).  Assertions were disabled.</li>
-<li>TreeDB: TreeDB was compiled using the <a href="http://www.oberhumer.com/opensource/lzo/">LZO</a> compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.</li>
-<li>SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive.  We also enabled SQLite's <a href="http://www.sqlite.org/draft/wal.html">write-ahead logging</a>.</li>
+<li>LevelDB: LevelDB was compiled with the <a href="https://github.com/gperftools/gperftools">tcmalloc</a> library and the <a href="https://github.com/google/snappy">Snappy</a> compression library (revision 33).  Assertions were disabled.</li>
+<li>TreeDB: TreeDB was compiled using the <a href="https://www.oberhumer.com/opensource/lzo/">LZO</a> compression library (version 2.03). Furthermore, we enabled the TSMALL and TLINEAR options when opening the database in order to reduce the footprint of each record.</li>
+<li>SQLite: We tuned SQLite's performance, by setting its locking mode to exclusive.  We also enabled SQLite's <a href="https://www.sqlite.org/draft/wal.html">write-ahead logging</a>.</li>
 </ul>
 
 <h2>1. Baseline Performance</h2>
@@ -451,7 +451,7 @@ performance may very well be better with compression if it allows more
 of the working set to fit in memory.</p>
 
 <h2>Note about Ext4 Filesystems</h2>
-<p>The preceding numbers are for an ext3 file system. Synchronous writes are much slower under <a href="http://en.wikipedia.org/wiki/Ext4">ext4</a> (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of <span class="code">fsync</span> / <span class="code">msync</span> calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues <span class="code">fsync</span> calls when switching to a new file.</p>
+<p>The preceding numbers are for an ext3 file system. Synchronous writes are much slower under <a href="https://en.wikipedia.org/wiki/Ext4">ext4</a> (LevelDB drops to ~31 writes / second and TreeDB drops to ~5 writes / second; SQLite3's synchronous writes do not noticeably drop) due to ext4's different handling of <span class="code">fsync</span> / <span class="code">msync</span> calls. Even LevelDB's asynchronous write performance drops somewhat since it spreads its storage across multiple files and issues <span class="code">fsync</span> calls when switching to a new file.</p>
 
 <h2>Acknowledgements</h2>
 <p>Jeff Dean and Sanjay Ghemawat wrote LevelDB. Kevin Tseng wrote and compiled these benchmarks. Mikio Hirabayashi, Scott Hess, and Gabor Cselle provided help and advice.</p>
index cacabb9..c9bb621 100644 (file)
@@ -1,7 +1,7 @@
 ## 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).
+single [Bigtable tablet (section 5.3)](https://research.google/pubs/pub27898/).
 However the organization of the files that make up the representation is
 somewhat different and is explained below.
 
@@ -166,7 +166,7 @@ So maybe even the sharding is not necessary on modern filesystems?
 
 ## Garbage collection of files
 
-`DeleteObsoleteFiles()` is called at the end of every compaction and at the end
+`RemoveObsoleteFiles()` 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.
index ea4609d..01693ad 100644 (file)
@@ -307,7 +307,7 @@ 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`.
+`include/options.h`.
 
 ### Block size
 
@@ -438,7 +438,7 @@ class CustomFilterPolicy : public leveldb::FilterPolicy {
     for (int i = 0; i < n; i++) {
       trimmed[i] = RemoveTrailingSpaces(keys[i]);
     }
-    return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
+    return builtin_policy_->CreateFilter(trimmed.data(), n, dst);
   }
 };
 ```
@@ -478,7 +478,7 @@ 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);
+db->GetApproximateSizes(ranges, 2, sizes);
 ```
 
 The preceding call will set `sizes[0]` to the approximate number of bytes of
index b6b790c..e476613 100644 (file)
@@ -4,8 +4,7 @@
 
 #include "helpers/memenv/memenv.h"
 
-#include <string.h>
-
+#include <cstring>
 #include <limits>
 #include <map>
 #include <string>
@@ -94,7 +93,7 @@ class FileState {
       if (avail > bytes_to_copy) {
         avail = bytes_to_copy;
       }
-      memcpy(dst, blocks_[block] + block_offset, avail);
+      std::memcpy(dst, blocks_[block] + block_offset, avail);
 
       bytes_to_copy -= avail;
       dst += avail;
@@ -127,7 +126,7 @@ class FileState {
       if (avail > src_len) {
         avail = src_len;
       }
-      memcpy(blocks_.back() + offset, src, avail);
+      std::memcpy(blocks_.back() + offset, src, avail);
       src_len -= avail;
       src += avail;
       size_ += avail;
@@ -156,9 +155,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 +165,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 +186,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 +201,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,23 +215,22 @@ class WritableFileImpl : public WritableFile {
 
 class NoOpLogger : public Logger {
  public:
-  virtual void Logv(const char* format, va_list ap) {}
+  void Logv(const char* format, std::va_list ap) override {}
 };
 
 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 +241,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 +253,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 +273,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 +286,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<std::string>* result) {
+  Status GetChildren(const std::string& dir,
+                     std::vector<std::string>* 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))) {
@@ -311,7 +308,7 @@ class InMemoryEnv : public EnvWrapper {
     return Status::OK();
   }
 
-  void DeleteFileInternal(const std::string& fname)
+  void RemoveFileInternal(const std::string& fname)
       EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
     if (file_map_.find(fname) == file_map_.end()) {
       return;
@@ -321,21 +318,21 @@ class InMemoryEnv : public EnvWrapper {
     file_map_.erase(fname);
   }
 
-  virtual Status DeleteFile(const std::string& fname) {
+  Status RemoveFile(const std::string& fname) override {
     MutexLock lock(&mutex_);
     if (file_map_.find(fname) == file_map_.end()) {
       return Status::IOError(fname, "File not found");
     }
 
-    DeleteFileInternal(fname);
+    RemoveFileInternal(fname);
     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 RemoveDir(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,34 +342,35 @@ 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");
     }
 
-    DeleteFileInternal(target);
+    RemoveFileInternal(target);
     file_map_[target] = file_map_[src];
     file_map_.erase(src);
     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();
   }
index 94ad06b..3f03cb6 100644 (file)
@@ -7,14 +7,15 @@
 #include <string>
 #include <vector>
 
+#include "gtest/gtest.h"
 #include "db/db_impl.h"
 #include "leveldb/db.h"
 #include "leveldb/env.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace leveldb {
 
-class MemEnvTest {
+class MemEnvTest : public testing::Test {
  public:
   MemEnvTest() : env_(NewMemEnv(Env::Default())) {}
   ~MemEnvTest() { delete env_; }
@@ -22,55 +23,55 @@ class MemEnvTest {
   Env* env_;
 };
 
-TEST(MemEnvTest, Basics) {
+TEST_F(MemEnvTest, Basics) {
   uint64_t file_size;
   WritableFile* writable_file;
   std::vector<std::string> children;
 
-  ASSERT_OK(env_->CreateDir("/dir"));
+  ASSERT_LEVELDB_OK(env_->CreateDir("/dir"));
 
   // Check that the directory is empty.
   ASSERT_TRUE(!env_->FileExists("/dir/non_existent"));
   ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok());
-  ASSERT_OK(env_->GetChildren("/dir", &children));
+  ASSERT_LEVELDB_OK(env_->GetChildren("/dir", &children));
   ASSERT_EQ(0, children.size());
 
   // Create a file.
-  ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
-  ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
+  ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
   ASSERT_EQ(0, file_size);
   delete writable_file;
 
   // Check that the file exists.
   ASSERT_TRUE(env_->FileExists("/dir/f"));
-  ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+  ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
   ASSERT_EQ(0, file_size);
-  ASSERT_OK(env_->GetChildren("/dir", &children));
+  ASSERT_LEVELDB_OK(env_->GetChildren("/dir", &children));
   ASSERT_EQ(1, children.size());
   ASSERT_EQ("f", children[0]);
 
   // Write to the file.
-  ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
-  ASSERT_OK(writable_file->Append("abc"));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
+  ASSERT_LEVELDB_OK(writable_file->Append("abc"));
   delete writable_file;
 
   // Check that append works.
-  ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file));
-  ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+  ASSERT_LEVELDB_OK(env_->NewAppendableFile("/dir/f", &writable_file));
+  ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
   ASSERT_EQ(3, file_size);
-  ASSERT_OK(writable_file->Append("hello"));
+  ASSERT_LEVELDB_OK(writable_file->Append("hello"));
   delete writable_file;
 
   // Check for expected size.
-  ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
+  ASSERT_LEVELDB_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());
-  ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g"));
+  ASSERT_LEVELDB_OK(env_->RenameFile("/dir/f", "/dir/g"));
   ASSERT_TRUE(!env_->FileExists("/dir/f"));
   ASSERT_TRUE(env_->FileExists("/dir/g"));
-  ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));
+  ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/g", &file_size));
   ASSERT_EQ(8, file_size);
 
   // Check that opening non-existent file fails.
@@ -82,49 +83,50 @@ TEST(MemEnvTest, Basics) {
   ASSERT_TRUE(!rand_file);
 
   // Check that deleting works.
-  ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok());
-  ASSERT_OK(env_->DeleteFile("/dir/g"));
+  ASSERT_TRUE(!env_->RemoveFile("/dir/non_existent").ok());
+  ASSERT_LEVELDB_OK(env_->RemoveFile("/dir/g"));
   ASSERT_TRUE(!env_->FileExists("/dir/g"));
-  ASSERT_OK(env_->GetChildren("/dir", &children));
+  ASSERT_LEVELDB_OK(env_->GetChildren("/dir", &children));
   ASSERT_EQ(0, children.size());
-  ASSERT_OK(env_->DeleteDir("/dir"));
+  ASSERT_LEVELDB_OK(env_->RemoveDir("/dir"));
 }
 
-TEST(MemEnvTest, ReadWrite) {
+TEST_F(MemEnvTest, ReadWrite) {
   WritableFile* writable_file;
   SequentialFile* seq_file;
   RandomAccessFile* rand_file;
   Slice result;
   char scratch[100];
 
-  ASSERT_OK(env_->CreateDir("/dir"));
+  ASSERT_LEVELDB_OK(env_->CreateDir("/dir"));
 
-  ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
-  ASSERT_OK(writable_file->Append("hello "));
-  ASSERT_OK(writable_file->Append("world"));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
+  ASSERT_LEVELDB_OK(writable_file->Append("hello "));
+  ASSERT_LEVELDB_OK(writable_file->Append("world"));
   delete writable_file;
 
   // Read sequentially.
-  ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));
-  ASSERT_OK(seq_file->Read(5, &result, scratch));  // Read "hello".
+  ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f", &seq_file));
+  ASSERT_LEVELDB_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_LEVELDB_OK(seq_file->Skip(1));
+  ASSERT_LEVELDB_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_LEVELDB_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->Read(1000, &result, scratch));
+  ASSERT_LEVELDB_OK(seq_file->Skip(100));  // Try to skip past end of file.
+  ASSERT_LEVELDB_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_LEVELDB_OK(env_->NewRandomAccessFile("/dir/f", &rand_file));
+  ASSERT_LEVELDB_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_LEVELDB_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_LEVELDB_OK(rand_file->Read(10, 100, &result, scratch));  // Read "d".
   ASSERT_EQ(0, result.compare("d"));
 
   // Too high offset.
@@ -132,30 +134,30 @@ TEST(MemEnvTest, ReadWrite) {
   delete rand_file;
 }
 
-TEST(MemEnvTest, Locks) {
+TEST_F(MemEnvTest, Locks) {
   FileLock* lock;
 
   // These are no-ops, but we test they return success.
-  ASSERT_OK(env_->LockFile("some file", &lock));
-  ASSERT_OK(env_->UnlockFile(lock));
+  ASSERT_LEVELDB_OK(env_->LockFile("some file", &lock));
+  ASSERT_LEVELDB_OK(env_->UnlockFile(lock));
 }
 
-TEST(MemEnvTest, Misc) {
+TEST_F(MemEnvTest, Misc) {
   std::string test_dir;
-  ASSERT_OK(env_->GetTestDirectory(&test_dir));
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
   ASSERT_TRUE(!test_dir.empty());
 
   WritableFile* writable_file;
-  ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile("/a/b", &writable_file));
 
   // These are no-ops, but we test they return success.
-  ASSERT_OK(writable_file->Sync());
-  ASSERT_OK(writable_file->Flush());
-  ASSERT_OK(writable_file->Close());
+  ASSERT_LEVELDB_OK(writable_file->Sync());
+  ASSERT_LEVELDB_OK(writable_file->Flush());
+  ASSERT_LEVELDB_OK(writable_file->Close());
   delete writable_file;
 }
 
-TEST(MemEnvTest, LargeWrite) {
+TEST_F(MemEnvTest, LargeWrite) {
   const size_t kWriteSize = 300 * 1024;
   char* scratch = new char[kWriteSize * 2];
 
@@ -165,21 +167,21 @@ TEST(MemEnvTest, LargeWrite) {
   }
 
   WritableFile* writable_file;
-  ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
-  ASSERT_OK(writable_file->Append("foo"));
-  ASSERT_OK(writable_file->Append(write_data));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
+  ASSERT_LEVELDB_OK(writable_file->Append("foo"));
+  ASSERT_LEVELDB_OK(writable_file->Append(write_data));
   delete writable_file;
 
   SequentialFile* seq_file;
   Slice result;
-  ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));
-  ASSERT_OK(seq_file->Read(3, &result, scratch));  // Read "foo".
+  ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f", &seq_file));
+  ASSERT_LEVELDB_OK(seq_file->Read(3, &result, scratch));  // Read "foo".
   ASSERT_EQ(0, result.compare("foo"));
 
   size_t read = 0;
   std::string read_data;
   while (read < kWriteSize) {
-    ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
+    ASSERT_LEVELDB_OK(seq_file->Read(kWriteSize - read, &result, scratch));
     read_data.append(result.data(), result.size());
     read += result.size();
   }
@@ -188,30 +190,30 @@ TEST(MemEnvTest, LargeWrite) {
   delete[] scratch;
 }
 
-TEST(MemEnvTest, OverwriteOpenFile) {
+TEST_F(MemEnvTest, OverwriteOpenFile) {
   const char kWrite1Data[] = "Write #1 data";
   const size_t kFileDataLen = sizeof(kWrite1Data) - 1;
-  const std::string kTestFileName = test::TmpDir() + "/leveldb-TestFile.dat";
+  const std::string kTestFileName = testing::TempDir() + "leveldb-TestFile.dat";
 
-  ASSERT_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName));
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName));
 
   RandomAccessFile* rand_file;
-  ASSERT_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file));
+  ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file));
 
   const char kWrite2Data[] = "Write #2 data";
-  ASSERT_OK(WriteStringToFile(env_, kWrite2Data, kTestFileName));
+  ASSERT_LEVELDB_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_LEVELDB_OK(rand_file->Read(0, kFileDataLen, &result, scratch));
   ASSERT_EQ(0, result.compare(kWrite2Data));
 
   delete rand_file;
 }
 
-TEST(MemEnvTest, DBTest) {
+TEST_F(MemEnvTest, DBTest) {
   Options options;
   options.create_if_missing = true;
   options.env = env_;
@@ -220,14 +222,14 @@ TEST(MemEnvTest, DBTest) {
   const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")};
   const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")};
 
-  ASSERT_OK(DB::Open(options, "/dir/db", &db));
+  ASSERT_LEVELDB_OK(DB::Open(options, "/dir/db", &db));
   for (size_t i = 0; i < 3; ++i) {
-    ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i]));
+    ASSERT_LEVELDB_OK(db->Put(WriteOptions(), keys[i], vals[i]));
   }
 
   for (size_t i = 0; i < 3; ++i) {
     std::string res;
-    ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));
+    ASSERT_LEVELDB_OK(db->Get(ReadOptions(), keys[i], &res));
     ASSERT_TRUE(res == vals[i]);
   }
 
@@ -243,11 +245,11 @@ TEST(MemEnvTest, DBTest) {
   delete iterator;
 
   DBImpl* dbi = reinterpret_cast<DBImpl*>(db);
-  ASSERT_OK(dbi->TEST_CompactMemTable());
+  ASSERT_LEVELDB_OK(dbi->TEST_CompactMemTable());
 
   for (size_t i = 0; i < 3; ++i) {
     std::string res;
-    ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));
+    ASSERT_LEVELDB_OK(db->Get(ReadOptions(), keys[i], &res));
     ASSERT_TRUE(res == vals[i]);
   }
 
@@ -256,4 +258,7 @@ TEST(MemEnvTest, DBTest) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 8e0d592..02c79ba 100644 (file)
@@ -32,7 +32,7 @@
   On failure, leveldb frees the old value of *errptr and
   set *errptr to a malloc()ed error message.
 
-  (4) Bools have the type unsigned char (0 == false; rest == true)
+  (4) Bools have the type uint8_t (0 == false; rest == true)
 
   (5) All of the pointer arguments must be non-NULL.
 */
@@ -131,7 +131,7 @@ LEVELDB_EXPORT void leveldb_repair_db(const leveldb_options_t* options,
 /* Iterator */
 
 LEVELDB_EXPORT void leveldb_iter_destroy(leveldb_iterator_t*);
-LEVELDB_EXPORT unsigned char leveldb_iter_valid(const leveldb_iterator_t*);
+LEVELDB_EXPORT uint8_t 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,
@@ -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,18 +164,18 @@ 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*);
 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);
+                                                          uint8_t);
 LEVELDB_EXPORT void leveldb_options_set_error_if_exists(leveldb_options_t*,
-                                                        unsigned char);
+                                                        uint8_t);
 LEVELDB_EXPORT void leveldb_options_set_paranoid_checks(leveldb_options_t*,
-                                                        unsigned char);
+                                                        uint8_t);
 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*);
@@ -209,8 +209,8 @@ LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create(
     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),
+    uint8_t (*key_may_match)(void*, const char* key, size_t length,
+                             const char* filter, size_t filter_length),
     const char* (*name)(void*));
 LEVELDB_EXPORT void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*);
 
@@ -219,21 +219,21 @@ 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);
+    leveldb_readoptions_t*, uint8_t);
 LEVELDB_EXPORT void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t*,
-                                                       unsigned char);
+                                                       uint8_t);
 LEVELDB_EXPORT void leveldb_readoptions_set_snapshot(leveldb_readoptions_t*,
                                                      const leveldb_snapshot_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);
+                                                  uint8_t);
 
 /* Cache */
 
@@ -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" */
index 7d1a221..98c95ac 100644 (file)
@@ -18,7 +18,7 @@
 #ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_
 #define STORAGE_LEVELDB_INCLUDE_CACHE_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "leveldb/export.h"
 #include "leveldb/slice.h"
index ea3d9e5..a13d147 100644 (file)
@@ -5,8 +5,8 @@
 #ifndef STORAGE_LEVELDB_INCLUDE_DB_H_
 #define STORAGE_LEVELDB_INCLUDE_DB_H_
 
-#include <stdint.h>
-#include <stdio.h>
+#include <cstdint>
+#include <cstdio>
 
 #include "leveldb/export.h"
 #include "leveldb/iterator.h"
@@ -16,7 +16,7 @@ namespace leveldb {
 
 // Update CMakeLists.txt if you change these
 static const int kMajorVersion = 1;
-static const int kMinorVersion = 22;
+static const int kMinorVersion = 23;
 
 struct Options;
 struct ReadOptions;
@@ -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
index 112fe96..e00895a 100644 (file)
 #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_
 #define STORAGE_LEVELDB_INCLUDE_ENV_H_
 
-#include <stdarg.h>
-#include <stdint.h>
-
+#include <cstdarg>
+#include <cstdint>
 #include <string>
 #include <vector>
 
 #include "leveldb/export.h"
 #include "leveldb/status.h"
 
+// This workaround can be removed when leveldb::Env::DeleteFile is removed.
 #if defined(_WIN32)
-// The leveldb::Env class below contains a DeleteFile method.
-// At the same time, <windows.h>, a fairly popular header
-// file for Windows applications, defines a DeleteFile macro.
+// On Windows, the method name DeleteFile (below) introduces the risk of
+// triggering undefined behavior by exposing the compiler to different
+// declarations of the Env class in different translation units.
 //
-// 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 <windows.h> was included before or after the LevelDB
-// headers.
+// This is because <windows.h>, a fairly popular header file for Windows
+// applications, defines a DeleteFile macro. So, files that include the Windows
+// header before this header will contain an altered Env declaration.
 //
-// To avoid headaches, we undefined DeleteFile (if defined) and
-// redefine it at the bottom of this file. This way <windows.h>
-// can be included before this file (or not at all) and the
-// exported method will always be leveldb::Env::DeleteFile.
+// This workaround ensures that the compiler sees the same Env declaration,
+// independently of whether <windows.h> was included.
 #if defined(DeleteFile)
 #undef DeleteFile
 #define LEVELDB_DELETEFILE_UNDEFINED
@@ -54,7 +50,7 @@ class WritableFile;
 
 class LEVELDB_EXPORT Env {
  public:
-  Env() = default;
+  Env();
 
   Env(const Env&) = delete;
   Env& operator=(const Env&) = delete;
@@ -122,15 +118,48 @@ class LEVELDB_EXPORT Env {
   // Original contents of *results are dropped.
   virtual Status GetChildren(const std::string& dir,
                              std::vector<std::string>* result) = 0;
-
   // Delete the named file.
-  virtual Status DeleteFile(const std::string& fname) = 0;
+  //
+  // The default implementation calls DeleteFile, to support legacy Env
+  // implementations. Updated Env implementations must override RemoveFile and
+  // ignore the existence of DeleteFile. Updated code calling into the Env API
+  // must call RemoveFile instead of DeleteFile.
+  //
+  // A future release will remove DeleteDir and the default implementation of
+  // RemoveDir.
+  virtual Status RemoveFile(const std::string& fname);
+
+  // DEPRECATED: Modern Env implementations should override RemoveFile instead.
+  //
+  // The default implementation calls RemoveFile, to support legacy Env user
+  // code that calls this method on modern Env implementations. Modern Env user
+  // code should call RemoveFile.
+  //
+  // A future release will remove this method.
+  virtual Status DeleteFile(const std::string& fname);
 
   // Create the specified directory.
   virtual Status CreateDir(const std::string& dirname) = 0;
 
   // Delete the specified directory.
-  virtual Status DeleteDir(const std::string& dirname) = 0;
+  //
+  // The default implementation calls DeleteDir, to support legacy Env
+  // implementations. Updated Env implementations must override RemoveDir and
+  // ignore the existence of DeleteDir. Modern code calling into the Env API
+  // must call RemoveDir instead of DeleteDir.
+  //
+  // A future release will remove DeleteDir and the default implementation of
+  // RemoveDir.
+  virtual Status RemoveDir(const std::string& dirname);
+
+  // DEPRECATED: Modern Env implementations should override RemoveDir instead.
+  //
+  // The default implementation calls RemoveDir, to support legacy Env user
+  // code that calls this method on modern Env implementations. Modern Env user
+  // code should call RemoveDir.
+  //
+  // A future release will remove this method.
+  virtual Status DeleteDir(const std::string& dirname);
 
   // Store the size of fname in *file_size.
   virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0;
@@ -271,7 +300,7 @@ class LEVELDB_EXPORT Logger {
   virtual ~Logger();
 
   // Write an entry to the log file with the specified format.
-  virtual void Logv(const char* format, va_list ap) = 0;
+  virtual void Logv(const char* format, std::va_list ap) = 0;
 };
 
 // Identifies a locked file.
@@ -333,14 +362,14 @@ class LEVELDB_EXPORT EnvWrapper : public Env {
                      std::vector<std::string>* r) override {
     return target_->GetChildren(dir, r);
   }
-  Status DeleteFile(const std::string& f) override {
-    return target_->DeleteFile(f);
+  Status RemoveFile(const std::string& f) override {
+    return target_->RemoveFile(f);
   }
   Status CreateDir(const std::string& d) override {
     return target_->CreateDir(d);
   }
-  Status DeleteDir(const std::string& d) override {
-    return target_->DeleteDir(d);
+  Status RemoveDir(const std::string& d) override {
+    return target_->RemoveDir(d);
   }
   Status GetFileSize(const std::string& f, uint64_t* s) override {
     return target_->GetFileSize(f, s);
@@ -375,7 +404,8 @@ class LEVELDB_EXPORT EnvWrapper : public Env {
 
 }  // namespace leveldb
 
-// Redefine DeleteFile if necessary.
+// This workaround can be removed when leveldb::Env::DeleteFile is removed.
+// Redefine DeleteFile if it was undefined earlier.
 #if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
 #if defined(UNICODE)
 #define DeleteFile DeleteFileW
index b748772..0f285bc 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_INCLUDE_OPTIONS_H_
 #define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_
 
-#include <stddef.h>
+#include <cstddef>
 
 #include "leveldb/export.h"
 
index 2df417d..37cb821 100644 (file)
 #ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_
 #define STORAGE_LEVELDB_INCLUDE_SLICE_H_
 
-#include <assert.h>
-#include <stddef.h>
-#include <string.h>
-
+#include <cassert>
+#include <cstddef>
+#include <cstring>
 #include <string>
 
 #include "leveldb/export.h"
index 25c6013..a30e903 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_INCLUDE_TABLE_H_
 #define STORAGE_LEVELDB_INCLUDE_TABLE_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "leveldb/export.h"
 #include "leveldb/iterator.h"
index 7d8896b..85710c3 100644 (file)
@@ -13,7 +13,7 @@
 #ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_
 #define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 #include "leveldb/export.h"
 #include "leveldb/options.h"
index d50ffeb..8fa5bb9 100644 (file)
@@ -7,9 +7,10 @@
 #include <iostream>
 #include <sstream>
 
+#include "gtest/gtest.h"
 #include "leveldb/db.h"
 #include "leveldb/write_batch.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace {
 
@@ -17,17 +18,15 @@ const int kNumKeys = 1100000;
 
 std::string Key1(int i) {
   char buf[100];
-  snprintf(buf, sizeof(buf), "my_key_%d", i);
+  std::snprintf(buf, sizeof(buf), "my_key_%d", i);
   return buf;
 }
 
 std::string Key2(int i) { return Key1(i) + "_xxx"; }
 
-class Issue178 {};
-
 TEST(Issue178, Test) {
   // Get rid of any state from an old run.
-  std::string dbpath = leveldb::test::TmpDir() + "/leveldb_cbug_test";
+  std::string dbpath = testing::TempDir() + "leveldb_cbug_test";
   DestroyDB(dbpath, leveldb::Options());
 
   // Open database.  Disable compression since it affects the creation
@@ -37,28 +36,28 @@ TEST(Issue178, Test) {
   leveldb::Options db_options;
   db_options.create_if_missing = true;
   db_options.compression = leveldb::kNoCompression;
-  ASSERT_OK(leveldb::DB::Open(db_options, dbpath, &db));
+  ASSERT_LEVELDB_OK(leveldb::DB::Open(db_options, dbpath, &db));
 
   // create first key range
   leveldb::WriteBatch batch;
   for (size_t i = 0; i < kNumKeys; i++) {
     batch.Put(Key1(i), "value for range 1 key");
   }
-  ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch));
+  ASSERT_LEVELDB_OK(db->Write(leveldb::WriteOptions(), &batch));
 
   // create second key range
   batch.Clear();
   for (size_t i = 0; i < kNumKeys; i++) {
     batch.Put(Key2(i), "value for range 2 key");
   }
-  ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch));
+  ASSERT_LEVELDB_OK(db->Write(leveldb::WriteOptions(), &batch));
 
   // delete second key range
   batch.Clear();
   for (size_t i = 0; i < kNumKeys; i++) {
     batch.Delete(Key2(i));
   }
-  ASSERT_OK(db->Write(leveldb::WriteOptions(), &batch));
+  ASSERT_LEVELDB_OK(db->Write(leveldb::WriteOptions(), &batch));
 
   // compact database
   std::string start_key = Key1(0);
@@ -85,4 +84,7 @@ TEST(Issue178, Test) {
 
 }  // anonymous namespace
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 877b2af..4eba23a 100644 (file)
@@ -6,35 +6,34 @@
 // to forward, the current key can be yielded unexpectedly if a new
 // mutation has been added just before the current key.
 
+#include "gtest/gtest.h"
 #include "leveldb/db.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace leveldb {
 
-class Issue200 {};
-
 TEST(Issue200, Test) {
   // Get rid of any state from an old run.
-  std::string dbpath = test::TmpDir() + "/leveldb_issue200_test";
+  std::string dbpath = testing::TempDir() + "leveldb_issue200_test";
   DestroyDB(dbpath, Options());
 
   DB* db;
   Options options;
   options.create_if_missing = true;
-  ASSERT_OK(DB::Open(options, dbpath, &db));
+  ASSERT_LEVELDB_OK(DB::Open(options, dbpath, &db));
 
   WriteOptions write_options;
-  ASSERT_OK(db->Put(write_options, "1", "b"));
-  ASSERT_OK(db->Put(write_options, "2", "c"));
-  ASSERT_OK(db->Put(write_options, "3", "d"));
-  ASSERT_OK(db->Put(write_options, "4", "e"));
-  ASSERT_OK(db->Put(write_options, "5", "f"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "1", "b"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "2", "c"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "3", "d"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "4", "e"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "5", "f"));
 
   ReadOptions 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"));
+  ASSERT_LEVELDB_OK(db->Put(write_options, "25", "cd"));
 
   iter->Seek("5");
   ASSERT_EQ(iter->key().ToString(), "5");
@@ -54,4 +53,7 @@ TEST(Issue200, Test) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index c5fcbfc..c08296a 100644 (file)
@@ -9,9 +9,10 @@
 #include <string>
 #include <vector>
 
+#include "gtest/gtest.h"
 #include "leveldb/db.h"
 #include "leveldb/write_batch.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace leveldb {
 
@@ -37,8 +38,6 @@ std::string CreateRandomString(int32_t index) {
 
 }  // namespace
 
-class Issue320 {};
-
 TEST(Issue320, Test) {
   std::srand(0);
 
@@ -53,8 +52,8 @@ TEST(Issue320, Test) {
   Options options;
   options.create_if_missing = true;
 
-  std::string dbpath = test::TmpDir() + "/leveldb_issue320_test";
-  ASSERT_OK(DB::Open(options, dbpath, &db));
+  std::string dbpath = testing::TempDir() + "leveldb_issue320_test";
+  ASSERT_LEVELDB_OK(DB::Open(options, dbpath, &db));
 
   uint32_t target_size = 10000;
   uint32_t num_items = 0;
@@ -78,7 +77,8 @@ TEST(Issue320, Test) {
           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));
+      ASSERT_LEVELDB_OK(
+          db->Get(readOptions, test_map[index]->first, &old_value));
       if (old_value != test_map[index]->second) {
         std::cout << "ERROR incorrect value returned by Get" << std::endl;
         std::cout << "  count=" << count << std::endl;
@@ -102,7 +102,7 @@ TEST(Issue320, Test) {
       }
     }
 
-    ASSERT_OK(db->Write(writeOptions, &batch));
+    ASSERT_LEVELDB_OK(db->Write(writeOptions, &batch));
 
     if (keep_snapshots && GenerateRandomNumber(10) == 0) {
       int i = GenerateRandomNumber(snapshots.size());
@@ -125,4 +125,7 @@ TEST(Issue320, Test) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index d6a6d01..272671d 100644 (file)
 #cmakedefine01 HAVE_FULLFSYNC
 #endif  // !defined(HAVE_FULLFSYNC)
 
+// Define to 1 if you have a definition for O_CLOEXEC in <fcntl.h>.
+#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
 #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)
-
 #endif  // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
\ No newline at end of file
index 1a8fca2..a665910 100644 (file)
@@ -18,10 +18,6 @@ namespace port {
 // TODO(jorlow): Many of these belong more in the environment class rather than
 //               here. We should try moving them and see if it affects perf.
 
-// The following boolean constant must be true on a little-endian machine
-// and false otherwise.
-static const bool kLittleEndian = true /* or some other expression */;
-
 // ------------------ Threading -------------------
 
 // A Mutex represents an exclusive lock.
index e9cb0e5..2bda48d 100644 (file)
@@ -41,8 +41,6 @@
 namespace leveldb {
 namespace port {
 
-static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN;
-
 class CondVar;
 
 // Thinly wraps std::mutex.
index ad0ee98..3b15257 100644 (file)
@@ -7,6 +7,7 @@
 #include "table/block.h"
 
 #include <algorithm>
+#include <cstdint>
 #include <vector>
 
 #include "leveldb/comparator.h"
@@ -55,9 +56,9 @@ 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 nullptr;
-  *shared = reinterpret_cast<const unsigned char*>(p)[0];
-  *non_shared = reinterpret_cast<const unsigned char*>(p)[1];
-  *value_length = reinterpret_cast<const unsigned char*>(p)[2];
+  *shared = reinterpret_cast<const uint8_t*>(p)[0];
+  *non_shared = reinterpret_cast<const uint8_t*>(p)[1];
+  *value_length = reinterpret_cast<const uint8_t*>(p)[2];
   if ((*shared | *non_shared | *value_length) < 128) {
     // Fast path: all three values are encoded in one byte each
     p += 3;
@@ -123,23 +124,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,11 +161,29 @@ 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;
     uint32_t right = num_restarts_ - 1;
+    int current_key_compare = 0;
+
+    if (Valid()) {
+      // If we're already scanning, use the current position as a starting
+      // point. This is beneficial if the key we're seeking to is ahead of the
+      // current position.
+      current_key_compare = Compare(key_, target);
+      if (current_key_compare < 0) {
+        // key_ is smaller than target
+        left = restart_index_;
+      } else if (current_key_compare > 0) {
+        right = restart_index_;
+      } else {
+        // We're seeking to the key we're already at.
+        return;
+      }
+    }
+
     while (left < right) {
       uint32_t mid = (left + right + 1) / 2;
       uint32_t region_offset = GetRestartPoint(mid);
@@ -188,8 +207,15 @@ class Block::Iter : public Iterator {
       }
     }
 
+    // We might be able to use our current position within the restart block.
+    // This is true if we determined the key we desire is in the current block
+    // and is after than the current key.
+    assert(current_key_compare == 0 || Valid());
+    bool skip_seek = left == restart_index_ && current_key_compare < 0;
+    if (!skip_seek) {
+      SeekToRestartPoint(left);
+    }
     // Linear search (within restart block) for first key >= target
-    SeekToRestartPoint(left);
     while (true) {
       if (!ParseNextKey()) {
         return;
@@ -200,12 +226,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
index c8f1f7b..5224108 100644 (file)
@@ -5,8 +5,8 @@
 #ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_
 #define STORAGE_LEVELDB_TABLE_BLOCK_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
 #include "leveldb/iterator.h"
 
index f7cb1b0..37d4008 100644 (file)
 
 #include "table/block_builder.h"
 
-#include <assert.h>
-
 #include <algorithm>
+#include <cassert>
 
 #include "leveldb/comparator.h"
-#include "leveldb/table_builder.h"
+#include "leveldb/options.h"
 #include "util/coding.h"
 
 namespace leveldb {
index f91f5e6..7a481cd 100644 (file)
@@ -5,8 +5,7 @@
 #ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_
 #define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <vector>
 
 #include "leveldb/slice.h"
index 73b5399..25ab75b 100644 (file)
@@ -9,9 +9,8 @@
 #ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
 #define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
+#include <cstddef>
+#include <cstdint>
 #include <string>
 #include <vector>
 
index 6cdd435..91a6be2 100644 (file)
@@ -4,11 +4,11 @@
 
 #include "table/filter_block.h"
 
+#include "gtest/gtest.h"
 #include "leveldb/filter_policy.h"
 #include "util/coding.h"
 #include "util/hash.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
@@ -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)) {
@@ -36,12 +36,12 @@ class TestHashFilter : public FilterPolicy {
   }
 };
 
-class FilterBlockTest {
+class FilterBlockTest : public testing::Test {
  public:
   TestHashFilter policy_;
 };
 
-TEST(FilterBlockTest, EmptyBuilder) {
+TEST_F(FilterBlockTest, EmptyBuilder) {
   FilterBlockBuilder builder(&policy_);
   Slice block = builder.Finish();
   ASSERT_EQ("\\x00\\x00\\x00\\x00\\x0b", EscapeString(block));
@@ -50,7 +50,7 @@ TEST(FilterBlockTest, EmptyBuilder) {
   ASSERT_TRUE(reader.KeyMayMatch(100000, "foo"));
 }
 
-TEST(FilterBlockTest, SingleChunk) {
+TEST_F(FilterBlockTest, SingleChunk) {
   FilterBlockBuilder builder(&policy_);
   builder.StartBlock(100);
   builder.AddKey("foo");
@@ -71,7 +71,7 @@ TEST(FilterBlockTest, SingleChunk) {
   ASSERT_TRUE(!reader.KeyMayMatch(100, "other"));
 }
 
-TEST(FilterBlockTest, MultiChunk) {
+TEST_F(FilterBlockTest, MultiChunk) {
   FilterBlockBuilder builder(&policy_);
 
   // First filter
@@ -121,4 +121,7 @@ TEST(FilterBlockTest, MultiChunk) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 2ad145c..f6ea304 100644 (file)
@@ -5,8 +5,7 @@
 #ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_
 #define STORAGE_LEVELDB_TABLE_FORMAT_H_
 
-#include <stdint.h>
-
+#include <cstdint>
 #include <string>
 
 #include "leveldb/slice.h"
@@ -53,7 +52,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_; }
index 1bbc6cf..76441b1 100644 (file)
@@ -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();
index b07bc88..29e835f 100644 (file)
@@ -54,13 +54,11 @@ Status Table::Open(const Options& options, RandomAccessFile* file,
 
   // Read the index block
   BlockContents index_block_contents;
-  if (s.ok()) {
-    ReadOptions opt;
-    if (options.paranoid_checks) {
-      opt.verify_checksums = true;
-    }
-    s = ReadBlock(file, opt, footer.index_handle(), &index_block_contents);
+  ReadOptions opt;
+  if (options.paranoid_checks) {
+    opt.verify_checksums = true;
   }
+  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
index 278febf..29a619d 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "leveldb/table_builder.h"
 
-#include <assert.h>
+#include <cassert>
 
 #include "leveldb/comparator.h"
 #include "leveldb/env.h"
index 0974052..190dd0f 100644 (file)
@@ -7,6 +7,7 @@
 #include <map>
 #include <string>
 
+#include "gtest/gtest.h"
 #include "db/dbformat.h"
 #include "db/memtable.h"
 #include "db/write_batch_internal.h"
@@ -18,7 +19,6 @@
 #include "table/block_builder.h"
 #include "table/format.h"
 #include "util/random.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
@@ -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() {}
+  ~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,19 +111,19 @@ class StringSource : public RandomAccessFile {
   StringSource(const Slice& contents)
       : contents_(contents.data(), contents.size()) {}
 
-  virtual ~StringSource() {}
+  ~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");
     }
     if (offset + n > contents_.size()) {
       n = contents_.size() - offset;
     }
-    memcpy(scratch, &contents_[offset], n);
+    std::memcpy(scratch, &contents_[offset], n);
     *result = Slice(scratch, n);
     return Status::OK();
   }
@@ -139,7 +139,7 @@ typedef std::map<std::string, std::string, STLLessThan> 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,20 +211,20 @@ 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);
-      ASSERT_TRUE(builder.status().ok());
+    for (const auto& kvp : data) {
+      builder.Add(kvp.first, kvp.second);
+      EXPECT_LEVELDB_OK(builder.status());
     }
     Status s = builder.Finish();
-    ASSERT_TRUE(s.ok()) << s.ToString();
+    EXPECT_LEVELDB_OK(s);
 
-    ASSERT_EQ(sink.contents().size(), builder.FileSize());
+    EXPECT_EQ(sink.contents().size(), builder.FileSize());
 
     // Open the table
     source_ = new StringSource(sink.contents());
@@ -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,27 +332,27 @@ 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);
-      ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok());
+      batch.Put(kvp.first, kvp.second);
+      EXPECT_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() {
-    std::string name = test::TmpDir() + "/table_testdb";
+    std::string name = testing::TempDir() + "table_testdb";
 
     Options options;
     options.comparator = comparator_;
@@ -365,7 +366,7 @@ class DBConstructor : public Constructor {
     ASSERT_TRUE(status.ok()) << status.ToString();
   }
 
-  const Comparator* comparator_;
+  const Comparator* const comparator_;
   DB* db_;
 };
 
@@ -402,7 +403,7 @@ static const TestArgs kTestArgList[] = {
 };
 static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]);
 
-class Harness {
+class Harness : public testing::Test {
  public:
   Harness() : constructor_(nullptr) {}
 
@@ -484,13 +485,13 @@ class Harness {
     Iterator* iter = constructor_->NewIterator();
     ASSERT_TRUE(!iter->Valid());
     KVMap::const_iterator model_iter = data.begin();
-    if (kVerbose) fprintf(stderr, "---\n");
+    if (kVerbose) std::fprintf(stderr, "---\n");
     for (int i = 0; i < 200; i++) {
       const int toss = rnd->Uniform(5);
       switch (toss) {
         case 0: {
           if (iter->Valid()) {
-            if (kVerbose) fprintf(stderr, "Next\n");
+            if (kVerbose) std::fprintf(stderr, "Next\n");
             iter->Next();
             ++model_iter;
             ASSERT_EQ(ToString(data, model_iter), ToString(iter));
@@ -499,7 +500,7 @@ class Harness {
         }
 
         case 1: {
-          if (kVerbose) fprintf(stderr, "SeekToFirst\n");
+          if (kVerbose) std::fprintf(stderr, "SeekToFirst\n");
           iter->SeekToFirst();
           model_iter = data.begin();
           ASSERT_EQ(ToString(data, model_iter), ToString(iter));
@@ -510,7 +511,7 @@ class Harness {
           std::string key = PickRandomKey(rnd, keys);
           model_iter = data.lower_bound(key);
           if (kVerbose)
-            fprintf(stderr, "Seek '%s'\n", EscapeString(key).c_str());
+            std::fprintf(stderr, "Seek '%s'\n", EscapeString(key).c_str());
           iter->Seek(Slice(key));
           ASSERT_EQ(ToString(data, model_iter), ToString(iter));
           break;
@@ -518,7 +519,7 @@ class Harness {
 
         case 3: {
           if (iter->Valid()) {
-            if (kVerbose) fprintf(stderr, "Prev\n");
+            if (kVerbose) std::fprintf(stderr, "Prev\n");
             iter->Prev();
             if (model_iter == data.begin()) {
               model_iter = data.end();  // Wrap around to invalid value
@@ -531,7 +532,7 @@ class Harness {
         }
 
         case 4: {
-          if (kVerbose) fprintf(stderr, "SeekToLast\n");
+          if (kVerbose) std::fprintf(stderr, "SeekToLast\n");
           iter->SeekToLast();
           if (keys.empty()) {
             model_iter = data.end();
@@ -608,7 +609,7 @@ class Harness {
 };
 
 // Test empty table/block.
-TEST(Harness, Empty) {
+TEST_F(Harness, Empty) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 1);
@@ -619,7 +620,7 @@ TEST(Harness, Empty) {
 // Special test for a block with no restart entries.  The C++ leveldb
 // code never generates such blocks, but the Java version of leveldb
 // seems to.
-TEST(Harness, ZeroRestartPointsInBlock) {
+TEST_F(Harness, ZeroRestartPointsInBlock) {
   char data[sizeof(uint32_t)];
   memset(data, 0, sizeof(data));
   BlockContents contents;
@@ -638,7 +639,7 @@ TEST(Harness, ZeroRestartPointsInBlock) {
 }
 
 // Test the empty key
-TEST(Harness, SimpleEmptyKey) {
+TEST_F(Harness, SimpleEmptyKey) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 1);
@@ -647,7 +648,7 @@ TEST(Harness, SimpleEmptyKey) {
   }
 }
 
-TEST(Harness, SimpleSingle) {
+TEST_F(Harness, SimpleSingle) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 2);
@@ -656,7 +657,7 @@ TEST(Harness, SimpleSingle) {
   }
 }
 
-TEST(Harness, SimpleMulti) {
+TEST_F(Harness, SimpleMulti) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 3);
@@ -667,7 +668,7 @@ TEST(Harness, SimpleMulti) {
   }
 }
 
-TEST(Harness, SimpleSpecialKey) {
+TEST_F(Harness, SimpleSpecialKey) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 4);
@@ -676,15 +677,15 @@ TEST(Harness, SimpleSpecialKey) {
   }
 }
 
-TEST(Harness, Randomized) {
+TEST_F(Harness, Randomized) {
   for (int i = 0; i < kNumTestArgs; i++) {
     Init(kTestArgList[i]);
     Random rnd(test::RandomSeed() + 5);
     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);
+        std::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;
@@ -696,7 +697,7 @@ TEST(Harness, Randomized) {
   }
 }
 
-TEST(Harness, RandomizedLongDB) {
+TEST_F(Harness, RandomizedLongDB) {
   Random rnd(test::RandomSeed());
   TestArgs args = {DB_TEST, false, 16};
   Init(args);
@@ -713,15 +714,13 @@ TEST(Harness, RandomizedLongDB) {
   for (int level = 0; level < config::kNumLevels; level++) {
     std::string value;
     char name[100];
-    snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
+    std::snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
     ASSERT_TRUE(db()->GetProperty(name, &value));
     files += atoi(value.c_str());
   }
   ASSERT_GT(files, 0);
 }
 
-class MemTableTest {};
-
 TEST(MemTableTest, Simple) {
   InternalKeyComparator cmp(BytewiseComparator());
   MemTable* memtable = new MemTable(cmp);
@@ -737,8 +736,8 @@ TEST(MemTableTest, Simple) {
   Iterator* iter = memtable->NewIterator();
   iter->SeekToFirst();
   while (iter->Valid()) {
-    fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(),
-            iter->value().ToString().c_str());
+    std::fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(),
+                 iter->value().ToString().c_str());
     iter->Next();
   }
 
@@ -749,15 +748,13 @@ TEST(MemTableTest, Simple) {
 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)(high));
+    std::fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
+                 (unsigned long long)(val), (unsigned long long)(low),
+                 (unsigned long long)(high));
   }
   return result;
 }
 
-class TableTest {};
-
 TEST(TableTest, ApproximateOffsetOfPlain) {
   TableConstructor c(BytewiseComparator());
   c.Add("k01", "hello");
@@ -795,7 +792,7 @@ static bool SnappyCompressionSupported() {
 
 TEST(TableTest, ApproximateOffsetOfCompressed) {
   if (!SnappyCompressionSupported()) {
-    fprintf(stderr, "skipping compression tests\n");
+    std::fprintf(stderr, "skipping compression tests\n");
     return;
   }
 
@@ -831,4 +828,7 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 5340a4d..144790d 100644 (file)
@@ -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();
@@ -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);
index f34095c..90226fe 100644 (file)
@@ -4,17 +4,15 @@
 
 #include "util/arena.h"
 
+#include "gtest/gtest.h"
 #include "util/random.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
-class ArenaTest {};
-
 TEST(ArenaTest, Empty) { Arena arena; }
 
 TEST(ArenaTest, Simple) {
-  std::vector<std::pair<size_t, char*> > allocated;
+  std::vector<std::pair<size_t, char*>> allocated;
   Arena arena;
   const int N = 100000;
   size_t bytes = 0;
@@ -62,4 +60,7 @@ TEST(ArenaTest, Simple) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 7f97464..87547a7 100644 (file)
@@ -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;
 
index 436daa9..520473e 100644 (file)
@@ -2,11 +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 "gtest/gtest.h"
 #include "leveldb/filter_policy.h"
-
 #include "util/coding.h"
 #include "util/logging.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
@@ -18,7 +17,7 @@ static Slice Key(int i, char* buffer) {
   return Slice(buffer, sizeof(uint32_t));
 }
 
-class BloomTest {
+class BloomTest : public testing::Test {
  public:
   BloomTest() : policy_(NewBloomFilterPolicy(10)) {}
 
@@ -46,14 +45,14 @@ class BloomTest {
   size_t FilterSize() const { return filter_.size(); }
 
   void DumpFilter() {
-    fprintf(stderr, "F(");
+    std::fprintf(stderr, "F(");
     for (size_t i = 0; i + 1 < filter_.size(); i++) {
       const unsigned int c = static_cast<unsigned int>(filter_[i]);
       for (int j = 0; j < 8; j++) {
-        fprintf(stderr, "%c", (c & (1 << j)) ? '1' : '.');
+        std::fprintf(stderr, "%c", (c & (1 << j)) ? '1' : '.');
       }
     }
-    fprintf(stderr, ")\n");
+    std::fprintf(stderr, ")\n");
   }
 
   bool Matches(const Slice& s) {
@@ -80,12 +79,12 @@ class BloomTest {
   std::vector<std::string> keys_;
 };
 
-TEST(BloomTest, EmptyFilter) {
+TEST_F(BloomTest, EmptyFilter) {
   ASSERT_TRUE(!Matches("hello"));
   ASSERT_TRUE(!Matches("world"));
 }
 
-TEST(BloomTest, Small) {
+TEST_F(BloomTest, Small) {
   Add("hello");
   Add("world");
   ASSERT_TRUE(Matches("hello"));
@@ -107,7 +106,7 @@ static int NextLength(int length) {
   return length;
 }
 
-TEST(BloomTest, VaryingLengths) {
+TEST_F(BloomTest, VaryingLengths) {
   char buffer[sizeof(int)];
 
   // Count number of filters that significantly exceed the false positive rate
@@ -133,8 +132,9 @@ TEST(BloomTest, VaryingLengths) {
     // Check false positive rate
     double rate = FalsePositiveRate();
     if (kVerbose >= 1) {
-      fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n",
-              rate * 100.0, length, static_cast<int>(FilterSize()));
+      std::fprintf(stderr,
+                   "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n",
+                   rate * 100.0, length, static_cast<int>(FilterSize()));
     }
     ASSERT_LE(rate, 0.02);  // Must not be over 2%
     if (rate > 0.0125)
@@ -143,8 +143,8 @@ TEST(BloomTest, VaryingLengths) {
       good_filters++;
   }
   if (kVerbose >= 1) {
-    fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters,
-            mediocre_filters);
+    std::fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters,
+                 mediocre_filters);
   }
   ASSERT_LE(mediocre_filters, good_filters / 5);
 }
@@ -153,4 +153,7 @@ TEST(BloomTest, VaryingLengths) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 0f801cc..ad1e9a2 100644 (file)
@@ -2,11 +2,12 @@
 // 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 <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
 #include "leveldb/cache.h"
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
 #include "port/port.h"
 #include "port/thread_annotations.h"
 #include "util/hash.h"
@@ -278,7 +279,7 @@ Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value,
   e->hash = hash;
   e->in_cache = false;
   e->refs = 1;  // for the returned handle.
-  memcpy(e->key_data, key.data(), key.size());
+  std::memcpy(e->key_data, key.data(), key.size());
 
   if (capacity_ > 0) {
     e->refs++;  // for the cache's reference.
@@ -354,37 +355,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<LRUHandle*>(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<LRUHandle*>(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();
index 974334b..79cfc27 100644 (file)
@@ -5,8 +5,9 @@
 #include "leveldb/cache.h"
 
 #include <vector>
+
+#include "gtest/gtest.h"
 #include "util/coding.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
@@ -23,14 +24,14 @@ static int DecodeKey(const Slice& k) {
 static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }
 static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); }
 
-class CacheTest {
+class CacheTest : public testing::Test {
  public:
   static void Deleter(const Slice& key, void* v) {
     current_->deleted_keys_.push_back(DecodeKey(key));
     current_->deleted_values_.push_back(DecodeValue(v));
   }
 
-  static const int kCacheSize = 1000;
+  static constexpr int kCacheSize = 1000;
   std::vector<int> deleted_keys_;
   std::vector<int> deleted_values_;
   Cache* cache_;
@@ -59,12 +60,11 @@ class CacheTest {
   }
 
   void Erase(int key) { cache_->Erase(EncodeKey(key)); }
-
   static CacheTest* current_;
 };
 CacheTest* CacheTest::current_;
 
-TEST(CacheTest, HitAndMiss) {
+TEST_F(CacheTest, HitAndMiss) {
   ASSERT_EQ(-1, Lookup(100));
 
   Insert(100, 101);
@@ -87,7 +87,7 @@ TEST(CacheTest, HitAndMiss) {
   ASSERT_EQ(101, deleted_values_[0]);
 }
 
-TEST(CacheTest, Erase) {
+TEST_F(CacheTest, Erase) {
   Erase(200);
   ASSERT_EQ(0, deleted_keys_.size());
 
@@ -106,7 +106,7 @@ TEST(CacheTest, Erase) {
   ASSERT_EQ(1, deleted_keys_.size());
 }
 
-TEST(CacheTest, EntriesArePinned) {
+TEST_F(CacheTest, EntriesArePinned) {
   Insert(100, 101);
   Cache::Handle* h1 = cache_->Lookup(EncodeKey(100));
   ASSERT_EQ(101, DecodeValue(cache_->Value(h1)));
@@ -131,7 +131,7 @@ TEST(CacheTest, EntriesArePinned) {
   ASSERT_EQ(102, deleted_values_[1]);
 }
 
-TEST(CacheTest, EvictionPolicy) {
+TEST_F(CacheTest, EvictionPolicy) {
   Insert(100, 101);
   Insert(200, 201);
   Insert(300, 301);
@@ -150,7 +150,7 @@ TEST(CacheTest, EvictionPolicy) {
   cache_->Release(h);
 }
 
-TEST(CacheTest, UseExceedsCacheSize) {
+TEST_F(CacheTest, UseExceedsCacheSize) {
   // Overfill the cache, keeping handles on all inserted entries.
   std::vector<Cache::Handle*> h;
   for (int i = 0; i < kCacheSize + 100; i++) {
@@ -167,7 +167,7 @@ TEST(CacheTest, UseExceedsCacheSize) {
   }
 }
 
-TEST(CacheTest, HeavyEntries) {
+TEST_F(CacheTest, HeavyEntries) {
   // Add a bunch of light and heavy entries and then count the combined
   // size of items still in the cache, which must be approximately the
   // same as the total capacity.
@@ -194,13 +194,13 @@ TEST(CacheTest, HeavyEntries) {
   ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10);
 }
 
-TEST(CacheTest, NewId) {
+TEST_F(CacheTest, NewId) {
   uint64_t a = cache_->NewId();
   uint64_t b = cache_->NewId();
   ASSERT_NE(a, b);
 }
 
-TEST(CacheTest, Prune) {
+TEST_F(CacheTest, Prune) {
   Insert(1, 100);
   Insert(2, 200);
 
@@ -213,7 +213,7 @@ TEST(CacheTest, Prune) {
   ASSERT_EQ(-1, Lookup(2));
 }
 
-TEST(CacheTest, ZeroSizeCache) {
+TEST_F(CacheTest, ZeroSizeCache) {
   delete cache_;
   cache_ = NewLRUCache(0);
 
@@ -223,4 +223,7 @@ TEST(CacheTest, ZeroSizeCache) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index e2089df..df3fa10 100644 (file)
@@ -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);
@@ -46,7 +20,7 @@ void PutFixed64(std::string* dst, uint64_t value) {
 
 char* EncodeVarint32(char* dst, uint32_t v) {
   // Operate on characters as unsigneds
-  unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
   static const int B = 128;
   if (v < (1 << 7)) {
     *(ptr++) = v;
@@ -80,12 +54,12 @@ void PutVarint32(std::string* dst, uint32_t v) {
 
 char* EncodeVarint64(char* dst, uint64_t v) {
   static const int B = 128;
-  unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
   while (v >= B) {
     *(ptr++) = v | B;
     v >>= 7;
   }
-  *(ptr++) = static_cast<unsigned char>(v);
+  *(ptr++) = static_cast<uint8_t>(v);
   return reinterpret_cast<char*>(ptr);
 }
 
@@ -113,7 +87,7 @@ 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) {
-    uint32_t byte = *(reinterpret_cast<const unsigned char*>(p));
+    uint32_t byte = *(reinterpret_cast<const uint8_t*>(p));
     p++;
     if (byte & 128) {
       // More bytes are present
@@ -142,7 +116,7 @@ bool GetVarint32(Slice* input, uint32_t* value) {
 const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) {
   uint64_t result = 0;
   for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) {
-    uint64_t byte = *(reinterpret_cast<const unsigned char*>(p));
+    uint64_t byte = *(reinterpret_cast<const uint8_t*>(p));
     p++;
     if (byte & 128) {
       // More bytes are present
index d9eeaa3..f0bb57b 100644 (file)
@@ -10,9 +10,8 @@
 #ifndef STORAGE_LEVELDB_UTIL_CODING_H_
 #define STORAGE_LEVELDB_UTIL_CODING_H_
 
-#include <stdint.h>
-#include <string.h>
-
+#include <cstdint>
+#include <cstring>
 #include <string>
 
 #include "leveldb/slice.h"
@@ -44,44 +43,63 @@ const char* GetVarint64Ptr(const char* p, const char* limit, 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
-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);
 
+// 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<uint8_t*>(dst);
+
+  // Recent clang and gcc optimize this to a single mov / str instruction.
+  buffer[0] = static_cast<uint8_t>(value);
+  buffer[1] = static_cast<uint8_t>(value >> 8);
+  buffer[2] = static_cast<uint8_t>(value >> 16);
+  buffer[3] = static_cast<uint8_t>(value >> 24);
+}
+
+inline void EncodeFixed64(char* dst, uint64_t value) {
+  uint8_t* const buffer = reinterpret_cast<uint8_t*>(dst);
+
+  // Recent clang and gcc optimize this to a single mov / str instruction.
+  buffer[0] = static_cast<uint8_t>(value);
+  buffer[1] = static_cast<uint8_t>(value >> 8);
+  buffer[2] = static_cast<uint8_t>(value >> 16);
+  buffer[3] = static_cast<uint8_t>(value >> 24);
+  buffer[4] = static_cast<uint8_t>(value >> 32);
+  buffer[5] = static_cast<uint8_t>(value >> 40);
+  buffer[6] = static_cast<uint8_t>(value >> 48);
+  buffer[7] = static_cast<uint8_t>(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) {
-  if (port::kLittleEndian) {
-    // Load the raw bytes
-    uint32_t result;
-    memcpy(&result, ptr, sizeof(result));  // gcc optimizes this to a plain load
-    return result;
-  } else {
-    return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) |
-            (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) |
-            (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) |
-            (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));
-  }
+  const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
+
+  // Recent clang and gcc optimize this to a single mov / ldr instruction.
+  return (static_cast<uint32_t>(buffer[0])) |
+         (static_cast<uint32_t>(buffer[1]) << 8) |
+         (static_cast<uint32_t>(buffer[2]) << 16) |
+         (static_cast<uint32_t>(buffer[3]) << 24);
 }
 
 inline uint64_t DecodeFixed64(const char* ptr) {
-  if (port::kLittleEndian) {
-    // Load the raw bytes
-    uint64_t result;
-    memcpy(&result, ptr, sizeof(result));  // gcc optimizes this to a plain load
-    return result;
-  } else {
-    uint64_t lo = DecodeFixed32(ptr);
-    uint64_t hi = DecodeFixed32(ptr + 4);
-    return (hi << 32) | lo;
-  }
+  const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
+
+  // Recent clang and gcc optimize this to a single mov / ldr instruction.
+  return (static_cast<uint64_t>(buffer[0])) |
+         (static_cast<uint64_t>(buffer[1]) << 8) |
+         (static_cast<uint64_t>(buffer[2]) << 16) |
+         (static_cast<uint64_t>(buffer[3]) << 24) |
+         (static_cast<uint64_t>(buffer[4]) << 32) |
+         (static_cast<uint64_t>(buffer[5]) << 40) |
+         (static_cast<uint64_t>(buffer[6]) << 48) |
+         (static_cast<uint64_t>(buffer[7]) << 56);
 }
 
 // Internal routine for use by fallback path of GetVarint32Ptr
@@ -90,7 +108,7 @@ const char* GetVarint32PtrFallback(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<const unsigned char*>(p));
+    uint32_t result = *(reinterpret_cast<const uint8_t*>(p));
     if ((result & 128) == 0) {
       *value = result;
       return p + 1;
index 0d2a0c5..aa6c748 100644 (file)
@@ -2,15 +2,14 @@
 // 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 <vector>
 
-#include "util/coding.h"
-#include "util/testharness.h"
+#include "gtest/gtest.h"
 
 namespace leveldb {
 
-class Coding {};
-
 TEST(Coding, Fixed32) {
   std::string s;
   for (uint32_t v = 0; v < 100000; v++) {
@@ -193,4 +192,7 @@ TEST(Coding, Strings) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 26d1eb3..c5766e9 100644 (file)
@@ -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 <algorithm>
 #include <cstdint>
 #include <string>
+#include <type_traits>
 
-#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++) {
index c2e61f7..3f18908 100644 (file)
@@ -6,8 +6,8 @@
 
 #include "util/crc32c.h"
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
 #include "port/port.h"
 #include "util/coding.h"
index 98fabb0..b420b5f 100644 (file)
@@ -5,8 +5,8 @@
 #ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_
 #define STORAGE_LEVELDB_UTIL_CRC32C_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
 namespace leveldb {
 namespace crc32c {
index dbd2ba4..647e561 100644 (file)
@@ -3,13 +3,12 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include "util/crc32c.h"
-#include "util/testharness.h"
+
+#include "gtest/gtest.h"
 
 namespace leveldb {
 namespace crc32c {
 
-class CRC {};
-
 TEST(CRC, StandardResults) {
   // From rfc3720 section B.4.
   char buf[32];
@@ -30,7 +29,7 @@ TEST(CRC, StandardResults) {
   }
   ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf)));
 
-  unsigned char data[48] = {
+  uint8_t 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,
@@ -56,4 +55,7 @@ TEST(CRC, Mask) {
 }  // namespace crc32c
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 6cd5f2e..a53b230 100644 (file)
@@ -4,27 +4,43 @@
 
 #include "leveldb/env.h"
 
+#include <cstdarg>
+
+// This workaround can be removed when leveldb::Env::DeleteFile is removed.
+// See env.h for justification.
+#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
+#undef DeleteFile
+#endif
+
 namespace leveldb {
 
-Env::~Env() {}
+Env::Env() = default;
+
+Env::~Env() = default;
 
 Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) {
   return Status::NotSupported("NewAppendableFile", fname);
 }
 
-SequentialFile::~SequentialFile() {}
+Status Env::RemoveDir(const std::string& dirname) { return DeleteDir(dirname); }
+Status Env::DeleteDir(const std::string& dirname) { return RemoveDir(dirname); }
+
+Status Env::RemoveFile(const std::string& fname) { return DeleteFile(fname); }
+Status Env::DeleteFile(const std::string& fname) { return RemoveFile(fname); }
+
+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) {
-    va_list ap;
+    std::va_list ap;
     va_start(ap, format);
     info_log->Logv(format, ap);
     va_end(ap);
@@ -47,7 +63,7 @@ static Status DoWriteStringToFile(Env* env, const Slice& data,
   }
   delete file;  // Will auto-close if we did not close above
   if (!s.ok()) {
-    env->DeleteFile(fname);
+    env->RemoveFile(fname);
   }
   return s;
 }
index 8c74f5a..d84cd1e 100644 (file)
@@ -45,9 +45,16 @@ 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.
+// 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 kOpenBaseFlags = O_CLOEXEC;
+#else
+constexpr const int kOpenBaseFlags = 0;
+#endif  // defined(HAVE_O_CLOEXEC)
+
 constexpr const size_t kWritableFileBufferSize = 65536;
 
 Status PosixError(const std::string& context, int error_number) {
@@ -165,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);
+      fd = ::open(filename_.c_str(), O_RDONLY | kOpenBaseFlags);
       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 | kOpenBaseFlags);
     if (fd < 0) {
       status = PosixError(dirname_, errno);
     } else {
@@ -456,7 +463,7 @@ class PosixFileLock : public FileLock {
 
 // Tracks the files locked by PosixEnv::LockFile().
 //
-// We maintain a separate set instead of relying on fcntrl(F_SETLK) because
+// We maintain a separate set instead of relying on fcntl(F_SETLK) because
 // fcntl(F_SETLK) does not provide any protection against multiple uses from the
 // same process.
 //
@@ -484,14 +491,15 @@ class PosixEnv : public Env {
  public:
   PosixEnv();
   ~PosixEnv() override {
-    static char msg[] = "PosixEnv singleton destroyed. Unsupported behavior!\n";
+    static const char msg[] =
+        "PosixEnv singleton destroyed. Unsupported behavior!\n";
     std::fwrite(msg, 1, sizeof(msg), stderr);
     std::abort();
   }
 
   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 | kOpenBaseFlags);
     if (fd < 0) {
       *result = nullptr;
       return PosixError(filename, errno);
@@ -504,7 +512,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 | kOpenBaseFlags);
     if (fd < 0) {
       return PosixError(filename, errno);
     }
@@ -536,7 +544,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, 0644);
+    int fd = ::open(filename.c_str(),
+                    O_TRUNC | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
     if (fd < 0) {
       *result = nullptr;
       return PosixError(filename, errno);
@@ -548,7 +557,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, 0644);
+    int fd = ::open(filename.c_str(),
+                    O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
     if (fd < 0) {
       *result = nullptr;
       return PosixError(filename, errno);
@@ -577,7 +587,7 @@ class PosixEnv : public Env {
     return Status::OK();
   }
 
-  Status DeleteFile(const std::string& filename) override {
+  Status RemoveFile(const std::string& filename) override {
     if (::unlink(filename.c_str()) != 0) {
       return PosixError(filename, errno);
     }
@@ -591,7 +601,7 @@ class PosixEnv : public Env {
     return Status::OK();
   }
 
-  Status DeleteDir(const std::string& dirname) override {
+  Status RemoveDir(const std::string& dirname) override {
     if (::rmdir(dirname.c_str()) != 0) {
       return PosixError(dirname, errno);
     }
@@ -618,7 +628,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 | kOpenBaseFlags, 0644);
     if (fd < 0) {
       return PosixError(filename, errno);
     }
@@ -654,7 +664,10 @@ class PosixEnv : public Env {
                 void* background_work_arg) override;
 
   void StartThread(void (*thread_main)(void* thread_main_arg),
-                   void* thread_main_arg) override;
+                   void* thread_main_arg) override {
+    std::thread new_thread(thread_main, thread_main_arg);
+    new_thread.detach();
+  }
 
   Status GetTestDirectory(std::string* result) override {
     const char* env = std::getenv("TEST_TMPDIR");
@@ -674,8 +687,16 @@ class PosixEnv : public Env {
   }
 
   Status NewLogger(const std::string& filename, Logger** result) override {
-    std::FILE* fp = std::fopen(filename.c_str(), "w");
+    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 {
@@ -691,7 +712,9 @@ class PosixEnv : public Env {
     return static_cast<uint64_t>(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec;
   }
 
-  void SleepForMicroseconds(int micros) override { ::usleep(micros); }
+  void SleepForMicroseconds(int micros) override {
+    std::this_thread::sleep_for(std::chrono::microseconds(micros));
+  }
 
  private:
   void BackgroundThreadMain();
@@ -852,12 +875,6 @@ using PosixDefaultEnv = SingletonEnv<PosixEnv>;
 
 }  // namespace
 
-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();
-}
-
 void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
   PosixDefaultEnv::AssertEnvNotInitialized();
   g_open_read_only_file_limit = limit;
index 4b72934..da264f0 100644 (file)
 // 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 <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "gtest/gtest.h"
 #include "leveldb/env.h"
 #include "port/port.h"
 #include "util/env_posix_test_helper.h"
-#include "util/testharness.h"
+#include "util/testutil.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<char>* GetArgvZero() {
+  static std::vector<char> 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<int>* 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<int>& baseline_open_fds, int* result_fd) {
+  std::unordered_set<int> 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<int>& 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<char> 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;
 static const int kMMapLimit = 4;
 
-class EnvPosixTest {
+class EnvPosixTest : public testing::Test {
  public:
   static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
     EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
@@ -24,42 +181,173 @@ class EnvPosixTest {
   Env* env_;
 };
 
-TEST(EnvPosixTest, TestOpenOnRead) {
+TEST_F(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));
+  ASSERT_LEVELDB_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 = std::fopen(test_file.c_str(), "we");
   ASSERT_TRUE(f != nullptr);
   const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
   fputs(kFileData, f);
-  fclose(f);
+  std::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]));
+    ASSERT_LEVELDB_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_LEVELDB_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));
+  ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
+}
+
+#if HAVE_O_CLOEXEC
+
+TEST_F(EnvPosixTest, TestCloseOnExecSequentialFile) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_sequential.txt";
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
+
+  leveldb::SequentialFile* file = nullptr;
+  ASSERT_LEVELDB_OK(env_->NewSequentialFile(file_path, &file));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  delete file;
+
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
+}
+
+TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_random_access.txt";
+  ASSERT_LEVELDB_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_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
+  }
+
+  leveldb::RandomAccessFile* file = nullptr;
+  ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &file));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  delete file;
+
+  for (int i = 0; i < kReadOnlyFileLimit; i++) {
+    delete mmapped_files[i];
+  }
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
+}
+
+TEST_F(EnvPosixTest, TestCloseOnExecWritableFile) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_writable.txt";
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
+
+  leveldb::WritableFile* file = nullptr;
+  ASSERT_LEVELDB_OK(env_->NewWritableFile(file_path, &file));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  delete file;
+
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
+}
+
+TEST_F(EnvPosixTest, TestCloseOnExecAppendableFile) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_appendable.txt";
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
+
+  leveldb::WritableFile* file = nullptr;
+  ASSERT_LEVELDB_OK(env_->NewAppendableFile(file_path, &file));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  delete file;
+
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
+}
+
+TEST_F(EnvPosixTest, TestCloseOnExecLockFile) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_lock.txt";
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
+
+  leveldb::FileLock* lock = nullptr;
+  ASSERT_LEVELDB_OK(env_->LockFile(file_path, &lock));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  ASSERT_LEVELDB_OK(env_->UnlockFile(lock));
+
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
 }
 
+TEST_F(EnvPosixTest, TestCloseOnExecLogger) {
+  std::unordered_set<int> open_fds;
+  GetOpenFileDescriptors(&open_fds);
+
+  std::string test_dir;
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
+  std::string file_path = test_dir + "/close_on_exec_logger.txt";
+  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
+
+  leveldb::Logger* file = nullptr;
+  ASSERT_LEVELDB_OK(env_->NewLogger(file_path, &file));
+  CheckCloseOnExecDoesNotLeakFDs(open_fds);
+  delete file;
+
+  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
+}
+
+#endif  // HAVE_O_CLOEXEC
+
 }  // namespace leveldb
 
 int main(int argc, char** argv) {
+#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);
-  return leveldb::test::RunAllTests();
+
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
 }
index 9e2ad1e..491ef43 100644 (file)
@@ -5,44 +5,33 @@
 #include "leveldb/env.h"
 
 #include <algorithm>
-#include <atomic>
 
+#include "gtest/gtest.h"
 #include "port/port.h"
 #include "port/thread_annotations.h"
 #include "util/mutexlock.h"
-#include "util/testharness.h"
 #include "util/testutil.h"
 
 namespace leveldb {
 
 static const int kDelayMicros = 100000;
 
-class EnvTest {
+class EnvTest : public testing::Test {
  public:
   EnvTest() : env_(Env::Default()) {}
 
   Env* env_;
 };
 
-namespace {
-
-static void SetAtomicBool(void* atomic_bool_ptr) {
-  std::atomic<bool>* atomic_bool =
-      reinterpret_cast<std::atomic<bool>*>(atomic_bool_ptr);
-  atomic_bool->store(true, std::memory_order_relaxed);
-}
-
-}  // namespace
-
-TEST(EnvTest, ReadWrite) {
+TEST_F(EnvTest, ReadWrite) {
   Random rnd(test::RandomSeed());
 
   // Get file to use for testing.
   std::string test_dir;
-  ASSERT_OK(env_->GetTestDirectory(&test_dir));
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
   std::string test_file_name = test_dir + "/open_on_read.txt";
   WritableFile* writable_file;
-  ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+  ASSERT_LEVELDB_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;
@@ -51,26 +40,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(writable_file->Append(r));
+    ASSERT_LEVELDB_OK(writable_file->Append(r));
     data += r;
     if (rnd.OneIn(10)) {
-      ASSERT_OK(writable_file->Flush());
+      ASSERT_LEVELDB_OK(writable_file->Flush());
     }
   }
-  ASSERT_OK(writable_file->Sync());
-  ASSERT_OK(writable_file->Close());
+  ASSERT_LEVELDB_OK(writable_file->Sync());
+  ASSERT_LEVELDB_OK(writable_file->Close());
   delete writable_file;
 
   // Read all data using a sequence of randomly sized reads.
   SequentialFile* sequential_file;
-  ASSERT_OK(env_->NewSequentialFile(test_file_name, &sequential_file));
+  ASSERT_LEVELDB_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<int>(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(sequential_file->Read(len, &read, &scratch[0]));
+    ASSERT_LEVELDB_OK(sequential_file->Read(len, &read, &scratch[0]));
     if (len > 0) {
       ASSERT_GT(read.size(), 0);
     }
@@ -81,46 +70,74 @@ TEST(EnvTest, ReadWrite) {
   delete sequential_file;
 }
 
-TEST(EnvTest, RunImmediately) {
-  std::atomic<bool> called(false);
-  env_->Schedule(&SetAtomicBool, &called);
-  env_->SleepForMicroseconds(kDelayMicros);
-  ASSERT_TRUE(called.load(std::memory_order_relaxed));
+TEST_F(EnvTest, RunImmediately) {
+  struct RunState {
+    port::Mutex mu;
+    port::CondVar cvar{&mu};
+    bool called = false;
+
+    static void Run(void* arg) {
+      RunState* state = reinterpret_cast<RunState*>(arg);
+      MutexLock l(&state->mu);
+      ASSERT_EQ(state->called, false);
+      state->called = true;
+      state->cvar.Signal();
+    }
+  };
+
+  RunState state;
+  env_->Schedule(&RunState::Run, &state);
+
+  MutexLock l(&state.mu);
+  while (!state.called) {
+    state.cvar.Wait();
+  }
 }
 
-TEST(EnvTest, RunMany) {
-  std::atomic<int> last_id(0);
+TEST_F(EnvTest, RunMany) {
+  struct RunState {
+    port::Mutex mu;
+    port::CondVar cvar{&mu};
+    int last_id = 0;
+  };
 
   struct Callback {
-    std::atomic<int>* const last_id_ptr_;  // Pointer to shared state.
+    RunState* state_;  // Pointer to shared state.
     const int id_;  // Order# for the execution of this callback.
 
-    Callback(std::atomic<int>* last_id_ptr, int id)
-        : last_id_ptr_(last_id_ptr), id_(id) {}
+    Callback(RunState* s, int id) : state_(s), id_(id) {}
 
     static void Run(void* arg) {
       Callback* callback = reinterpret_cast<Callback*>(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);
+      RunState* state = callback->state_;
+
+      MutexLock l(&state->mu);
+      ASSERT_EQ(state->last_id, callback->id_ - 1);
+      state->last_id = callback->id_;
+      state->cvar.Signal();
     }
   };
 
-  Callback callback1(&last_id, 1);
-  Callback callback2(&last_id, 2);
-  Callback callback3(&last_id, 3);
-  Callback callback4(&last_id, 4);
+  RunState state;
+  Callback callback1(&state, 1);
+  Callback callback2(&state, 2);
+  Callback callback3(&state, 3);
+  Callback callback4(&state, 4);
   env_->Schedule(&Callback::Run, &callback1);
   env_->Schedule(&Callback::Run, &callback2);
   env_->Schedule(&Callback::Run, &callback3);
   env_->Schedule(&Callback::Run, &callback4);
 
-  env_->SleepForMicroseconds(kDelayMicros);
-  ASSERT_EQ(4, last_id.load(std::memory_order_relaxed));
+  MutexLock l(&state.mu);
+  while (state.last_id != 4) {
+    state.cvar.Wait();
+  }
 }
 
 struct State {
   port::Mutex mu;
+  port::CondVar cvar{&mu};
+
   int val GUARDED_BY(mu);
   int num_running GUARDED_BY(mu);
 
@@ -132,32 +149,27 @@ static void ThreadBody(void* arg) {
   s->mu.Lock();
   s->val += 1;
   s->num_running -= 1;
+  s->cvar.Signal();
   s->mu.Unlock();
 }
 
-TEST(EnvTest, StartThread) {
+TEST_F(EnvTest, StartThread) {
   State state(0, 3);
   for (int i = 0; i < 3; i++) {
     env_->StartThread(&ThreadBody, &state);
   }
-  while (true) {
-    state.mu.Lock();
-    int num = state.num_running;
-    state.mu.Unlock();
-    if (num == 0) {
-      break;
-    }
-    env_->SleepForMicroseconds(kDelayMicros);
-  }
 
   MutexLock l(&state.mu);
+  while (state.num_running != 0) {
+    state.cvar.Wait();
+  }
   ASSERT_EQ(state.val, 3);
 }
 
-TEST(EnvTest, TestOpenNonExistentFile) {
+TEST_F(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));
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
 
   std::string non_existent_file = test_dir + "/non_existent_file";
   ASSERT_TRUE(!env_->FileExists(non_existent_file));
@@ -172,54 +184,57 @@ TEST(EnvTest, TestOpenNonExistentFile) {
   ASSERT_TRUE(status.IsNotFound());
 }
 
-TEST(EnvTest, ReopenWritableFile) {
+TEST_F(EnvTest, ReopenWritableFile) {
   std::string test_dir;
-  ASSERT_OK(env_->GetTestDirectory(&test_dir));
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
   std::string test_file_name = test_dir + "/reopen_writable_file.txt";
-  env_->DeleteFile(test_file_name);
+  env_->RemoveFile(test_file_name);
 
   WritableFile* writable_file;
-  ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
   std::string data("hello world!");
-  ASSERT_OK(writable_file->Append(data));
-  ASSERT_OK(writable_file->Close());
+  ASSERT_LEVELDB_OK(writable_file->Append(data));
+  ASSERT_LEVELDB_OK(writable_file->Close());
   delete writable_file;
 
-  ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+  ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
   data = "42";
-  ASSERT_OK(writable_file->Append(data));
-  ASSERT_OK(writable_file->Close());
+  ASSERT_LEVELDB_OK(writable_file->Append(data));
+  ASSERT_LEVELDB_OK(writable_file->Close());
   delete writable_file;
 
-  ASSERT_OK(ReadFileToString(env_, test_file_name, &data));
+  ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
   ASSERT_EQ(std::string("42"), data);
-  env_->DeleteFile(test_file_name);
+  env_->RemoveFile(test_file_name);
 }
 
-TEST(EnvTest, ReopenAppendableFile) {
+TEST_F(EnvTest, ReopenAppendableFile) {
   std::string test_dir;
-  ASSERT_OK(env_->GetTestDirectory(&test_dir));
+  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
   std::string test_file_name = test_dir + "/reopen_appendable_file.txt";
-  env_->DeleteFile(test_file_name);
+  env_->RemoveFile(test_file_name);
 
   WritableFile* appendable_file;
-  ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
+  ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
   std::string data("hello world!");
-  ASSERT_OK(appendable_file->Append(data));
-  ASSERT_OK(appendable_file->Close());
+  ASSERT_LEVELDB_OK(appendable_file->Append(data));
+  ASSERT_LEVELDB_OK(appendable_file->Close());
   delete appendable_file;
 
-  ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
+  ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
   data = "42";
-  ASSERT_OK(appendable_file->Append(data));
-  ASSERT_OK(appendable_file->Close());
+  ASSERT_LEVELDB_OK(appendable_file->Append(data));
+  ASSERT_LEVELDB_OK(appendable_file->Close());
   delete appendable_file;
 
-  ASSERT_OK(ReadFileToString(env_, test_file_name, &data));
+  ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
   ASSERT_EQ(std::string("hello world!42"), data);
-  env_->DeleteFile(test_file_name);
+  env_->RemoveFile(test_file_name);
 }
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 09e3df6..449f564 100644 (file)
 #include <atomic>
 #include <chrono>
 #include <condition_variable>
-#include <deque>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
 #include <memory>
 #include <mutex>
+#include <queue>
 #include <sstream>
 #include <string>
 #include <vector>
 #include "util/mutexlock.h"
 #include "util/windows_logger.h"
 
-#if defined(DeleteFile)
-#undef DeleteFile
-#endif  // defined(DeleteFile)
-
 namespace leveldb {
 
 namespace {
@@ -40,9 +40,9 @@ 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;
+constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
 
-// Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
+// Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
 int g_mmap_limit = kDefaultMmapLimit;
 
 std::string GetWindowsErrorMessage(DWORD error_code) {
@@ -71,9 +71,12 @@ Status WindowsError(const std::string& context, DWORD error_code) {
 class ScopedHandle {
  public:
   ScopedHandle(HANDLE handle) : handle_(handle) {}
+  ScopedHandle(const ScopedHandle&) = delete;
   ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
   ~ScopedHandle() { Close(); }
 
+  ScopedHandle& operator=(const ScopedHandle&) = delete;
+
   ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
     if (this != &rhs) handle_ = rhs.Release();
     return *this;
@@ -142,44 +145,43 @@ class Limiter {
 
 class WindowsSequentialFile : public SequentialFile {
  public:
-  WindowsSequentialFile(std::string fname, ScopedHandle file)
-      : filename_(fname), file_(std::move(file)) {}
+  WindowsSequentialFile(std::string filename, ScopedHandle handle)
+      : handle_(std::move(handle)), filename_(std::move(filename)) {}
   ~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<DWORD>::max());
-    if (!::ReadFile(file_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
+    if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
                     nullptr)) {
-      s = WindowsError(filename_, ::GetLastError());
-    } else {
-      *result = Slice(scratch, bytes_read);
+      return WindowsError(filename_, ::GetLastError());
     }
-    return s;
+
+    *result = Slice(scratch, bytes_read);
+    return Status::OK();
   }
 
   Status Skip(uint64_t n) override {
     LARGE_INTEGER distance;
     distance.QuadPart = n;
-    if (!::SetFilePointerEx(file_.get(), distance, nullptr, FILE_CURRENT)) {
+    if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) {
       return WindowsError(filename_, ::GetLastError());
     }
     return Status::OK();
   }
 
  private:
-  std::string filename_;
-  ScopedHandle file_;
+  const ScopedHandle handle_;
+  const std::string filename_;
 };
 
 class WindowsRandomAccessFile : public RandomAccessFile {
  public:
-  WindowsRandomAccessFile(std::string fname, ScopedHandle handle)
-      : filename_(fname), handle_(std::move(handle)) {}
+  WindowsRandomAccessFile(std::string filename, ScopedHandle handle)
+      : handle_(std::move(handle)), filename_(std::move(filename)) {}
 
   ~WindowsRandomAccessFile() override = default;
 
@@ -204,107 +206,116 @@ class WindowsRandomAccessFile : public RandomAccessFile {
   }
 
  private:
-  std::string filename_;
-  ScopedHandle handle_;
+  const ScopedHandle handle_;
+  const std::string filename_;
 };
 
 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),
+  WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length,
+                          Limiter* mmap_limiter)
+      : mmap_base_(mmap_base),
         length_(length),
-        limiter_(limiter) {}
+        mmap_limiter_(mmap_limiter),
+        filename_(std::move(filename)) {}
 
   ~WindowsMmapReadableFile() override {
-    ::UnmapViewOfFile(mmapped_region_);
-    limiter_->Release();
+    ::UnmapViewOfFile(mmap_base_);
+    mmap_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<char*>(mmapped_region_) + offset, n);
+      return WindowsError(filename_, ERROR_INVALID_PARAMETER);
     }
-    return s;
+
+    *result = Slice(mmap_base_ + offset, n);
+    return Status::OK();
   }
 
  private:
-  std::string filename_;
-  void* mmapped_region_;
-  size_t length_;
-  Limiter* limiter_;
+  char* const mmap_base_;
+  const size_t length_;
+  Limiter* const mmap_limiter_;
+  const std::string filename_;
 };
 
 class WindowsWritableFile : public WritableFile {
  public:
-  WindowsWritableFile(std::string fname, ScopedHandle handle)
-      : filename_(std::move(fname)), handle_(std::move(handle)), pos_(0) {}
+  WindowsWritableFile(std::string filename, ScopedHandle handle)
+      : pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {}
 
   ~WindowsWritableFile() override = default;
 
   Status Append(const Slice& data) override {
-    size_t n = data.size();
-    const char* p = data.data();
+    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, kWritableFileBufferSize - 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 < kWritableFileBufferSize) {
-      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);
   }
 
   Status Close() override {
-    Status result = FlushBuffered();
-    if (!handle_.Close() && result.ok()) {
-      result = WindowsError(filename_, ::GetLastError());
+    Status status = FlushBuffer();
+    if (!handle_.Close() && status.ok()) {
+      status = WindowsError(filename_, ::GetLastError());
     }
-    return result;
+    return status;
   }
 
-  Status Flush() override { return FlushBuffered(); }
+  Status Flush() override { return FlushBuffer(); }
 
   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();
+    // On Windows no need to sync parent directory. Its metadata will be updated
+    // via the creation of the new file, without an explicit sync.
+
+    Status status = FlushBuffer();
+    if (!status.ok()) {
+      return status;
+    }
+
+    if (!::FlushFileBuffers(handle_.get())) {
+      return Status::IOError(filename_,
+                             GetWindowsErrorMessage(::GetLastError()));
+    }
+    return Status::OK();
   }
 
  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) {
+  Status WriteUnbuffered(const char* data, size_t size) {
     DWORD bytes_written;
-    if (!::WriteFile(handle_.get(), p, static_cast<DWORD>(n), &bytes_written,
-                     nullptr)) {
+    if (!::WriteFile(handle_.get(), data, static_cast<DWORD>(size),
+                     &bytes_written, nullptr)) {
       return Status::IOError(filename_,
                              GetWindowsErrorMessage(::GetLastError()));
     }
@@ -312,10 +323,11 @@ class WindowsWritableFile : public WritableFile {
   }
 
   // buf_[0, pos_-1] contains data to be written to handle_.
-  const std::string filename_;
-  ScopedHandle handle_;
   char buf_[kWritableFileBufferSize];
   size_t pos_;
+
+  ScopedHandle handle_;
+  const std::string filename_;
 };
 
 // Lock or unlock the entire file as specified by |lock|. Returns true
@@ -337,124 +349,132 @@ bool LockOrUnlock(HANDLE handle, bool lock) {
 
 class WindowsFileLock : public FileLock {
  public:
-  WindowsFileLock(ScopedHandle handle, std::string name)
-      : handle_(std::move(handle)), name_(std::move(name)) {}
+  WindowsFileLock(ScopedHandle handle, std::string filename)
+      : handle_(std::move(handle)), filename_(std::move(filename)) {}
 
-  ScopedHandle& handle() { return handle_; }
-  const std::string& name() const { return name_; }
+  const ScopedHandle& handle() const { return handle_; }
+  const std::string& filename() const { return filename_; }
 
  private:
-  ScopedHandle handle_;
-  std::string name_;
+  const ScopedHandle handle_;
+  const std::string filename_;
 };
 
 class WindowsEnv : public Env {
  public:
   WindowsEnv();
   ~WindowsEnv() override {
-    static char msg[] = "Destroying Env::Default()\n";
-    fwrite(msg, 1, sizeof(msg), stderr);
-    abort();
+    static const char msg[] =
+        "WindowsEnv singleton destroyed. Unsupported behavior!\n";
+    std::fwrite(msg, 1, sizeof(msg), stderr);
+    std::abort();
   }
 
-  Status NewSequentialFile(const std::string& fname,
+  Status NewSequentialFile(const std::string& filename,
                            SequentialFile** result) override {
     *result = nullptr;
     DWORD desired_access = GENERIC_READ;
     DWORD share_mode = FILE_SHARE_READ;
-    ScopedHandle handle =
-        ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
-                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+    ScopedHandle handle = ::CreateFileA(
+        filename.c_str(), desired_access, share_mode,
+        /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+        /*hTemplateFile=*/nullptr);
     if (!handle.is_valid()) {
-      return WindowsError(fname, ::GetLastError());
+      return WindowsError(filename, ::GetLastError());
     }
-    *result = new WindowsSequentialFile(fname, std::move(handle));
+
+    *result = new WindowsSequentialFile(filename, std::move(handle));
     return Status::OK();
   }
 
-  Status NewRandomAccessFile(const std::string& fname,
+  Status NewRandomAccessFile(const std::string& filename,
                              RandomAccessFile** result) override {
     *result = nullptr;
     DWORD desired_access = GENERIC_READ;
     DWORD share_mode = FILE_SHARE_READ;
-    DWORD file_flags = FILE_ATTRIBUTE_READONLY;
-
     ScopedHandle handle =
-        ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
-                      OPEN_EXISTING, file_flags, nullptr);
+        ::CreateFileA(filename.c_str(), desired_access, share_mode,
+                      /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
+                      FILE_ATTRIBUTE_READONLY,
+                      /*hTemplateFile=*/nullptr);
     if (!handle.is_valid()) {
-      return WindowsError(fname, ::GetLastError());
+      return WindowsError(filename, ::GetLastError());
     }
     if (!mmap_limiter_.Acquire()) {
-      *result = new WindowsRandomAccessFile(fname, std::move(handle));
+      *result = new WindowsRandomAccessFile(filename, std::move(handle));
       return Status::OK();
     }
 
     LARGE_INTEGER file_size;
+    Status status;
     if (!::GetFileSizeEx(handle.get(), &file_size)) {
-      return WindowsError(fname, ::GetLastError());
+      mmap_limiter_.Release();
+      return WindowsError(filename, ::GetLastError());
     }
 
     ScopedHandle mapping =
         ::CreateFileMappingA(handle.get(),
                              /*security attributes=*/nullptr, PAGE_READONLY,
                              /*dwMaximumSizeHigh=*/0,
-                             /*dwMaximumSizeLow=*/0, nullptr);
+                             /*dwMaximumSizeLow=*/0,
+                             /*lpName=*/nullptr);
     if (mapping.is_valid()) {
-      void* base = MapViewOfFile(mapping.get(), FILE_MAP_READ, 0, 0, 0);
-      if (base) {
+      void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
+                                        /*dwFileOffsetHigh=*/0,
+                                        /*dwFileOffsetLow=*/0,
+                                        /*dwNumberOfBytesToMap=*/0);
+      if (mmap_base) {
         *result = new WindowsMmapReadableFile(
-            fname, base, static_cast<size_t>(file_size.QuadPart),
-            &mmap_limiter_);
+            filename, reinterpret_cast<char*>(mmap_base),
+            static_cast<size_t>(file_size.QuadPart), &mmap_limiter_);
         return Status::OK();
       }
     }
-    Status s = WindowsError(fname, ::GetLastError());
-
-    if (!s.ok()) {
-      mmap_limiter_.Release();
-    }
-    return s;
+    mmap_limiter_.Release();
+    return WindowsError(filename, ::GetLastError());
   }
 
-  Status NewWritableFile(const std::string& fname,
+  Status NewWritableFile(const std::string& filename,
                          WritableFile** result) override {
     DWORD desired_access = GENERIC_WRITE;
-    DWORD share_mode = 0;
-
-    ScopedHandle handle =
-        ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
-                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+    DWORD share_mode = 0;  // Exclusive access.
+    ScopedHandle handle = ::CreateFileA(
+        filename.c_str(), desired_access, share_mode,
+        /*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+        /*hTemplateFile=*/nullptr);
     if (!handle.is_valid()) {
       *result = nullptr;
-      return WindowsError(fname, ::GetLastError());
+      return WindowsError(filename, ::GetLastError());
     }
 
-    *result = new WindowsWritableFile(fname, std::move(handle));
+    *result = new WindowsWritableFile(filename, std::move(handle));
     return Status::OK();
   }
 
-  Status NewAppendableFile(const std::string& fname,
+  Status NewAppendableFile(const std::string& filename,
                            WritableFile** result) override {
-    ScopedHandle handle =
-        ::CreateFileA(fname.c_str(), FILE_APPEND_DATA, 0, nullptr, OPEN_ALWAYS,
-                      FILE_ATTRIBUTE_NORMAL, nullptr);
+    DWORD desired_access = FILE_APPEND_DATA;
+    DWORD share_mode = 0;  // Exclusive access.
+    ScopedHandle handle = ::CreateFileA(
+        filename.c_str(), desired_access, share_mode,
+        /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+        /*hTemplateFile=*/nullptr);
     if (!handle.is_valid()) {
       *result = nullptr;
-      return WindowsError(fname, ::GetLastError());
+      return WindowsError(filename, ::GetLastError());
     }
 
-    *result = new WindowsWritableFile(fname, std::move(handle));
+    *result = new WindowsWritableFile(filename, std::move(handle));
     return Status::OK();
   }
 
-  bool FileExists(const std::string& fname) override {
-    return GetFileAttributesA(fname.c_str()) != INVALID_FILE_ATTRIBUTES;
+  bool FileExists(const std::string& filename) override {
+    return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
   }
 
-  Status GetChildren(const std::string& dir,
+  Status GetChildren(const std::string& directory_path,
                      std::vector<std::string>* result) override {
-    const std::string find_pattern = dir + "\\*";
+    const std::string find_pattern = directory_path + "\\*";
     WIN32_FIND_DATAA find_data;
     HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
     if (dir_handle == INVALID_HANDLE_VALUE) {
@@ -462,7 +482,7 @@ class WindowsEnv : public Env {
       if (last_error == ERROR_FILE_NOT_FOUND) {
         return Status::OK();
       }
-      return WindowsError(dir, last_error);
+      return WindowsError(directory_path, last_error);
     }
     do {
       char base_name[_MAX_FNAME];
@@ -476,105 +496,109 @@ class WindowsEnv : public Env {
     DWORD last_error = ::GetLastError();
     ::FindClose(dir_handle);
     if (last_error != ERROR_NO_MORE_FILES) {
-      return WindowsError(dir, last_error);
+      return WindowsError(directory_path, last_error);
     }
     return Status::OK();
   }
 
-  Status DeleteFile(const std::string& fname) override {
-    if (!::DeleteFileA(fname.c_str())) {
-      return WindowsError(fname, ::GetLastError());
+  Status RemoveFile(const std::string& filename) override {
+    if (!::DeleteFileA(filename.c_str())) {
+      return WindowsError(filename, ::GetLastError());
     }
     return Status::OK();
   }
 
-  Status CreateDir(const std::string& name) override {
-    if (!::CreateDirectoryA(name.c_str(), nullptr)) {
-      return WindowsError(name, ::GetLastError());
+  Status CreateDir(const std::string& dirname) override {
+    if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
+      return WindowsError(dirname, ::GetLastError());
     }
     return Status::OK();
   }
 
-  Status DeleteDir(const std::string& name) override {
-    if (!::RemoveDirectoryA(name.c_str())) {
-      return WindowsError(name, ::GetLastError());
+  Status RemoveDir(const std::string& dirname) override {
+    if (!::RemoveDirectoryA(dirname.c_str())) {
+      return WindowsError(dirname, ::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());
+  Status GetFileSize(const std::string& filename, uint64_t* size) override {
+    WIN32_FILE_ATTRIBUTE_DATA file_attributes;
+    if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
+                                &file_attributes)) {
+      return WindowsError(filename, ::GetLastError());
     }
     ULARGE_INTEGER file_size;
-    file_size.HighPart = attrs.nFileSizeHigh;
-    file_size.LowPart = attrs.nFileSizeLow;
+    file_size.HighPart = file_attributes.nFileSizeHigh;
+    file_size.LowPart = file_attributes.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())) {
+  Status RenameFile(const std::string& from, const std::string& to) override {
+    // Try a simple move first. It will only succeed when |to| doesn't already
+    // exist.
+    if (::MoveFileA(from.c_str(), to.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
+    // succeed when |to| 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)) {
+    if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
+                       REPLACEFILE_IGNORE_MERGE_ERRORS,
+                       /*lpExclude=*/nullptr, /*lpReserved=*/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.
+    // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
+    // |to| 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);
+      return WindowsError(from, move_error);
     } else {
-      return WindowsError(src, replace_error);
+      return WindowsError(from, replace_error);
     }
   }
 
-  Status LockFile(const std::string& fname, FileLock** lock) override {
+  Status LockFile(const std::string& filename, FileLock** lock) override {
     *lock = nullptr;
     Status result;
     ScopedHandle handle = ::CreateFileA(
-        fname.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
+        filename.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());
+      result = WindowsError(filename, ::GetLastError());
     } else if (!LockOrUnlock(handle.get(), true)) {
-      result = WindowsError("lock " + fname, ::GetLastError());
+      result = WindowsError("lock " + filename, ::GetLastError());
     } else {
-      *lock = new WindowsFileLock(std::move(handle), std::move(fname));
+      *lock = new WindowsFileLock(std::move(handle), filename);
     }
     return result;
   }
 
   Status UnlockFile(FileLock* lock) override {
-    std::unique_ptr<WindowsFileLock> my_lock(
-        reinterpret_cast<WindowsFileLock*>(lock));
-    Status result;
-    if (!LockOrUnlock(my_lock->handle().get(), false)) {
-      result = WindowsError("unlock", ::GetLastError());
+    WindowsFileLock* windows_file_lock =
+        reinterpret_cast<WindowsFileLock*>(lock);
+    if (!LockOrUnlock(windows_file_lock->handle().get(), false)) {
+      return WindowsError("unlock " + windows_file_lock->filename(),
+                          ::GetLastError());
     }
-    return result;
+    delete windows_file_lock;
+    return Status::OK();
   }
 
-  void Schedule(void (*function)(void*), void* arg) override;
+  void Schedule(void (*background_work_function)(void* background_work_arg),
+                void* background_work_arg) override;
 
-  void StartThread(void (*function)(void* arg), void* arg) override {
-    std::thread t(function, arg);
-    t.detach();
+  void StartThread(void (*thread_main)(void* thread_main_arg),
+                   void* thread_main_arg) override {
+    std::thread new_thread(thread_main, thread_main_arg);
+    new_thread.detach();
   }
 
   Status GetTestDirectory(std::string* result) override {
@@ -601,7 +625,7 @@ class WindowsEnv : public Env {
     std::FILE* fp = std::fopen(filename.c_str(), "w");
     if (fp == nullptr) {
       *result = nullptr;
-      return WindowsError("NewLogger", ::GetLastError());
+      return WindowsError(filename, ::GetLastError());
     } else {
       *result = new WindowsLogger(fp);
       return Status::OK();
@@ -626,86 +650,147 @@ class WindowsEnv : public Env {
   }
 
  private:
-  // Entry per Schedule() call
-  struct BGItem {
-    void* arg;
-    void (*function)(void*);
+  void BackgroundThreadMain();
+
+  static void BackgroundThreadEntryPoint(WindowsEnv* env) {
+    env->BackgroundThreadMain();
+  }
+
+  // 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;
   };
 
-  // BGThread() is the body of the background thread
-  void BGThread();
+  port::Mutex background_work_mutex_;
+  port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
+  bool started_background_thread_ GUARDED_BY(background_work_mutex_);
+
+  std::queue<BackgroundWorkItem> background_work_queue_
+      GUARDED_BY(background_work_mutex_);
 
-  std::mutex mu_;
-  std::condition_variable bgsignal_;
-  bool started_bgthread_;
-  std::deque<BGItem> queue_;
-  Limiter mmap_limiter_;
+  Limiter mmap_limiter_;  // Thread-safe.
 };
 
 // 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;
-}
+int MaxMmaps() { return g_mmap_limit; }
 
 WindowsEnv::WindowsEnv()
-    : started_bgthread_(false), mmap_limiter_(MaxMmaps()) {}
+    : background_work_cv_(&background_work_mutex_),
+      started_background_thread_(false),
+      mmap_limiter_(MaxMmaps()) {}
 
-void WindowsEnv::Schedule(void (*function)(void*), void* arg) {
-  std::lock_guard<std::mutex> guard(mu_);
+void WindowsEnv::Schedule(
+    void (*background_work_function)(void* background_work_arg),
+    void* background_work_arg) {
+  background_work_mutex_.Lock();
 
-  // Start background thread if necessary
-  if (!started_bgthread_) {
-    started_bgthread_ = true;
-    std::thread t(&WindowsEnv::BGThread, this);
-    t.detach();
+  // Start the background thread, if we haven't done so already.
+  if (!started_background_thread_) {
+    started_background_thread_ = true;
+    std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this);
+    background_thread.detach();
   }
 
-  // If the queue is currently empty, the background thread may currently be
-  // waiting.
-  if (queue_.empty()) {
-    bgsignal_.notify_one();
+  // 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;
+  background_work_queue_.emplace(background_work_function, background_work_arg);
+  background_work_mutex_.Unlock();
 }
 
-void WindowsEnv::BGThread() {
+void WindowsEnv::BackgroundThreadMain() {
   while (true) {
-    // Wait until there is an item that is ready to run
-    std::unique_lock<std::mutex> lk(mu_);
-    bgsignal_.wait(lk, [this] { return !queue_.empty(); });
+    background_work_mutex_.Lock();
 
-    void (*function)(void*) = queue_.front().function;
-    void* arg = queue_.front().arg;
-    queue_.pop_front();
+    // Wait until there is work to be done.
+    while (background_work_queue_.empty()) {
+      background_work_cv_.Wait();
+    }
 
-    lk.unlock();
-    (*function)(arg);
+    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();
+
+    background_work_mutex_.Unlock();
+    background_work_function(background_work_arg);
   }
 }
 
-}  // namespace
+// Wraps an Env instance whose destructor is never created.
+//
+// Intended usage:
+//   using PlatformSingletonEnv = SingletonEnv<PlatformEnv>;
+//   void ConfigurePosixEnv(int param) {
+//     PlatformSingletonEnv::AssertEnvNotInitialized();
+//     // set global configuration flags.
+//   }
+//   Env* Env::Default() {
+//     static PlatformSingletonEnv default_env;
+//     return default_env.env();
+//   }
+template <typename EnvType>
+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*>(&env_storage_); }
 
-static std::once_flag once;
-static Env* default_env;
-static void InitDefaultEnv() { default_env = new WindowsEnv(); }
+  static void AssertEnvNotInitialized() {
+#if !defined(NDEBUG)
+    assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
+#endif  // !defined(NDEBUG)
+  }
+
+ private:
+  typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
+      env_storage_;
+#if !defined(NDEBUG)
+  static std::atomic<bool> env_initialized_;
+#endif  // !defined(NDEBUG)
+};
+
+#if !defined(NDEBUG)
+template <typename EnvType>
+std::atomic<bool> SingletonEnv<EnvType>::env_initialized_;
+#endif  // !defined(NDEBUG)
+
+using WindowsDefaultEnv = SingletonEnv<WindowsEnv>;
+
+}  // namespace
 
 void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
-  assert(default_env == nullptr);
+  WindowsDefaultEnv::AssertEnvNotInitialized();
   g_mmap_limit = limit;
 }
 
 Env* Env::Default() {
-  std::call_once(once, InitDefaultEnv);
-  return default_env;
+  static WindowsDefaultEnv env_container;
+  return env_container.env();
 }
 
 }  // namespace leveldb
index 3c22133..d6822d2 100644 (file)
@@ -2,17 +2,17 @@
 // 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 "gtest/gtest.h"
 #include "leveldb/env.h"
-
 #include "port/port.h"
 #include "util/env_windows_test_helper.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
 
 namespace leveldb {
 
 static const int kMMapLimit = 4;
 
-class EnvWindowsTest {
+class EnvWindowsTest : public testing::Test {
  public:
   static void SetFileLimits(int mmap_limit) {
     EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit);
@@ -23,17 +23,17 @@ class EnvWindowsTest {
   Env* env_;
 };
 
-TEST(EnvWindowsTest, TestOpenOnRead) {
+TEST_F(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));
+  ASSERT_LEVELDB_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 = std::fopen(test_file.c_str(), "w");
   ASSERT_TRUE(f != nullptr);
   const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
   fputs(kFileData, f);
-  fclose(f);
+  std::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
@@ -41,18 +41,18 @@ TEST(EnvWindowsTest, TestOpenOnRead) {
   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]));
+    ASSERT_LEVELDB_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_LEVELDB_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));
+  ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
 }
 
 }  // namespace leveldb
@@ -60,5 +60,6 @@ TEST(EnvWindowsTest, TestOpenOnRead) {
 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();
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
 }
index 67dc134..8122fa8 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "util/hash.h"
 
-#include <string.h>
+#include <cstring>
 
 #include "util/coding.h"
 
@@ -38,13 +38,13 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) {
   // Pick up remaining bytes
   switch (limit - data) {
     case 3:
-      h += static_cast<unsigned char>(data[2]) << 16;
+      h += static_cast<uint8_t>(data[2]) << 16;
       FALLTHROUGH_INTENDED;
     case 2:
-      h += static_cast<unsigned char>(data[1]) << 8;
+      h += static_cast<uint8_t>(data[1]) << 8;
       FALLTHROUGH_INTENDED;
     case 1:
-      h += static_cast<unsigned char>(data[0]);
+      h += static_cast<uint8_t>(data[0]);
       h *= m;
       h ^= (h >> r);
       break;
index 74bdb6e..87ab279 100644 (file)
@@ -7,8 +7,8 @@
 #ifndef STORAGE_LEVELDB_UTIL_HASH_H_
 #define STORAGE_LEVELDB_UTIL_HASH_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
 namespace leveldb {
 
index 8f579cc..6d6771f 100644 (file)
@@ -3,18 +3,17 @@
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
 #include "util/hash.h"
-#include "util/testharness.h"
 
-namespace leveldb {
+#include "gtest/gtest.h"
 
-class HASH {};
+namespace leveldb {
 
 TEST(HASH, SignedUnsignedIssue) {
-  const unsigned char data1[1] = {0x62};
-  const unsigned char data2[2] = {0xc3, 0x97};
-  const unsigned char data3[3] = {0xe2, 0x99, 0xa5};
-  const unsigned char data4[4] = {0xe1, 0x80, 0xb9, 0x32};
-  const unsigned char data5[48] = {
+  const uint8_t data1[1] = {0x62};
+  const uint8_t data2[2] = {0xc3, 0x97};
+  const uint8_t data3[3] = {0xe2, 0x99, 0xa5};
+  const uint8_t data4[4] = {0xe1, 0x80, 0xb9, 0x32};
+  const uint8_t 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,
@@ -41,4 +40,7 @@ TEST(HASH, SignedUnsignedIssue) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 65092c8..7af4030 100644 (file)
@@ -4,8 +4,8 @@
 
 #include "util/histogram.h"
 
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <cstdio>
 
 #include "port/port.h"
 
@@ -241,11 +241,11 @@ 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());
+  std::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",
-           (num_ == 0.0 ? 0.0 : min_), Median(), max_);
+  std::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");
   const double mult = 100.0 / num_;
@@ -253,12 +253,12 @@ 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
+    std::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%.
index 1ad8f1c..8d6fb5b 100644 (file)
@@ -4,11 +4,9 @@
 
 #include "util/logging.h"
 
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
 #include <limits>
 
 #include "leveldb/env.h"
@@ -18,7 +16,7 @@ namespace leveldb {
 
 void AppendNumberTo(std::string* str, uint64_t num) {
   char buf[30];
-  snprintf(buf, sizeof(buf), "%llu", (unsigned long long)num);
+  std::snprintf(buf, sizeof(buf), "%llu", static_cast<unsigned long long>(num));
   str->append(buf);
 }
 
@@ -29,8 +27,8 @@ void AppendEscapedStringTo(std::string* str, const Slice& value) {
       str->push_back(c);
     } else {
       char buf[10];
-      snprintf(buf, sizeof(buf), "\\x%02x",
-               static_cast<unsigned int>(c) & 0xff);
+      std::snprintf(buf, sizeof(buf), "\\x%02x",
+                    static_cast<unsigned int>(c) & 0xff);
       str->append(buf);
     }
   }
@@ -56,14 +54,13 @@ bool ConsumeDecimalNumber(Slice* in, uint64_t* val) {
 
   uint64_t value = 0;
 
-  // reinterpret_cast-ing from char* to unsigned char* to avoid signedness.
-  const unsigned char* start =
-      reinterpret_cast<const unsigned char*>(in->data());
+  // reinterpret_cast-ing from char* to uint8_t* to avoid signedness.
+  const uint8_t* start = reinterpret_cast<const uint8_t*>(in->data());
 
-  const unsigned char* end = start + in->size();
-  const unsigned char* current = start;
+  const uint8_t* end = start + in->size();
+  const uint8_t* current = start;
   for (; current != end; ++current) {
-    const unsigned char ch = *current;
+    const uint8_t ch = *current;
     if (ch < '0' || ch > '9') break;
 
     // Overflow check.
index 8ff2da8..a0394b2 100644 (file)
@@ -8,9 +8,8 @@
 #ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_
 #define STORAGE_LEVELDB_UTIL_LOGGING_H_
 
-#include <stdint.h>
-#include <stdio.h>
-
+#include <cstdint>
+#include <cstdio>
 #include <string>
 
 #include "port/port.h"
index 389cbeb..24e1fe9 100644 (file)
@@ -2,17 +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 "util/logging.h"
+
 #include <limits>
 #include <string>
 
+#include "gtest/gtest.h"
 #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));
@@ -140,4 +139,7 @@ TEST(Logging, ConsumeDecimalNumberNoDigits) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index b41caca..68fdfee 100644 (file)
@@ -2,12 +2,13 @@
 // 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/no_destructor.h"
+
 #include <cstdint>
 #include <cstdlib>
 #include <utility>
 
-#include "util/no_destructor.h"
-#include "util/testharness.h"
+#include "gtest/gtest.h"
 
 namespace leveldb {
 
@@ -28,8 +29,6 @@ constexpr const uint64_t kGoldenB = 0xaabbccddeeffaabb;
 
 }  // namespace
 
-class NoDestructorTest {};
-
 TEST(NoDestructorTest, StackInstance) {
   NoDestructor<DoNotDestruct> instance(kGoldenA, kGoldenB);
   ASSERT_EQ(kGoldenA, instance.get()->a);
@@ -44,4 +43,7 @@ TEST(NoDestructorTest, StaticInstance) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
index 28e15d1..6bbc1a0 100644 (file)
@@ -30,7 +30,7 @@ class PosixLogger final : public Logger {
 
   ~PosixLogger() override { std::fclose(fp_); }
 
-  void Logv(const char* format, va_list arguments) override {
+  void Logv(const char* format, std::va_list arguments) override {
     // Record the time as close to the Logv() call as possible.
     struct ::timeval now_timeval;
     ::gettimeofday(&now_timeval, nullptr);
@@ -62,7 +62,7 @@ class PosixLogger final : public Logger {
           (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
 
       // Print the header into the buffer.
-      int buffer_offset = snprintf(
+      int buffer_offset = std::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,
@@ -98,8 +98,8 @@ class PosixLogger final : public Logger {
         }
 
         // 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.
+        // not happen, assuming a correct implementation of std::(v)snprintf.
+        // Fail in tests, recover by truncating the log message in production.
         assert(false);
         buffer_offset = buffer_size - 1;
       }
index 76f7daf..fe76ab4 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_
 #define STORAGE_LEVELDB_UTIL_RANDOM_H_
 
-#include <stdint.h>
+#include <cstdint>
 
 namespace leveldb {
 
index 6ca8da6..0559f5b 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "leveldb/status.h"
 
-#include <stdio.h>
+#include <cstdio>
 
 #include "port/port.h"
 
@@ -12,25 +12,25 @@ namespace leveldb {
 
 const char* Status::CopyState(const char* state) {
   uint32_t size;
-  memcpy(&size, state, sizeof(size));
+  std::memcpy(&size, state, sizeof(size));
   char* result = new char[size + 5];
-  memcpy(result, state, size + 5);
+  std::memcpy(result, state, size + 5);
   return result;
 }
 
 Status::Status(Code code, const Slice& msg, const Slice& msg2) {
   assert(code != kOk);
-  const uint32_t len1 = msg.size();
-  const uint32_t len2 = msg2.size();
+  const uint32_t len1 = static_cast<uint32_t>(msg.size());
+  const uint32_t len2 = static_cast<uint32_t>(msg2.size());
   const uint32_t size = len1 + (len2 ? (2 + len2) : 0);
   char* result = new char[size + 5];
-  memcpy(result, &size, sizeof(size));
+  std::memcpy(result, &size, sizeof(size));
   result[4] = static_cast<char>(code);
-  memcpy(result + 5, msg.data(), len1);
+  std::memcpy(result + 5, msg.data(), len1);
   if (len2) {
     result[5 + len1] = ':';
     result[6 + len1] = ' ';
-    memcpy(result + 7 + len1, msg2.data(), len2);
+    std::memcpy(result + 7 + len1, msg2.data(), len2);
   }
   state_ = result;
 }
@@ -61,14 +61,14 @@ std::string Status::ToString() const {
         type = "IO error: ";
         break;
       default:
-        snprintf(tmp, sizeof(tmp),
-                 "Unknown code(%d): ", static_cast<int>(code()));
+        std::snprintf(tmp, sizeof(tmp),
+                      "Unknown code(%d): ", static_cast<int>(code()));
         type = tmp;
         break;
     }
     std::string result(type);
     uint32_t length;
-    memcpy(&length, state_, sizeof(length));
+    std::memcpy(&length, state_, sizeof(length));
     result.append(state_ + 5, length);
     return result;
   }
index 2842319..914b386 100644 (file)
@@ -2,11 +2,12 @@
 // 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 <utility>
 
+#include "gtest/gtest.h"
 #include "leveldb/slice.h"
-#include "leveldb/status.h"
-#include "util/testharness.h"
 
 namespace leveldb {
 
@@ -37,4 +38,7 @@ TEST(Status, MoveConstructor) {
 
 }  // namespace leveldb
 
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/util/testharness.cc b/util/testharness.cc
deleted file mode 100644 (file)
index 318ecfa..0000000
+++ /dev/null
@@ -1,81 +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 "util/testharness.h"
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <string>
-#include <vector>
-
-#include "leveldb/env.h"
-
-namespace leveldb {
-namespace test {
-
-namespace {
-struct Test {
-  const char* base;
-  const char* name;
-  void (*func)();
-};
-std::vector<Test>* tests;
-}  // namespace
-
-bool RegisterTest(const char* base, const char* name, void (*func)()) {
-  if (tests == nullptr) {
-    tests = new std::vector<Test>;
-  }
-  Test t;
-  t.base = base;
-  t.name = name;
-  t.func = func;
-  tests->push_back(t);
-  return true;
-}
-
-int RunAllTests() {
-  const char* matcher = getenv("LEVELDB_TESTS");
-
-  int num = 0;
-  if (tests != nullptr) {
-    for (size_t i = 0; i < tests->size(); i++) {
-      const Test& t = (*tests)[i];
-      if (matcher != nullptr) {
-        std::string name = t.base;
-        name.push_back('.');
-        name.append(t.name);
-        if (strstr(name.c_str(), matcher) == nullptr) {
-          continue;
-        }
-      }
-      fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);
-      (*t.func)();
-      ++num;
-    }
-  }
-  fprintf(stderr, "==== PASSED %d tests\n", num);
-  return 0;
-}
-
-std::string TmpDir() {
-  std::string dir;
-  Status s = Env::Default()->GetTestDirectory(&dir);
-  ASSERT_TRUE(s.ok()) << s.ToString();
-  return dir;
-}
-
-int RandomSeed() {
-  const char* env = getenv("TEST_RANDOM_SEED");
-  int result = (env != nullptr ? atoi(env) : 301);
-  if (result <= 0) {
-    result = 301;
-  }
-  return result;
-}
-
-}  // namespace test
-}  // namespace leveldb
diff --git a/util/testharness.h b/util/testharness.h
deleted file mode 100644 (file)
index 72cd162..0000000
+++ /dev/null
@@ -1,141 +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.
-
-#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
-#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <sstream>
-
-#include "leveldb/status.h"
-
-namespace leveldb {
-namespace test {
-
-// Run some of the tests registered by the TEST() macro.  If the
-// environment variable "LEVELDB_TESTS" is not set, runs all tests.
-// Otherwise, runs only the tests whose name contains the value of
-// "LEVELDB_TESTS" as a substring.  E.g., suppose the tests are:
-//    TEST(Foo, Hello) { ... }
-//    TEST(Foo, World) { ... }
-// LEVELDB_TESTS=Hello will run the first test
-// LEVELDB_TESTS=o     will run both tests
-// LEVELDB_TESTS=Junk  will run no tests
-//
-// Returns 0 if all tests pass.
-// Dies or returns a non-zero value if some test fails.
-int RunAllTests();
-
-// Return the directory to use for temporary storage.
-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.
-int RandomSeed();
-
-// An instance of Tester is allocated to hold temporary state during
-// the execution of an assertion.
-class Tester {
- private:
-  bool ok_;
-  const char* fname_;
-  int line_;
-  std::stringstream ss_;
-
- public:
-  Tester(const char* f, int l) : ok_(true), fname_(f), line_(l) {}
-
-  ~Tester() {
-    if (!ok_) {
-      fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str());
-      exit(1);
-    }
-  }
-
-  Tester& Is(bool b, const char* msg) {
-    if (!b) {
-      ss_ << " Assertion failure " << msg;
-      ok_ = false;
-    }
-    return *this;
-  }
-
-  Tester& IsOk(const Status& s) {
-    if (!s.ok()) {
-      ss_ << " " << s.ToString();
-      ok_ = false;
-    }
-    return *this;
-  }
-
-#define BINARY_OP(name, op)                          \
-  template <class X, class Y>                        \
-  Tester& name(const X& x, const Y& y) {             \
-    if (!(x op y)) {                                 \
-      ss_ << " failed: " << x << (" " #op " ") << y; \
-      ok_ = false;                                   \
-    }                                                \
-    return *this;                                    \
-  }
-
-  BINARY_OP(IsEq, ==)
-  BINARY_OP(IsNe, !=)
-  BINARY_OP(IsGe, >=)
-  BINARY_OP(IsGt, >)
-  BINARY_OP(IsLe, <=)
-  BINARY_OP(IsLt, <)
-#undef BINARY_OP
-
-  // Attach the specified value to the error message if an error has occurred
-  template <class V>
-  Tester& operator<<(const V& value) {
-    if (!ok_) {
-      ss_ << " " << value;
-    }
-    return *this;
-  }
-};
-
-#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 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.
-bool RegisterTest(const char* base, const char* name, void (*func)());
-
-}  // namespace test
-}  // namespace leveldb
-
-#endif  // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
index 6b151b9..5f77b08 100644 (file)
@@ -4,6 +4,8 @@
 
 #include "util/testutil.h"
 
+#include <string>
+
 #include "util/random.h"
 
 namespace leveldb {
index bb4051b..e0e2d64 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_
 #define STORAGE_LEVELDB_UTIL_TESTUTIL_H_
 
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include "helpers/memenv/memenv.h"
 #include "leveldb/env.h"
 #include "leveldb/slice.h"
 namespace leveldb {
 namespace test {
 
+MATCHER(IsOK, "") { return arg.ok(); }
+
+// Macros for testing the results of functions that return leveldb::Status or
+// absl::StatusOr<T> (for any type T).
+#define EXPECT_LEVELDB_OK(expression) \
+  EXPECT_THAT(expression, leveldb::test::IsOK())
+#define ASSERT_LEVELDB_OK(expression) \
+  ASSERT_THAT(expression, leveldb::test::IsOK())
+
+// Returns the random seed used at the start of the current test run.
+inline int RandomSeed() {
+  return testing::UnitTest::GetInstance()->random_seed();
+}
+
 // Store in *dst a random string of length "len" and return a Slice that
 // references the generated data.
 Slice RandomString(Random* rnd, int len, std::string* dst);
index 9296063..26e6c7b 100644 (file)
@@ -27,7 +27,7 @@ class WindowsLogger final : public Logger {
 
   ~WindowsLogger() override { std::fclose(fp_); }
 
-  void Logv(const char* format, va_list arguments) override {
+  void Logv(const char* format, std::va_list arguments) override {
     // Record the time as close to the Logv() call as possible.
     SYSTEMTIME now_components;
     ::GetLocalTime(&now_components);
@@ -56,7 +56,7 @@ class WindowsLogger final : public Logger {
           (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
 
       // Print the header into the buffer.
-      int buffer_offset = snprintf(
+      int buffer_offset = std::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,
@@ -92,8 +92,8 @@ class WindowsLogger final : public Logger {
         }
 
         // 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.
+        // not happen, assuming a correct implementation of std::(v)snprintf.
+        // Fail in tests, recover by truncating the log message in production.
         assert(false);
         buffer_offset = buffer_size - 1;
       }