mirror of
https://github.com/google/leveldb.git
synced 2026-08-18 10:33:27 +08:00
Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ee830d02b | ||
|
|
9e273a6aef | ||
|
|
4a0c572440 | ||
|
|
78a352f47e | ||
|
|
06188fdcf5 | ||
|
|
ac691084fd | ||
|
|
4ee78d7ea9 | ||
|
|
e829478c6a | ||
|
|
302786e211 | ||
|
|
578eeb702e | ||
|
|
23e35d792b | ||
|
|
eb31d19999 | ||
|
|
2cc36eb566 | ||
|
|
068d5ee1a3 | ||
|
|
c61238dcf3 | ||
|
|
77d66aaf3e | ||
|
|
9cbbc5fb75 | ||
|
|
80d858fb2a | ||
|
|
1d6e8d64ee | ||
|
|
13ebad24dc | ||
|
|
df68d9578c | ||
|
|
bfae97ff7d | ||
|
|
89ea7f2643 | ||
|
|
fb644cb445 | ||
|
|
aa5479bbf4 | ||
|
|
ca684d00b5 | ||
|
|
7b650f85de | ||
|
|
0a9b7b8e95 | ||
|
|
fff74f20ff | ||
|
|
9e1c274074 | ||
|
|
d019e3605f | ||
|
|
8b5b1ca96c | ||
|
|
4fb146810c | ||
|
|
479a1f4e9b | ||
|
|
1b51a3a968 | ||
|
|
a797000713 | ||
|
|
bf4fcd85b5 | ||
|
|
8a68093c84 | ||
|
|
bfea90d883 | ||
|
|
bda46dd00d | ||
|
|
ec4e3a5cb3 | ||
|
|
3ab94e7da8 | ||
|
|
3180f9cb40 | ||
|
|
8ccb79b57e | ||
|
|
74f0be238f | ||
|
|
56f2394250 | ||
|
|
068a0f1214 | ||
|
|
8f5aa6375e | ||
|
|
7a2f90460a | ||
|
|
4db0eaccf1 | ||
|
|
7ee3889a61 | ||
|
|
42cf899927 | ||
|
|
8796c44772 | ||
|
|
e4ccaa0c9c | ||
|
|
c8b708d496 | ||
|
|
639195221c | ||
|
|
87b3a371b1 | ||
|
|
0e8aa26c4e | ||
|
|
8f464e7f68 | ||
|
|
7a2f64ed50 | ||
|
|
b2801ee1a0 | ||
|
|
335876a133 | ||
|
|
42d00a80cc | ||
|
|
e426c83e88 | ||
|
|
6124f47490 | ||
|
|
dd6658754f | ||
|
|
d7da5d9d35 | ||
|
|
68d14a723a | ||
|
|
c5d5174a66 | ||
|
|
11aafab31f | ||
|
|
5783a79309 | ||
|
|
54340b4a10 | ||
|
|
8e62cc5124 | ||
|
|
8949158f5d | ||
|
|
5d94ad4d95 | ||
|
|
c7a0fa28a4 | ||
|
|
13e3c4efc6 | ||
|
|
f6fe2ec561 | ||
|
|
3806fbc23c | ||
|
|
dbf24d9a0c | ||
|
|
1ca4f5b466 | ||
|
|
f6d094e994 | ||
|
|
e2ad7dad54 | ||
|
|
9e8500518f | ||
|
|
f57513a1d6 | ||
|
|
24bcf7f7ce | ||
|
|
99b3c03b32 | ||
|
|
37aaf2fccd | ||
|
|
2a47801868 | ||
|
|
4a919ea4f7 | ||
|
|
1998c0ef15 | ||
|
|
8f1861462b | ||
|
|
8cce47e450 | ||
|
|
fdc8f72895 | ||
|
|
532be85306 | ||
|
|
6721eda0b4 | ||
|
|
295ce1336f | ||
|
|
c3b52f7db6 | ||
|
|
28df52115d | ||
|
|
2dcbd4a2c5 | ||
|
|
b754fdca72 | ||
|
|
37d36c92f8 | ||
|
|
2802398c94 | ||
|
|
99ab4730d6 | ||
|
|
ed781070b4 | ||
|
|
b7d3023269 | ||
|
|
1454924aac | ||
|
|
1754c12c54 | ||
|
|
c46e79c760 | ||
|
|
28602d3625 | ||
|
|
5bd5f0f67a | ||
|
|
23b6337f69 | ||
|
|
5c6dd75897 | ||
|
|
a6b3a2012e | ||
|
|
3f934e3705 | ||
|
|
23d67e7c1f | ||
|
|
98a3b8cf65 | ||
|
|
10bc0f2595 | ||
|
|
201f52201f | ||
|
|
ba369ddbaf | ||
|
|
5903e7a112 | ||
|
|
a0191e5563 | ||
|
|
d152b23f3b | ||
|
|
58a89bbcb2 | ||
|
|
c0d43142ff | ||
|
|
e36b831851 | ||
|
|
583a42b596 | ||
|
|
db8352187b | ||
|
|
1c58902bdc | ||
|
|
2c9c80bd53 | ||
|
|
ed72a3496e | ||
|
|
41c8d83914 | ||
|
|
0c40829872 | ||
|
|
5abdf4c019 | ||
|
|
cf4d9ab23d | ||
|
|
95d0ba1cb0 | ||
|
|
657ba51429 | ||
|
|
f2dae4e74a | ||
|
|
f933ad1693 | ||
|
|
370d532a00 | ||
|
|
45ee61579c | ||
|
|
60db170a43 | ||
|
|
21304d41f7 | ||
|
|
5e921896ee | ||
|
|
53e280b568 | ||
|
|
046216a7ca | ||
|
|
9ee91ac747 | ||
|
|
e0d5f83a4f | ||
|
|
69061b464a | ||
|
|
107a75b62c | ||
|
|
76ca116276 | ||
|
|
f668239bb2 | ||
|
|
177cd08629 | ||
|
|
8fa7a937ee | ||
|
|
6a90bb91ee | ||
|
|
4cb80b7ddc | ||
|
|
72a38ff7f2 | ||
|
|
863f185970 | ||
|
|
a3b71c1ff6 | ||
|
|
ae49533210 | ||
|
|
63d5315e1c | ||
|
|
c00e177f36 | ||
|
|
1d0b101165 | ||
|
|
28e6d238be | ||
|
|
85cd40d108 | ||
|
|
1aae5c9f29 | ||
|
|
b7b86baec9 | ||
|
|
3e6c000e18 | ||
|
|
1d94fe2f4d | ||
|
|
27dc99fb26 | ||
|
|
9521545b06 | ||
|
|
900f7d37eb | ||
|
|
a7528a5d2b | ||
|
|
142035edd4 | ||
|
|
e22b1cec6e | ||
|
|
cd1ec032cd | ||
|
|
4bd052d7e8 | ||
|
|
506b1722ef | ||
|
|
24424a1ef2 | ||
|
|
9a56c49ed4 | ||
|
|
abf441b657 | ||
|
|
d3d1c8a0f4 | ||
|
|
75fceae700 | ||
|
|
77e9dfad9f | ||
|
|
f314b63e5e | ||
|
|
3ee04c5cea | ||
|
|
5b817400a0 | ||
|
|
7d060117fa | ||
|
|
2883fcd849 | ||
|
|
e5f0a51fa4 | ||
|
|
dd598676cd |
@@ -1,35 +0,0 @@
|
||||
# Build matrix / environment variables are explained on:
|
||||
# https://www.appveyor.com/docs/appveyor-yml/
|
||||
# This file can be validated on: https://ci.appveyor.com/tools/validate-yaml
|
||||
|
||||
version: "{build}"
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
# AppVeyor currently has no custom job name feature.
|
||||
# http://help.appveyor.com/discussions/questions/1623-can-i-provide-a-friendly-name-for-jobs
|
||||
- JOB: Visual Studio 2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- RelWithDebInfo
|
||||
- Debug
|
||||
|
||||
build_script:
|
||||
- git submodule update --init --recursive
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if "%platform%"=="x64" set CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64
|
||||
- cmake --version
|
||||
- cmake .. -G "%CMAKE_GENERATOR%"
|
||||
-DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%"
|
||||
- cmake --build . --config "%CONFIGURATION%"
|
||||
- cd ..
|
||||
|
||||
test_script:
|
||||
- cd build && ctest --verbose --build-config "%CONFIGURATION%" && cd ..
|
||||
@@ -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
|
||||
|
||||
102
.github/workflows/build.yml
vendored
Normal file
102
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright 2021 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.
|
||||
|
||||
name: ci
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: >-
|
||||
CI
|
||||
${{ matrix.os }}
|
||||
${{ matrix.compiler }}
|
||||
${{ matrix.optimized && 'release' || 'debug' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [clang, gcc, msvc]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
optimized: [true, false]
|
||||
exclude:
|
||||
# MSVC only works on Windows.
|
||||
- os: ubuntu-latest
|
||||
compiler: msvc
|
||||
- os: macos-latest
|
||||
compiler: msvc
|
||||
# Not testing with GCC on macOS.
|
||||
- os: macos-latest
|
||||
compiler: gcc
|
||||
# Only testing with MSVC on Windows.
|
||||
- os: windows-latest
|
||||
compiler: clang
|
||||
- os: windows-latest
|
||||
compiler: gcc
|
||||
include:
|
||||
- compiler: clang
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
- compiler: gcc
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
- compiler: msvc
|
||||
CC:
|
||||
CXX:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_DIR: ${{ github.workspace }}/build
|
||||
CMAKE_BUILD_TYPE: ${{ matrix.optimized && 'RelWithDebInfo' || 'Debug' }}
|
||||
CC: ${{ matrix.CC }}
|
||||
CXX: ${{ matrix.CXX }}
|
||||
BINARY_SUFFIX: ${{ startsWith(matrix.os, 'windows') && '.exe' || '' }}
|
||||
BINARY_PATH: >-
|
||||
${{ format(
|
||||
startsWith(matrix.os, 'windows') && '{0}\build\{1}\' || '{0}/build/',
|
||||
github.workspace,
|
||||
matrix.optimized && 'RelWithDebInfo' || 'Debug') }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install dependencies on Linux
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
# libgoogle-perftools-dev is temporarily removed from the package list
|
||||
# because it is currently broken on GitHub's Ubuntu 22.04.
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libkyotocabinet-dev libsnappy-dev libsqlite3-dev
|
||||
|
||||
- name: Generate build config
|
||||
run: >-
|
||||
cmake -S "${{ github.workspace }}" -B "${{ env.CMAKE_BUILD_DIR }}"
|
||||
-DCMAKE_BUILD_TYPE=${{ env.CMAKE_BUILD_TYPE }}
|
||||
-DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/install_test/
|
||||
|
||||
- name: Build
|
||||
run: >-
|
||||
cmake --build "${{ env.CMAKE_BUILD_DIR }}"
|
||||
--config "${{ env.CMAKE_BUILD_TYPE }}"
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
run: ctest -C "${{ env.CMAKE_BUILD_TYPE }}" --verbose
|
||||
|
||||
- name: Run LevelDB Benchmarks
|
||||
run: ${{ env.BINARY_PATH }}db_bench${{ env.BINARY_SUFFIX }}
|
||||
|
||||
- name: Run SQLite Benchmarks
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
run: ${{ env.BINARY_PATH }}db_bench_sqlite3${{ env.BINARY_SUFFIX }}
|
||||
|
||||
- name: Run Kyoto Cabinet Benchmarks
|
||||
if: ${{ runner.os == 'Linux' && matrix.compiler == 'clang' }}
|
||||
run: ${{ env.BINARY_PATH }}db_bench_tree_db${{ env.BINARY_SUFFIX }}
|
||||
|
||||
- name: Test CMake installation
|
||||
run: cmake --build "${{ env.CMAKE_BUILD_DIR }}" --target install
|
||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -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
|
||||
79
.travis.yml
79
.travis.yml
@@ -1,79 +0,0 @@
|
||||
# Build matrix / environment variables are explained on:
|
||||
# http://about.travis-ci.org/docs/user/build-configuration/
|
||||
# This file can be validated on: http://lint.travis-ci.org/
|
||||
|
||||
language: cpp
|
||||
dist: xenial
|
||||
osx_image: xcode10.2
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
- BUILD_TYPE=Debug
|
||||
- BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-xenial-8
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- clang-8
|
||||
- cmake
|
||||
- gcc-8
|
||||
- g++-8
|
||||
- libgoogle-perftools-dev
|
||||
- libkyotocabinet-dev
|
||||
- libsnappy-dev
|
||||
- libsqlite3-dev
|
||||
- ninja-build
|
||||
homebrew:
|
||||
packages:
|
||||
- cmake
|
||||
- crc32c
|
||||
- gcc@8
|
||||
- gperftools
|
||||
- kyotocabinet
|
||||
- llvm@8
|
||||
- ninja
|
||||
- snappy
|
||||
- sqlite3
|
||||
update: true
|
||||
|
||||
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
|
||||
export PATH="$(brew --prefix llvm)/bin:$PATH";
|
||||
fi
|
||||
# /usr/bin/gcc points to an older compiler on both Linux and macOS.
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-8" CC="gcc-8"; fi
|
||||
# /usr/bin/clang points to an older compiler on both Linux and macOS.
|
||||
#
|
||||
# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values
|
||||
# below don't work on macOS. Fortunately, the path change above makes the
|
||||
# default values (clang and clang++) resolve to the correct compiler on macOS.
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
if [ "$CXX" = "clang++" ]; then export CXX="clang++-8" CC="clang-8"; fi;
|
||||
fi
|
||||
- echo ${CC}
|
||||
- echo ${CXX}
|
||||
- ${CXX} --version
|
||||
- cmake --version
|
||||
|
||||
before_script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- cmake --build .
|
||||
- cd ..
|
||||
|
||||
script:
|
||||
- cd build && ctest --verbose && cd ..
|
||||
- "if [ -f build/db_bench ] ; then build/db_bench ; fi"
|
||||
- "if [ -f build/db_bench_sqlite3 ] ; then build/db_bench_sqlite3 ; fi"
|
||||
- "if [ -f build/db_bench_tree_db ] ; then build/db_bench_tree_db ; fi"
|
||||
434
CMakeLists.txt
434
CMakeLists.txt
@@ -2,19 +2,25 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
# Keep the version below in sync with the one in db.h
|
||||
project(leveldb VERSION 1.22.0 LANGUAGES C CXX)
|
||||
project(leveldb VERSION 1.23.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)
|
||||
# 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)
|
||||
|
||||
# This project requires C++11.
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
# C++ standard can be overridden when this is used as a sub-project.
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
# This project requires C++17.
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
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,15 +34,13 @@ option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON)
|
||||
option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON)
|
||||
option(LEVELDB_INSTALL "Install LevelDB's header and library" ON)
|
||||
|
||||
include(TestBigEndian)
|
||||
test_big_endian(LEVELDB_IS_BIG_ENDIAN)
|
||||
|
||||
include(CheckIncludeFile)
|
||||
check_include_file("unistd.h" HAVE_UNISTD_H)
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists(crc32c crc32c_value "" HAVE_CRC32C)
|
||||
check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)
|
||||
check_library_exists(zstd zstd_compress "" HAVE_ZSTD)
|
||||
check_library_exists(tcmalloc malloc "" HAVE_TCMALLOC)
|
||||
|
||||
include(CheckCXXSymbolExists)
|
||||
@@ -47,26 +51,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)
|
||||
check_cxx_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
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 +100,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 +114,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 +213,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
|
||||
@@ -251,6 +274,9 @@ endif(HAVE_CRC32C)
|
||||
if(HAVE_SNAPPY)
|
||||
target_link_libraries(leveldb snappy)
|
||||
endif(HAVE_SNAPPY)
|
||||
if(HAVE_ZSTD)
|
||||
target_link_libraries(leveldb zstd)
|
||||
endif(HAVE_ZSTD)
|
||||
if(HAVE_TCMALLOC)
|
||||
target_link_libraries(leveldb tcmalloc)
|
||||
endif(HAVE_TCMALLOC)
|
||||
@@ -260,13 +286,84 @@ 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")
|
||||
|
||||
# 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)
|
||||
|
||||
add_executable(leveldb_tests "")
|
||||
target_sources(leveldb_tests
|
||||
PRIVATE
|
||||
# "db/fault_injection_test.cc"
|
||||
# "issues/issue178_test.cc"
|
||||
# "issues/issue200_test.cc"
|
||||
# "issues/issue320_test.cc"
|
||||
"${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h"
|
||||
# "util/env_test.cc"
|
||||
"util/status_test.cc"
|
||||
"util/no_destructor_test.cc"
|
||||
"util/testutil.cc"
|
||||
"util/testutil.h"
|
||||
)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_sources(leveldb_tests
|
||||
PRIVATE
|
||||
"db/autocompact_test.cc"
|
||||
"db/corruption_test.cc"
|
||||
"db/db_test.cc"
|
||||
"db/dbformat_test.cc"
|
||||
"db/filename_test.cc"
|
||||
"db/log_test.cc"
|
||||
"db/recovery_test.cc"
|
||||
"db/skiplist_test.cc"
|
||||
"db/version_edit_test.cc"
|
||||
"db/version_set_test.cc"
|
||||
"db/write_batch_test.cc"
|
||||
"helpers/memenv/memenv_test.cc"
|
||||
"table/filter_block_test.cc"
|
||||
"table/table_test.cc"
|
||||
"util/arena_test.cc"
|
||||
"util/bloom_test.cc"
|
||||
"util/cache_test.cc"
|
||||
"util/coding_test.cc"
|
||||
"util/crc32c_test.cc"
|
||||
"util/hash_test.cc"
|
||||
"util/logging_test.cc"
|
||||
)
|
||||
endif(NOT BUILD_SHARED_LIBS)
|
||||
target_link_libraries(leveldb_tests leveldb gmock gtest gtest_main)
|
||||
target_compile_definitions(leveldb_tests
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
)
|
||||
if (NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
target_compile_definitions(leveldb_tests
|
||||
PRIVATE
|
||||
LEVELDB_HAS_PORT_CONFIG_H=1
|
||||
)
|
||||
endif(NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
|
||||
add_test(NAME "leveldb_tests" COMMAND "leveldb_tests")
|
||||
|
||||
function(leveldb_test test_file)
|
||||
get_filename_component(test_target_name "${test_file}" NAME_WE)
|
||||
|
||||
@@ -274,14 +371,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)
|
||||
target_compile_definitions("${test_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
@@ -296,54 +391,25 @@ 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("${PROJECT_SOURCE_DIR}/issues/issue178_test.cc")
|
||||
leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue200_test.cc")
|
||||
leveldb_test("${PROJECT_SOURCE_DIR}/issues/issue320_test.cc")
|
||||
|
||||
leveldb_test("${PROJECT_SOURCE_DIR}/util/env_test.cc")
|
||||
leveldb_test("${PROJECT_SOURCE_DIR}/util/status_test.cc")
|
||||
leveldb_test("${PROJECT_SOURCE_DIR}/util/no_destructor_test.cc")
|
||||
leveldb_test("db/c_test.c")
|
||||
|
||||
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")
|
||||
|
||||
# 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)
|
||||
|
||||
if(LEVELDB_BUILD_BENCHMARKS)
|
||||
# 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")
|
||||
|
||||
function(leveldb_benchmark bench_file)
|
||||
get_filename_component(bench_target_name "${bench_file}" NAME_WE)
|
||||
|
||||
@@ -351,16 +417,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 benchmark)
|
||||
target_compile_definitions("${bench_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
@@ -374,12 +438,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 +463,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 +477,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)
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
# Contributing
|
||||
# How to Contribute
|
||||
|
||||
We'd love to accept your code patches! However, before we can take them, we
|
||||
have to jump a couple of legal hurdles.
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreements
|
||||
## Contributor License Agreement
|
||||
|
||||
Please fill out either the individual or corporate Contributor License
|
||||
Agreement as appropriate.
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution;
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then sign an [individual CLA](https://developers.google.com/open-source/cla/individual).
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate).
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it.
|
||||
## Code Reviews
|
||||
|
||||
## Submitting a Patch
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
||||
|
||||
1. Sign the contributors license agreement above.
|
||||
2. Decide which code you want to submit. A submission should be a set of changes
|
||||
that addresses one issue in the [issue tracker](https://github.com/google/leveldb/issues).
|
||||
Please don't mix more than one logical change per submission, because it makes
|
||||
the history hard to follow. If you want to make a change
|
||||
(e.g. add a sample or feature) that doesn't have a corresponding issue in the
|
||||
issue tracker, please create one.
|
||||
3. **Submitting**: When you are ready to submit, send us a Pull Request. Be
|
||||
sure to include the issue number you fixed and the name you used to sign
|
||||
the CLA.
|
||||
See [the README](README.md#contributing-to-the-leveldb-project) for areas
|
||||
where we are likely to accept external contributions.
|
||||
|
||||
## Writing Code ##
|
||||
## Community Guidelines
|
||||
|
||||
If your contribution contains code, please make sure that it follows
|
||||
[the style guide](http://google.github.io/styleguide/cppguide.html).
|
||||
Otherwise we will have to ask you to make changes, and that's no fun for anyone.
|
||||
This project follows [Google's Open Source Community
|
||||
Guidelines](https://opensource.google/conduct/).
|
||||
59
README.md
59
README.md
@@ -1,7 +1,11 @@
|
||||
**LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.**
|
||||
LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.
|
||||
|
||||
[](https://travis-ci.org/google/leveldb)
|
||||
[](https://ci.appveyor.com/project/pwnall/leveldb)
|
||||
> **This repository is receiving very limited maintenance. We will only review the following types of changes.**
|
||||
>
|
||||
> * Fixes for critical bugs, such as data loss or memory corruption
|
||||
> * Changes absolutely needed by internally supported leveldb clients. These typically fix breakage introduced by a language/standard library/OS update
|
||||
|
||||
[](https://github.com/google/leveldb/actions/workflows/build.yml)
|
||||
|
||||
Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
|
||||
|
||||
@@ -14,12 +18,12 @@ 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/), but [Zstd compression](https://facebook.github.io/zstd/) is also supported.
|
||||
* External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.
|
||||
|
||||
# Documentation
|
||||
|
||||
[LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code.
|
||||
[LevelDB library documentation](https://github.com/google/leveldb/blob/main/doc/index.md) is online and bundled with the source code.
|
||||
|
||||
# Limitations
|
||||
|
||||
@@ -27,6 +31,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.
|
||||
@@ -67,6 +77,11 @@ Please see the CMake documentation and `CMakeLists.txt` for more advanced usage.
|
||||
|
||||
# Contributing to the leveldb Project
|
||||
|
||||
> **This repository is receiving very limited maintenance. We will only review the following types of changes.**
|
||||
>
|
||||
> * Bug fixes
|
||||
> * Changes absolutely needed by internally supported leveldb clients. These typically fix breakage introduced by a language/standard library/OS update
|
||||
|
||||
The leveldb project welcomes contributions. leveldb's primary goal is to be
|
||||
a reliable and fast key/value store. Changes that are in line with the
|
||||
features/limitations outlined above, and meet the requirements below,
|
||||
@@ -94,6 +109,12 @@ Contribution requirements:
|
||||
clang-format -i --style=file <file>
|
||||
```
|
||||
|
||||
We are unlikely to accept contributions to the build configuration files, such
|
||||
as `CMakeLists.txt`. We are focused on maintaining a build configuration that
|
||||
allows us to test that the project works in a few supported configurations
|
||||
inside Google. We are not currently interested in supporting other requirements,
|
||||
such as different operating systems, compilers, or build systems.
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Before any pull request will be accepted the author must first sign a
|
||||
@@ -102,7 +123,7 @@ Contributor License Agreement (CLA) at https://cla.developers.google.com/.
|
||||
In order to keep the commit timeline linear
|
||||
[squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits)
|
||||
your changes down to a single commit and [rebase](https://git-scm.com/docs/git-rebase)
|
||||
on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed
|
||||
on google/leveldb/main. This keeps the commit timeline linear and more easily sync'ed
|
||||
with the internal repository at Google. More information at GitHub's
|
||||
[About Git rebase](https://help.github.com/articles/about-git-rebase/) page.
|
||||
|
||||
@@ -189,37 +210,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.
|
||||
|
||||
@@ -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:
|
||||
@@ -56,7 +60,9 @@ static const char* FLAGS_benchmarks =
|
||||
"fill100K,"
|
||||
"crc32c,"
|
||||
"snappycomp,"
|
||||
"snappyuncomp,";
|
||||
"snappyuncomp,"
|
||||
"zstdcomp,"
|
||||
"zstduncomp,";
|
||||
|
||||
// Number of key/values to place in database
|
||||
static int FLAGS_num = 1000000;
|
||||
@@ -77,6 +83,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 +109,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.
|
||||
@@ -108,14 +120,47 @@ static bool FLAGS_use_existing_db = false;
|
||||
// If true, reuse existing log/MANIFEST files when re-opening a database.
|
||||
static bool FLAGS_reuse_logs = false;
|
||||
|
||||
// If true, use compression.
|
||||
static bool FLAGS_compression = true;
|
||||
|
||||
// Use the db with the following name.
|
||||
static const char* FLAGS_db = nullptr;
|
||||
|
||||
// ZSTD compression level to try out
|
||||
static int FLAGS_zstd_compression_level = 1;
|
||||
|
||||
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 +193,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 +252,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 +285,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 +307,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 +325,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,9 +369,60 @@ 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) {}
|
||||
};
|
||||
|
||||
void Compress(
|
||||
ThreadState* thread, std::string name,
|
||||
std::function<bool(const char*, size_t, std::string*)> compress_func) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
int64_t bytes = 0;
|
||||
int64_t produced = 0;
|
||||
bool ok = true;
|
||||
std::string compressed;
|
||||
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
||||
ok = compress_func(input.data(), input.size(), &compressed);
|
||||
produced += compressed.size();
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
thread->stats.AddMessage("(" + name + " failure)");
|
||||
} else {
|
||||
char buf[100];
|
||||
std::snprintf(buf, sizeof(buf), "(output: %.1f%%)",
|
||||
(produced * 100.0) / bytes);
|
||||
thread->stats.AddMessage(buf);
|
||||
thread->stats.AddBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void Uncompress(
|
||||
ThreadState* thread, std::string name,
|
||||
std::function<bool(const char*, size_t, std::string*)> compress_func,
|
||||
std::function<bool(const char*, size_t, char*)> uncompress_func) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
std::string compressed;
|
||||
bool ok = compress_func(input.data(), input.size(), &compressed);
|
||||
int64_t bytes = 0;
|
||||
char* uncompressed = new char[input.size()];
|
||||
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
||||
ok = uncompress_func(compressed.data(), compressed.size(), uncompressed);
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
delete[] uncompressed;
|
||||
|
||||
if (!ok) {
|
||||
thread->stats.AddMessage("(" + name + " failure)");
|
||||
} else {
|
||||
thread->stats.AddBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class Benchmark {
|
||||
@@ -320,55 +436,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 +510,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 +528,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 +613,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")) {
|
||||
@@ -509,6 +635,10 @@ class Benchmark {
|
||||
method = &Benchmark::SnappyCompress;
|
||||
} else if (name == Slice("snappyuncomp")) {
|
||||
method = &Benchmark::SnappyUncompress;
|
||||
} else if (name == Slice("zstdcomp")) {
|
||||
method = &Benchmark::ZstdCompress;
|
||||
} else if (name == Slice("zstduncomp")) {
|
||||
method = &Benchmark::ZstdUncompress;
|
||||
} else if (name == Slice("heapprofile")) {
|
||||
HeapProfile();
|
||||
} else if (name == Slice("stats")) {
|
||||
@@ -517,14 +647,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 +716,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 +741,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,57 +766,37 @@ 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);
|
||||
}
|
||||
|
||||
void SnappyCompress(ThreadState* thread) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
int64_t bytes = 0;
|
||||
int64_t produced = 0;
|
||||
bool ok = true;
|
||||
std::string compressed;
|
||||
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
||||
ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
|
||||
produced += compressed.size();
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
thread->stats.AddMessage("(snappy failure)");
|
||||
} else {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "(output: %.1f%%)",
|
||||
(produced * 100.0) / bytes);
|
||||
thread->stats.AddMessage(buf);
|
||||
thread->stats.AddBytes(bytes);
|
||||
}
|
||||
Compress(thread, "snappy", &port::Snappy_Compress);
|
||||
}
|
||||
|
||||
void SnappyUncompress(ThreadState* thread) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
std::string compressed;
|
||||
bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);
|
||||
int64_t bytes = 0;
|
||||
char* uncompressed = new char[input.size()];
|
||||
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
||||
ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
|
||||
uncompressed);
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
delete[] uncompressed;
|
||||
Uncompress(thread, "snappy", &port::Snappy_Compress,
|
||||
&port::Snappy_Uncompress);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
thread->stats.AddMessage("(snappy failure)");
|
||||
} else {
|
||||
thread->stats.AddBytes(bytes);
|
||||
}
|
||||
void ZstdCompress(ThreadState* thread) {
|
||||
Compress(thread, "zstd",
|
||||
[](const char* input, size_t length, std::string* output) {
|
||||
return port::Zstd_Compress(FLAGS_zstd_compression_level, input,
|
||||
length, output);
|
||||
});
|
||||
}
|
||||
|
||||
void ZstdUncompress(ThreadState* thread) {
|
||||
Uncompress(
|
||||
thread, "zstd",
|
||||
[](const char* input, size_t length, std::string* output) {
|
||||
return port::Zstd_Compress(FLAGS_zstd_compression_level, input,
|
||||
length, output);
|
||||
},
|
||||
&port::Zstd_Uncompress);
|
||||
}
|
||||
|
||||
void Open() {
|
||||
@@ -688,13 +808,18 @@ 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;
|
||||
options.compression =
|
||||
FLAGS_compression ? kSnappyCompression : kNoCompression;
|
||||
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 +838,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 +846,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 +895,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 +926,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 +938,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 +953,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 +1003,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 +1013,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 +1035,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 +1044,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,12 +1081,18 @@ 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;
|
||||
} else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 &&
|
||||
(n == 0 || n == 1)) {
|
||||
FLAGS_reuse_logs = n;
|
||||
} else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
|
||||
(n == 0 || n == 1)) {
|
||||
FLAGS_compression = 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) {
|
||||
@@ -954,6 +1107,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 +1118,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);
|
||||
}
|
||||
}
|
||||
|
||||
92
benchmarks/db_bench_log.cc
Normal file
92
benchmarks/db_bench_log.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2019 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "db/version_set.h"
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "port/port.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string MakeKey(unsigned int num) {
|
||||
char buf[30];
|
||||
std::snprintf(buf, sizeof(buf), "%016u", num);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
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_LEVELDB_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
delete db;
|
||||
db = nullptr;
|
||||
|
||||
Env* env = Env::Default();
|
||||
|
||||
port::Mutex mu;
|
||||
MutexLock l(&mu);
|
||||
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
Options options;
|
||||
VersionSet vset(dbname, &options, nullptr, &cmp);
|
||||
bool save_manifest;
|
||||
ASSERT_LEVELDB_OK(vset.Recover(&save_manifest));
|
||||
VersionEdit vbase;
|
||||
uint64_t fnum = 1;
|
||||
for (int i = 0; i < num_base_files; i++) {
|
||||
InternalKey start(MakeKey(2 * fnum), 1, kTypeValue);
|
||||
InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion);
|
||||
vbase.AddFile(2, fnum++, 1 /* file size */, start, limit);
|
||||
}
|
||||
ASSERT_LEVELDB_OK(vset.LogAndApply(&vbase, &mu));
|
||||
|
||||
uint64_t start_micros = env->NowMicros();
|
||||
|
||||
for (auto st : state) {
|
||||
VersionEdit vedit;
|
||||
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);
|
||||
vset.LogAndApply(&vedit, &mu);
|
||||
}
|
||||
|
||||
uint64_t stop_micros = env->NowMicros();
|
||||
unsigned int us = stop_micros - start_micros;
|
||||
char buf[16];
|
||||
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
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake")
|
||||
9
cmake/leveldbConfig.cmake.in
Normal file
9
cmake/leveldbConfig.cmake.in
Normal file
@@ -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)
|
||||
@@ -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,8 @@ 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(); }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
69
db/c.cc
69
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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
@@ -13,14 +14,13 @@
|
||||
#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,21 @@ 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(); }
|
||||
|
||||
133
db/db_impl.cc
133
db/db_impl.cc
@@ -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>
|
||||
@@ -197,6 +196,9 @@ Status DBImpl::NewDB() {
|
||||
std::string record;
|
||||
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);
|
||||
}
|
||||
@@ -615,6 +629,11 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
background_work_finished_signal_.Wait();
|
||||
}
|
||||
}
|
||||
// Finish current background compaction in the case where
|
||||
// `background_work_finished_signal_` was signalled due to an error.
|
||||
while (background_compaction_scheduled_) {
|
||||
background_work_finished_signal_.Wait();
|
||||
}
|
||||
if (manual_compaction_ == &manual) {
|
||||
// Cancel my manual compaction since we aborted early for some reason.
|
||||
manual_compaction_ = nullptr;
|
||||
@@ -627,7 +646,8 @@ Status DBImpl::TEST_CompactMemTable() {
|
||||
if (s.ok()) {
|
||||
// Wait until the compaction completes
|
||||
MutexLock l(&mutex_);
|
||||
while (imm_ != nullptr && bg_error_.ok()) {
|
||||
while (imm_ != nullptr && bg_error_.ok() &&
|
||||
!shutting_down_.load(std::memory_order_acquire)) {
|
||||
background_work_finished_signal_.Wait();
|
||||
}
|
||||
if (imm_ != nullptr) {
|
||||
@@ -719,7 +739,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 +759,7 @@ void DBImpl::BackgroundCompaction() {
|
||||
}
|
||||
CleanupCompaction(compact);
|
||||
c->ReleaseInputs();
|
||||
DeleteObsoleteFiles();
|
||||
RemoveObsoleteFiles();
|
||||
}
|
||||
delete c;
|
||||
|
||||
@@ -893,17 +913,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 +1223,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 +1233,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 +1242,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 +1252,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);
|
||||
}
|
||||
@@ -1353,8 +1374,22 @@ Status DBImpl::MakeRoomForWrite(bool force) {
|
||||
versions_->ReuseFileNumber(new_log_number);
|
||||
break;
|
||||
}
|
||||
|
||||
delete log_;
|
||||
|
||||
s = logfile_->Close();
|
||||
if (!s.ok()) {
|
||||
// We may have lost some data written to the previous log file.
|
||||
// Switch to the new log file anyway, but record as a background
|
||||
// error so we do not attempt any more writes.
|
||||
//
|
||||
// We could perhaps attempt to save the memtable corresponding
|
||||
// to log file and suppress the error if that works, but that
|
||||
// would add more complexity in a critical code path.
|
||||
RecordBackgroundError(s);
|
||||
}
|
||||
delete logfile_;
|
||||
|
||||
logfile_ = lfile;
|
||||
logfile_number_ = new_log_number;
|
||||
log_ = new log::Writer(lfile);
|
||||
@@ -1386,26 +1421,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 +1457,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 +1468,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 +1481,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 +1498,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 +1530,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 +1543,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 +1563,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;
|
||||
}
|
||||
|
||||
29
db/db_impl.h
29
db/db_impl.h
@@ -33,20 +33,21 @@ class DBImpl : public DB {
|
||||
DBImpl(const DBImpl&) = delete;
|
||||
DBImpl& operator=(const DBImpl&) = delete;
|
||||
|
||||
virtual ~DBImpl();
|
||||
~DBImpl() override;
|
||||
|
||||
// Implementations of the DB interface
|
||||
virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value);
|
||||
virtual Status Delete(const WriteOptions&, const Slice& key);
|
||||
virtual Status Write(const WriteOptions& options, WriteBatch* updates);
|
||||
virtual Status Get(const ReadOptions& options, const Slice& key,
|
||||
std::string* value);
|
||||
virtual Iterator* NewIterator(const ReadOptions&);
|
||||
virtual const Snapshot* GetSnapshot();
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot);
|
||||
virtual bool GetProperty(const Slice& property, std::string* value);
|
||||
virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes);
|
||||
virtual void CompactRange(const Slice* begin, const Slice* end);
|
||||
Status Put(const WriteOptions&, const Slice& key,
|
||||
const Slice& value) override;
|
||||
Status Delete(const WriteOptions&, const Slice& key) override;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
Status Get(const ReadOptions& options, const Slice& key,
|
||||
std::string* value) override;
|
||||
Iterator* NewIterator(const ReadOptions&) override;
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
bool GetProperty(const Slice& property, std::string* value) override;
|
||||
void GetApproximateSizes(const Range* range, int n, uint64_t* sizes) override;
|
||||
void CompactRange(const Slice* begin, const Slice* end) override;
|
||||
|
||||
// Extra methods (for testing) that are not in the public DB interface
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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"
|
||||
|
||||
751
db/db_test.cc
751
db/db_test.cc
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,18 @@ TEST(FormatTest, InternalKeyShortestSuccessor) {
|
||||
ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
TEST(FormatTest, ParsedInternalKeyDebugString) {
|
||||
ParsedInternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue);
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,14 @@ 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(); }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
@@ -127,5 +125,3 @@ TEST(FileNameTest, Construction) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
@@ -24,7 +24,7 @@ class Reader {
|
||||
public:
|
||||
virtual ~Reporter();
|
||||
|
||||
// Some corruption was detected. "size" is the approximate number
|
||||
// Some corruption was detected. "bytes" is the approximate number
|
||||
// of bytes dropped due to the corruption.
|
||||
virtual void Corruption(size_t bytes, const Status& status) = 0;
|
||||
};
|
||||
|
||||
100
db/log_test.cc
100
db/log_test.cc
@@ -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,45 @@ 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(); }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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_); }
|
||||
@@ -77,6 +78,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key,
|
||||
// Format of an entry is concatenation of:
|
||||
// key_size : varint32 of internal_key.size()
|
||||
// key bytes : char[internal_key.size()]
|
||||
// tag : uint64((sequence << 8) | type)
|
||||
// value_size : varint32 of value.size()
|
||||
// value bytes : char[value.size()]
|
||||
size_t key_size = key.size();
|
||||
@@ -87,12 +89,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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -10,15 +11,14 @@
|
||||
#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_), ¤t));
|
||||
EXPECT_LEVELDB_OK(
|
||||
ReadFileToString(env_, CurrentFileName(dbname_), ¤t));
|
||||
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,15 +322,18 @@ 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();
|
||||
#if defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
// TODO(crbug.com/760362): See comment in MakeIOError() from env_chromium.cc.
|
||||
ASSERT_TRUE(status.IsIOError());
|
||||
#else
|
||||
ASSERT_TRUE(status.IsCorruption());
|
||||
#endif // defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
11
db/repair.cc
11
db/repair.cc
@@ -145,7 +145,7 @@ class Repairer {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
uint64_t lognum;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
// We print error messages for corruption, but continue repairing.
|
||||
Log(info_log, "Log #%llu: dropping %d bytes; %s",
|
||||
(unsigned long long)lognum, static_cast<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;
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Arena;
|
||||
|
||||
template <typename Key, class Comparator>
|
||||
class SkipList {
|
||||
private:
|
||||
@@ -243,7 +241,7 @@ int SkipList<Key, Comparator>::RandomHeight() {
|
||||
// Increase height with probability 1 in kBranching
|
||||
static const unsigned int kBranching = 4;
|
||||
int height = 1;
|
||||
while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {
|
||||
while (height < kMaxHeight && rnd_.OneIn(kBranching)) {
|
||||
height++;
|
||||
}
|
||||
assert(height > 0);
|
||||
|
||||
@@ -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);
|
||||
@@ -365,5 +366,3 @@ TEST(SkipTest, Concurrent4) { RunConcurrent(4); }
|
||||
TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -25,7 +25,7 @@ class SnapshotImpl : public Snapshot {
|
||||
friend class SnapshotList;
|
||||
|
||||
// SnapshotImpl is kept in a doubly-linked circular list. The SnapshotList
|
||||
// implementation operates on the next/previous fields direcly.
|
||||
// implementation operates on the next/previous fields directly.
|
||||
SnapshotImpl* prev_;
|
||||
SnapshotImpl* next_;
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -23,6 +22,10 @@ class Env;
|
||||
class TableCache {
|
||||
public:
|
||||
TableCache(const std::string& dbname, const Options& options, int entries);
|
||||
|
||||
TableCache(const TableCache&) = delete;
|
||||
TableCache& operator=(const TableCache&) = delete;
|
||||
|
||||
~TableCache();
|
||||
|
||||
// Return an iterator for the specified file number (the corresponding
|
||||
|
||||
@@ -34,6 +34,7 @@ void VersionEdit::Clear() {
|
||||
has_prev_log_number_ = false;
|
||||
has_next_file_number_ = false;
|
||||
has_last_sequence_ = false;
|
||||
compact_pointers_.clear();
|
||||
deleted_files_.clear();
|
||||
new_files_.clear();
|
||||
}
|
||||
@@ -66,11 +67,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 +87,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 +232,11 @@ std::string VersionEdit::DebugString() const {
|
||||
r.append(" ");
|
||||
r.append(compact_pointers_[i].second.DebugString());
|
||||
}
|
||||
for (DeletedFileSet::const_iterator iter = deleted_files_.begin();
|
||||
iter != deleted_files_.end(); ++iter) {
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -40,5 +39,3 @@ TEST(VersionEditTest, EncodeDecode) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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;
|
||||
state->last_file_read = f;
|
||||
state->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->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Status::NotFound(Slice()); // Use an empty error message for speed
|
||||
// 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_;
|
||||
|
||||
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) {
|
||||
@@ -647,7 +626,7 @@ class VersionSet::Builder {
|
||||
}
|
||||
|
||||
// Apply all of the edits in *edit to the current state.
|
||||
void Apply(VersionEdit* edit) {
|
||||
void Apply(const VersionEdit* edit) {
|
||||
// Update compaction pointers
|
||||
for (size_t i = 0; i < edit->compact_pointers_.size(); i++) {
|
||||
const int level = edit->compact_pointers_[i].first;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -830,7 +806,6 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
|
||||
// first call to LogAndApply (when opening the database).
|
||||
assert(descriptor_file_ == nullptr);
|
||||
new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_);
|
||||
edit->SetNextFile(next_file_number_);
|
||||
s = env_->NewWritableFile(new_manifest_file, &descriptor_file_);
|
||||
if (s.ok()) {
|
||||
descriptor_log_ = new log::Writer(descriptor_file_);
|
||||
@@ -876,7 +851,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 +861,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 +897,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 +907,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 +982,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 +1105,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;
|
||||
}
|
||||
|
||||
@@ -1321,7 +1303,7 @@ Compaction* VersionSet::PickCompaction() {
|
||||
return c;
|
||||
}
|
||||
|
||||
// Finds the largest key in a vector of files. Returns true if files it not
|
||||
// Finds the largest key in a vector of files. Returns true if files is not
|
||||
// empty.
|
||||
bool FindLargestKey(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
@@ -1409,6 +1391,7 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
||||
|
||||
current_->GetOverlappingInputs(level + 1, &smallest, &largest,
|
||||
&c->inputs_[1]);
|
||||
AddBoundaryInputs(icmp_, current_->files_[level + 1], &c->inputs_[1]);
|
||||
|
||||
// Get entire range covered by compaction
|
||||
InternalKey all_start, all_limit;
|
||||
@@ -1431,6 +1414,7 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
||||
std::vector<FileMetaData*> expanded1;
|
||||
current_->GetOverlappingInputs(level + 1, &new_start, &new_limit,
|
||||
&expanded1);
|
||||
AddBoundaryInputs(icmp_, current_->files_[level + 1], &expanded1);
|
||||
if (expanded1.size() == c->inputs_[1].size()) {
|
||||
Log(options_->info_log,
|
||||
"Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n",
|
||||
@@ -1525,7 +1509,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 +1519,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
|
||||
|
||||
@@ -59,9 +59,6 @@ bool SomeFileOverlapsRange(const InternalKeyComparator& icmp,
|
||||
|
||||
class Version {
|
||||
public:
|
||||
// Lookup the value for key. If found, store it in *val and
|
||||
// return OK. Else return a non-OK status. Fills *stats.
|
||||
// REQUIRES: lock is not held
|
||||
struct GetStats {
|
||||
FileMetaData* seek_file;
|
||||
int seek_file_level;
|
||||
@@ -72,6 +69,9 @@ class Version {
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
void AddIterators(const ReadOptions&, std::vector<Iterator*>* iters);
|
||||
|
||||
// Lookup the value for key. If found, store it in *val and
|
||||
// return OK. Else return a non-OK status. Fills *stats.
|
||||
// REQUIRES: lock is not held
|
||||
Status Get(const ReadOptions&, const LookupKey& key, std::string* val,
|
||||
GetStats* stats);
|
||||
|
||||
|
||||
@@ -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)));
|
||||
@@ -328,5 +329,3 @@ TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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_++;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
@@ -133,5 +130,3 @@ TEST(WriteBatchTest, ApproximateSize) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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/main/benchmarks/db_bench.cc">benchmarks/db_bench.cc</a>.</li>
|
||||
<li> <b>SQLite:</b> <a href="https://github.com/google/leveldb/blob/main/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/main/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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
15
doc/index.md
15
doc/index.md
@@ -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
|
||||
|
||||
@@ -369,6 +369,7 @@ leveldb::Iterator* it = db->NewIterator(options);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
...
|
||||
}
|
||||
delete it;
|
||||
```
|
||||
|
||||
### Key Layout
|
||||
@@ -424,21 +425,21 @@ spaces. For example:
|
||||
```c++
|
||||
class CustomFilterPolicy : public leveldb::FilterPolicy {
|
||||
private:
|
||||
FilterPolicy* builtin_policy_;
|
||||
leveldb::FilterPolicy* builtin_policy_;
|
||||
|
||||
public:
|
||||
CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) {}
|
||||
CustomFilterPolicy() : builtin_policy_(leveldb::NewBloomFilterPolicy(10)) {}
|
||||
~CustomFilterPolicy() { delete builtin_policy_; }
|
||||
|
||||
const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
|
||||
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
||||
void CreateFilter(const leveldb::Slice* keys, int n, std::string* dst) const {
|
||||
// Use builtin bloom filter code after removing trailing spaces
|
||||
std::vector<Slice> trimmed(n);
|
||||
std::vector<leveldb::Slice> trimmed(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trimmed[i] = RemoveTrailingSpaces(keys[i]);
|
||||
}
|
||||
return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
|
||||
builtin_policy_->CreateFilter(trimmed.data(), n, dst);
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -478,7 +479,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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -255,5 +257,3 @@ TEST(MemEnvTest, DBTest) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -40,16 +40,16 @@
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_C_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_C_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Exported types */
|
||||
|
||||
typedef struct leveldb_t leveldb_t;
|
||||
@@ -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" */
|
||||
|
||||
@@ -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"
|
||||
@@ -96,14 +96,6 @@ class LEVELDB_EXPORT Cache {
|
||||
// Return an estimate of the combined charges of all elements stored in the
|
||||
// cache.
|
||||
virtual size_t TotalCharge() const = 0;
|
||||
|
||||
private:
|
||||
void LRU_Remove(Handle* e);
|
||||
void LRU_Append(Handle* e);
|
||||
void Unref(Handle* e);
|
||||
|
||||
struct Rep;
|
||||
Rep* rep_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,30 +13,26 @@
|
||||
#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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -26,7 +26,8 @@ enum CompressionType {
|
||||
// NOTE: do not change the values of existing entries, as these are
|
||||
// part of the persistent format on disk.
|
||||
kNoCompression = 0x0,
|
||||
kSnappyCompression = 0x1
|
||||
kSnappyCompression = 0x1,
|
||||
kZstdCompression = 0x2,
|
||||
};
|
||||
|
||||
// Options to control the behavior of a database (passed to DB::Open)
|
||||
@@ -130,6 +131,10 @@ struct LEVELDB_EXPORT Options {
|
||||
// efficiently detect that and will switch to uncompressed mode.
|
||||
CompressionType compression = kSnappyCompression;
|
||||
|
||||
// Compression level for zstd.
|
||||
// Currently only the range [-5,22] is supported. Default is 1.
|
||||
int zstd_compression_level = 1;
|
||||
|
||||
// EXPERIMENTAL: If true, append to existing MANIFEST and log files
|
||||
// when a database is opened. This can significantly speed up open.
|
||||
//
|
||||
@@ -144,8 +149,6 @@ struct LEVELDB_EXPORT Options {
|
||||
|
||||
// Options that control read operations
|
||||
struct LEVELDB_EXPORT ReadOptions {
|
||||
ReadOptions() = default;
|
||||
|
||||
// If true, all data read from underlying storage will be
|
||||
// verified against corresponding checksums.
|
||||
bool verify_checksums = false;
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
#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"
|
||||
@@ -52,6 +51,9 @@ class LEVELDB_EXPORT Slice {
|
||||
// Return true iff the length of the referenced data is zero
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
const char* begin() const { return data(); }
|
||||
const char* end() const { return data() + size(); }
|
||||
|
||||
// Return the ith byte in the referenced data.
|
||||
// REQUIRES: n < size()
|
||||
char operator[](size_t n) const {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -84,5 +83,3 @@ TEST(Issue178, Test) {
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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");
|
||||
@@ -53,5 +52,3 @@ TEST(Issue200, Test) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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());
|
||||
@@ -124,5 +124,3 @@ TEST(Issue320, Test) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
#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
|
||||
@@ -25,10 +30,9 @@
|
||||
#cmakedefine01 HAVE_SNAPPY
|
||||
#endif // !defined(HAVE_SNAPPY)
|
||||
|
||||
// Define to 1 if your processor stores words with the most significant byte
|
||||
// first (like Motorola and SPARC, unlike Intel and VAX).
|
||||
#if !defined(LEVELDB_IS_BIG_ENDIAN)
|
||||
#cmakedefine01 LEVELDB_IS_BIG_ENDIAN
|
||||
#endif // !defined(LEVELDB_IS_BIG_ENDIAN)
|
||||
// Define to 1 if you have Zstd.
|
||||
#if !defined(HAVE_Zstd)
|
||||
#cmakedefine01 HAVE_ZSTD
|
||||
#endif // !defined(HAVE_ZSTD)
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
|
||||
@@ -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.
|
||||
@@ -59,7 +55,7 @@ class CondVar {
|
||||
void Signal();
|
||||
|
||||
// Wake up all waiting threads.
|
||||
void SignallAll();
|
||||
void SignalAll();
|
||||
};
|
||||
|
||||
// ------------------ Compression -------------------
|
||||
@@ -76,7 +72,7 @@ bool Snappy_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result);
|
||||
|
||||
// Attempt to snappy uncompress input[0,input_length-1] into *output.
|
||||
// Returns true if successful, false if the input is invalid lightweight
|
||||
// Returns true if successful, false if the input is invalid snappy
|
||||
// compressed data.
|
||||
//
|
||||
// REQUIRES: at least the first "n" bytes of output[] must be writable
|
||||
@@ -85,6 +81,26 @@ bool Snappy_GetUncompressedLength(const char* input, size_t length,
|
||||
bool Snappy_Uncompress(const char* input_data, size_t input_length,
|
||||
char* output);
|
||||
|
||||
// Store the zstd compression of "input[0,input_length-1]" in *output.
|
||||
// Returns false if zstd is not supported by this port.
|
||||
bool Zstd_Compress(int level, const char* input, size_t input_length,
|
||||
std::string* output);
|
||||
|
||||
// If input[0,input_length-1] looks like a valid zstd compressed
|
||||
// buffer, store the size of the uncompressed data in *result and
|
||||
// return true. Else return false.
|
||||
bool Zstd_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result);
|
||||
|
||||
// Attempt to zstd uncompress input[0,input_length-1] into *output.
|
||||
// Returns true if successful, false if the input is invalid zstd
|
||||
// compressed data.
|
||||
//
|
||||
// REQUIRES: at least the first "n" bytes of output[] must be writable
|
||||
// where "n" is the result of a successful call to
|
||||
// Zstd_GetUncompressedLength.
|
||||
bool Zstd_Uncompress(const char* input_data, size_t input_length, char* output);
|
||||
|
||||
// ------------------ Miscellaneous -------------------
|
||||
|
||||
// If heap profiling is not supported, returns false.
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#if HAVE_SNAPPY
|
||||
#include <snappy.h>
|
||||
#endif // HAVE_SNAPPY
|
||||
#if HAVE_ZSTD
|
||||
#define ZSTD_STATIC_LINKING_ONLY // For ZSTD_compressionParameters.
|
||||
#include <zstd.h>
|
||||
#endif // HAVE_ZSTD
|
||||
|
||||
#include <cassert>
|
||||
#include <condition_variable> // NOLINT
|
||||
@@ -41,8 +45,6 @@
|
||||
namespace leveldb {
|
||||
namespace port {
|
||||
|
||||
static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN;
|
||||
|
||||
class CondVar;
|
||||
|
||||
// Thinly wraps std::mutex.
|
||||
@@ -128,6 +130,74 @@ inline bool Snappy_Uncompress(const char* input, size_t length, char* output) {
|
||||
#endif // HAVE_SNAPPY
|
||||
}
|
||||
|
||||
inline bool Zstd_Compress(int level, const char* input, size_t length,
|
||||
std::string* output) {
|
||||
#if HAVE_ZSTD
|
||||
// Get the MaxCompressedLength.
|
||||
size_t outlen = ZSTD_compressBound(length);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
output->resize(outlen);
|
||||
ZSTD_CCtx* ctx = ZSTD_createCCtx();
|
||||
ZSTD_compressionParameters parameters =
|
||||
ZSTD_getCParams(level, std::max(length, size_t{1}), /*dictSize=*/0);
|
||||
ZSTD_CCtx_setCParams(ctx, parameters);
|
||||
outlen = ZSTD_compress2(ctx, &(*output)[0], output->size(), input, length);
|
||||
ZSTD_freeCCtx(ctx);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
output->resize(outlen);
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)level;
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool Zstd_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result) {
|
||||
#if HAVE_ZSTD
|
||||
size_t size = ZSTD_getFrameContentSize(input, length);
|
||||
if (size == 0) return false;
|
||||
*result = size;
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)result;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool Zstd_Uncompress(const char* input, size_t length, char* output) {
|
||||
#if HAVE_ZSTD
|
||||
size_t outlen;
|
||||
if (!Zstd_GetUncompressedLength(input, length, &outlen)) {
|
||||
return false;
|
||||
}
|
||||
ZSTD_DCtx* ctx = ZSTD_createDCtx();
|
||||
outlen = ZSTD_decompressDCtx(ctx, output, outlen, input, length);
|
||||
ZSTD_freeDCtx(ctx);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)func;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -28,12 +28,11 @@
|
||||
|
||||
#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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -120,5 +120,3 @@ TEST(FilterBlockTest, MultiChunk) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "table/format.h"
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "port/port.h"
|
||||
#include "table/block.h"
|
||||
#include "util/coding.h"
|
||||
@@ -40,6 +41,10 @@ void Footer::EncodeTo(std::string* dst) const {
|
||||
}
|
||||
|
||||
Status Footer::DecodeFrom(Slice* input) {
|
||||
if (input->size() < kEncodedLength) {
|
||||
return Status::Corruption("not an sstable (footer too short)");
|
||||
}
|
||||
|
||||
const char* magic_ptr = input->data() + kEncodedLength - 8;
|
||||
const uint32_t magic_lo = DecodeFixed32(magic_ptr);
|
||||
const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4);
|
||||
@@ -116,13 +121,31 @@ Status ReadBlock(RandomAccessFile* file, const ReadOptions& options,
|
||||
size_t ulength = 0;
|
||||
if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("corrupted compressed block contents");
|
||||
return Status::Corruption("corrupted snappy compressed block length");
|
||||
}
|
||||
char* ubuf = new char[ulength];
|
||||
if (!port::Snappy_Uncompress(data, n, ubuf)) {
|
||||
delete[] buf;
|
||||
delete[] ubuf;
|
||||
return Status::Corruption("corrupted compressed block contents");
|
||||
return Status::Corruption("corrupted snappy compressed block contents");
|
||||
}
|
||||
delete[] buf;
|
||||
result->data = Slice(ubuf, ulength);
|
||||
result->heap_allocated = true;
|
||||
result->cachable = true;
|
||||
break;
|
||||
}
|
||||
case kZstdCompression: {
|
||||
size_t ulength = 0;
|
||||
if (!port::Zstd_GetUncompressedLength(data, n, &ulength)) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("corrupted zstd compressed block length");
|
||||
}
|
||||
char* ubuf = new char[ulength];
|
||||
if (!port::Zstd_Uncompress(data, n, ubuf)) {
|
||||
delete[] buf;
|
||||
delete[] ubuf;
|
||||
return Status::Corruption("corrupted zstd compressed block contents");
|
||||
}
|
||||
delete[] buf;
|
||||
result->data = Slice(ubuf, ulength);
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "leveldb/table_builder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/env.h"
|
||||
@@ -168,6 +168,21 @@ void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kZstdCompression: {
|
||||
std::string* compressed = &r->compressed_output;
|
||||
if (port::Zstd_Compress(r->options.zstd_compression_level, raw.data(),
|
||||
raw.size(), compressed) &&
|
||||
compressed->size() < raw.size() - (raw.size() / 8u)) {
|
||||
block_contents = *compressed;
|
||||
} else {
|
||||
// Zstd not supported, or compressed less than 12.5%, so just
|
||||
// store uncompressed form
|
||||
block_contents = raw;
|
||||
type = kNoCompression;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
WriteRawBlock(block_contents, type, handle);
|
||||
r->compressed_output.clear();
|
||||
|
||||
@@ -7,18 +7,19 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/iterator.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "leveldb/table_builder.h"
|
||||
#include "table/block.h"
|
||||
#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 +39,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 +90,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 +112,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 +140,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 +153,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 +166,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 +178,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 +196,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 +212,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 +234,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 +260,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 +288,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 +305,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 +333,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 +367,7 @@ class DBConstructor : public Constructor {
|
||||
ASSERT_TRUE(status.ok()) << status.ToString();
|
||||
}
|
||||
|
||||
const Comparator* comparator_;
|
||||
const Comparator* const comparator_;
|
||||
DB* db_;
|
||||
};
|
||||
|
||||
@@ -402,7 +404,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 +486,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 +501,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 +512,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 +520,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 +533,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 +610,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 +621,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 +640,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 +649,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 +658,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 +669,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 +678,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 +698,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 +715,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 +737,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 +749,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");
|
||||
@@ -787,16 +785,28 @@ TEST(TableTest, ApproximateOffsetOfPlain) {
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000));
|
||||
}
|
||||
|
||||
static bool SnappyCompressionSupported() {
|
||||
static bool CompressionSupported(CompressionType type) {
|
||||
std::string out;
|
||||
Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
return port::Snappy_Compress(in.data(), in.size(), &out);
|
||||
if (type == kSnappyCompression) {
|
||||
return port::Snappy_Compress(in.data(), in.size(), &out);
|
||||
} else if (type == kZstdCompression) {
|
||||
return port::Zstd_Compress(/*level=*/1, in.data(), in.size(), &out);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST(TableTest, ApproximateOffsetOfCompressed) {
|
||||
if (!SnappyCompressionSupported()) {
|
||||
fprintf(stderr, "skipping compression tests\n");
|
||||
return;
|
||||
class CompressionTableTest
|
||||
: public ::testing::TestWithParam<std::tuple<CompressionType>> {};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(CompressionTests, CompressionTableTest,
|
||||
::testing::Values(kSnappyCompression,
|
||||
kZstdCompression));
|
||||
|
||||
TEST_P(CompressionTableTest, ApproximateOffsetOfCompressed) {
|
||||
CompressionType type = ::testing::get<0>(GetParam());
|
||||
if (!CompressionSupported(type)) {
|
||||
GTEST_SKIP() << "skipping compression test: " << type;
|
||||
}
|
||||
|
||||
Random rnd(301);
|
||||
@@ -810,7 +820,7 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
|
||||
KVMap kvmap;
|
||||
Options options;
|
||||
options.block_size = 1024;
|
||||
options.compression = kSnappyCompression;
|
||||
options.compression = type;
|
||||
c.Finish(options, &keys, &kvmap);
|
||||
|
||||
// Expected upper and lower bounds of space used by compressible strings.
|
||||
@@ -830,5 +840,3 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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);
|
||||
|
||||
1
third_party/benchmark
vendored
Submodule
1
third_party/benchmark
vendored
Submodule
Submodule third_party/benchmark added at 1a54956777
1
third_party/googletest
vendored
Submodule
1
third_party/googletest
vendored
Submodule
Submodule third_party/googletest added at a35bc7693c
@@ -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;
|
||||
@@ -61,5 +59,3 @@ TEST(ArenaTest, Simple) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -152,5 +152,3 @@ TEST(BloomTest, VaryingLengths) {
|
||||
// Different bits-per-byte
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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"
|
||||
@@ -53,7 +54,7 @@ struct LRUHandle {
|
||||
char key_data[1]; // Beginning of key
|
||||
|
||||
Slice key() const {
|
||||
// next_ is only equal to this if the LRU handle is the list head of an
|
||||
// next is only equal to this if the LRU handle is the list head of an
|
||||
// empty list. List heads never have meaningful keys.
|
||||
assert(next != this);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -222,5 +222,3 @@ TEST(CacheTest, ZeroSizeCache) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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
|
||||
@@ -168,16 +142,6 @@ bool GetVarint64(Slice* input, uint64_t* value) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetLengthPrefixedSlice(const char* p, const char* limit,
|
||||
Slice* result) {
|
||||
uint32_t len;
|
||||
p = GetVarint32Ptr(p, limit, &len);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (p + len > limit) return nullptr;
|
||||
*result = Slice(p, len);
|
||||
return p + len;
|
||||
}
|
||||
|
||||
bool GetLengthPrefixedSlice(Slice* input, Slice* result) {
|
||||
uint32_t len;
|
||||
if (GetVarint32(input, &len) && input->size() >= len) {
|
||||
|
||||
@@ -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"
|
||||
@@ -43,45 +42,64 @@ const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v);
|
||||
// Returns the length of the varint32 or varint64 encoding of "v"
|
||||
int VarintLength(uint64_t v);
|
||||
|
||||
// Lower-level versions of Put... that write directly into a character buffer
|
||||
// REQUIRES: dst has enough space for the value being written
|
||||
void EncodeFixed32(char* dst, uint32_t value);
|
||||
void EncodeFixed64(char* dst, uint64_t value);
|
||||
|
||||
// Lower-level versions of Put... that write directly into a character buffer
|
||||
// and return a pointer just past the last byte written.
|
||||
// REQUIRES: dst has enough space for the value being written
|
||||
char* EncodeVarint32(char* dst, uint32_t value);
|
||||
char* EncodeVarint64(char* dst, uint64_t value);
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -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++) {
|
||||
@@ -192,5 +191,3 @@ TEST(Coding, Strings) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
@@ -55,5 +54,3 @@ TEST(CRC, Mask) {
|
||||
|
||||
} // namespace crc32c
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
||||
32
util/env.cc
32
util/env.cc
@@ -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); }
|
||||
|
||||
RandomAccessFile::~RandomAccessFile() {}
|
||||
Status Env::RemoveFile(const std::string& fname) { return DeleteFile(fname); }
|
||||
Status Env::DeleteFile(const std::string& fname) { return RemoveFile(fname); }
|
||||
|
||||
WritableFile::~WritableFile() {}
|
||||
SequentialFile::~SequentialFile() = default;
|
||||
|
||||
Logger::~Logger() {}
|
||||
RandomAccessFile::~RandomAccessFile() = default;
|
||||
|
||||
FileLock::~FileLock() {}
|
||||
WritableFile::~WritableFile() = default;
|
||||
|
||||
Logger::~Logger() = default;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user