mirror of
https://github.com/google/leveldb.git
synced 2026-08-22 10:53:27 +08:00
Compare commits
91 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 | ||
|
|
f2dae4e74a | ||
|
|
f933ad1693 | ||
|
|
77e9dfad9f | ||
|
|
f314b63e5e | ||
|
|
3ee04c5cea |
@@ -1,36 +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 2019
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- RelWithDebInfo
|
||||
- Debug
|
||||
|
||||
build_script:
|
||||
- git submodule update --init --recursive
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if "%platform%"=="x86" (set CMAKE_GENERATOR_PLATFORM="Win32")
|
||||
else (set CMAKE_GENERATOR_PLATFORM="%platform%")
|
||||
- cmake --version
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" -A "%CMAKE_GENERATOR_PLATFORM%"
|
||||
-DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%"
|
||||
- cmake --build . --config "%CONFIGURATION%"
|
||||
- cd ..
|
||||
|
||||
test_script:
|
||||
- cd build && ctest --verbose --build-config "%CONFIGURATION%" && cd ..
|
||||
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
|
||||
88
.travis.yml
88
.travis.yml
@@ -1,88 +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: bionic
|
||||
osx_image: xcode12.2
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
- BUILD_TYPE=Debug
|
||||
- BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
# Homebrew's GCC is currently broken on XCode 11.
|
||||
- compiler: gcc
|
||||
os: osx
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
- sourceline: 'ppa:ubuntu-toolchain-r/test'
|
||||
packages:
|
||||
- clang-10
|
||||
- cmake
|
||||
- gcc-10
|
||||
- g++-10
|
||||
- libgoogle-perftools-dev
|
||||
- libkyotocabinet-dev
|
||||
- libsnappy-dev
|
||||
- libsqlite3-dev
|
||||
- ninja-build
|
||||
homebrew:
|
||||
packages:
|
||||
- cmake
|
||||
- crc32c
|
||||
- gcc@10
|
||||
- gperftools
|
||||
- kyoto-cabinet
|
||||
- llvm@10
|
||||
- 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++-10" CC="gcc-10"; fi
|
||||
# /usr/bin/clang points to an older compiler on both Linux and macOS.
|
||||
#
|
||||
# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values
|
||||
# below don't work on macOS. Fortunately, the path change above makes the
|
||||
# default values (clang and clang++) resolve to the correct compiler on macOS.
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
if [ "$CXX" = "clang++" ]; then export CXX="clang++-10" CC="clang-10"; 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
|
||||
-DCMAKE_INSTALL_PREFIX=$HOME/.local
|
||||
- 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"
|
||||
- cd build && cmake --build . --target install
|
||||
112
CMakeLists.txt
112
CMakeLists.txt
@@ -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.
|
||||
|
||||
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.23.0 LANGUAGES C CXX)
|
||||
|
||||
@@ -16,8 +16,8 @@ endif(NOT CMAKE_C_STANDARD)
|
||||
|
||||
# C++ standard can be overridden when this is used as a sub-project.
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
# This project requires C++11.
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
# 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)
|
||||
@@ -40,6 +40,7 @@ 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)
|
||||
@@ -273,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)
|
||||
@@ -298,11 +302,6 @@ if(LEVELDB_BUILD_TESTS)
|
||||
# This project is tested using GoogleTest.
|
||||
add_subdirectory("third_party/googletest")
|
||||
|
||||
# This project uses Google benchmark for benchmarking.
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(BENCHMARK_ENABLE_EXCEPTIONS OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory("third_party/benchmark")
|
||||
|
||||
# GoogleTest triggers a missing field initializers warning.
|
||||
if(LEVELDB_HAVE_NO_MISSING_FIELD_INITIALIZERS)
|
||||
set_property(TARGET gtest
|
||||
@@ -311,6 +310,60 @@ if(LEVELDB_BUILD_TESTS)
|
||||
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)
|
||||
|
||||
@@ -323,7 +376,7 @@ if(LEVELDB_BUILD_TESTS)
|
||||
|
||||
"${test_file}"
|
||||
)
|
||||
target_link_libraries("${test_target_name}" leveldb gmock gtest benchmark)
|
||||
target_link_libraries("${test_target_name}" leveldb gmock gtest)
|
||||
target_compile_definitions("${test_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
@@ -339,42 +392,8 @@ if(LEVELDB_BUILD_TESTS)
|
||||
endfunction(leveldb_test)
|
||||
|
||||
leveldb_test("db/c_test.c")
|
||||
leveldb_test("db/fault_injection_test.cc")
|
||||
|
||||
leveldb_test("issues/issue178_test.cc")
|
||||
leveldb_test("issues/issue200_test.cc")
|
||||
leveldb_test("issues/issue320_test.cc")
|
||||
|
||||
leveldb_test("util/env_test.cc")
|
||||
leveldb_test("util/status_test.cc")
|
||||
leveldb_test("util/no_destructor_test.cc")
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
leveldb_test("db/autocompact_test.cc")
|
||||
leveldb_test("db/corruption_test.cc")
|
||||
leveldb_test("db/db_test.cc")
|
||||
leveldb_test("db/dbformat_test.cc")
|
||||
leveldb_test("db/filename_test.cc")
|
||||
leveldb_test("db/log_test.cc")
|
||||
leveldb_test("db/recovery_test.cc")
|
||||
leveldb_test("db/skiplist_test.cc")
|
||||
leveldb_test("db/version_edit_test.cc")
|
||||
leveldb_test("db/version_set_test.cc")
|
||||
leveldb_test("db/write_batch_test.cc")
|
||||
|
||||
leveldb_test("helpers/memenv/memenv_test.cc")
|
||||
|
||||
leveldb_test("table/filter_block_test.cc")
|
||||
leveldb_test("table/table_test.cc")
|
||||
|
||||
leveldb_test("util/arena_test.cc")
|
||||
leveldb_test("util/bloom_test.cc")
|
||||
leveldb_test("util/cache_test.cc")
|
||||
leveldb_test("util/coding_test.cc")
|
||||
leveldb_test("util/crc32c_test.cc")
|
||||
leveldb_test("util/hash_test.cc")
|
||||
leveldb_test("util/logging_test.cc")
|
||||
|
||||
# TODO(costan): This test also uses
|
||||
# "util/env_{posix|windows}_test_helper.h"
|
||||
if (WIN32)
|
||||
@@ -386,6 +405,11 @@ if(LEVELDB_BUILD_TESTS)
|
||||
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)
|
||||
|
||||
@@ -400,7 +424,7 @@ if(LEVELDB_BUILD_BENCHMARKS)
|
||||
|
||||
"${bench_file}"
|
||||
)
|
||||
target_link_libraries("${bench_target_name}" leveldb gmock gtest)
|
||||
target_link_libraries("${bench_target_name}" leveldb gmock gtest benchmark)
|
||||
target_compile_definitions("${bench_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
|
||||
@@ -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](https://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/).
|
||||
27
README.md
27
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](https://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
|
||||
|
||||
@@ -73,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,
|
||||
@@ -100,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
|
||||
@@ -108,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.
|
||||
|
||||
|
||||
@@ -60,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;
|
||||
@@ -118,9 +120,15 @@ 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 {
|
||||
@@ -364,6 +372,57 @@ struct ThreadState {
|
||||
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 {
|
||||
@@ -576,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")) {
|
||||
@@ -710,50 +773,30 @@ class Benchmark {
|
||||
}
|
||||
|
||||
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];
|
||||
std::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() {
|
||||
@@ -771,6 +814,8 @@ class Benchmark {
|
||||
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()) {
|
||||
std::fprintf(stderr, "open error: %s\n", s.ToString().c_str());
|
||||
@@ -1045,6 +1090,9 @@ int main(int argc, char** argv) {
|
||||
} 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) {
|
||||
|
||||
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();
|
||||
@@ -108,8 +108,3 @@ TEST_F(AutoCompactTest, ReadAll) { DoReads(kCount); }
|
||||
TEST_F(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); }
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -360,8 +360,3 @@ TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -629,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;
|
||||
@@ -641,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) {
|
||||
@@ -1368,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);
|
||||
|
||||
144
db/db_test.cc
144
db/db_test.cc
@@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/version_set.h"
|
||||
@@ -66,6 +65,19 @@ class AtomicCounter {
|
||||
void DelayMilliseconds(int millis) {
|
||||
Env::Default()->SleepForMicroseconds(millis * 1000);
|
||||
}
|
||||
|
||||
bool IsLdbFile(const std::string& f) {
|
||||
return strstr(f.c_str(), ".ldb") != nullptr;
|
||||
}
|
||||
|
||||
bool IsLogFile(const std::string& f) {
|
||||
return strstr(f.c_str(), ".log") != nullptr;
|
||||
}
|
||||
|
||||
bool IsManifestFile(const std::string& f) {
|
||||
return strstr(f.c_str(), "MANIFEST") != nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Test Env to override default Env behavior for testing.
|
||||
@@ -101,6 +113,10 @@ class TestEnv : public EnvWrapper {
|
||||
// Special Env used to delay background operations.
|
||||
class SpecialEnv : public EnvWrapper {
|
||||
public:
|
||||
// For historical reasons, the std::atomic<> fields below are currently
|
||||
// accessed via acquired loads and release stores. We should switch
|
||||
// to plain load(), store() calls that provide sequential consistency.
|
||||
|
||||
// sstable/log Sync() calls are blocked while this pointer is non-null.
|
||||
std::atomic<bool> delay_data_sync_;
|
||||
|
||||
@@ -119,6 +135,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Force write to manifest files to fail while this pointer is non-null.
|
||||
std::atomic<bool> manifest_write_error_;
|
||||
|
||||
// Force log file close to fail while this bool is true.
|
||||
std::atomic<bool> log_file_close_;
|
||||
|
||||
bool count_random_reads_;
|
||||
AtomicCounter random_read_counter_;
|
||||
|
||||
@@ -130,6 +149,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
non_writable_(false),
|
||||
manifest_sync_error_(false),
|
||||
manifest_write_error_(false),
|
||||
log_file_close_(false),
|
||||
count_random_reads_(false) {}
|
||||
|
||||
Status NewWritableFile(const std::string& f, WritableFile** r) {
|
||||
@@ -137,9 +157,12 @@ class SpecialEnv : public EnvWrapper {
|
||||
private:
|
||||
SpecialEnv* const env_;
|
||||
WritableFile* const base_;
|
||||
const std::string fname_;
|
||||
|
||||
public:
|
||||
DataFile(SpecialEnv* env, WritableFile* base) : env_(env), base_(base) {}
|
||||
DataFile(SpecialEnv* env, WritableFile* base, const std::string& fname)
|
||||
: env_(env), base_(base), fname_(fname) {}
|
||||
|
||||
~DataFile() { delete base_; }
|
||||
Status Append(const Slice& data) {
|
||||
if (env_->no_space_.load(std::memory_order_acquire)) {
|
||||
@@ -149,7 +172,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
Status Close() { return base_->Close(); }
|
||||
Status Close() {
|
||||
Status s = base_->Close();
|
||||
if (s.ok() && IsLogFile(fname_) &&
|
||||
env_->log_file_close_.load(std::memory_order_acquire)) {
|
||||
s = Status::IOError("simulated log file Close error");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
Status Flush() { return base_->Flush(); }
|
||||
Status Sync() {
|
||||
if (env_->data_sync_error_.load(std::memory_order_acquire)) {
|
||||
@@ -193,10 +223,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
Status s = target()->NewWritableFile(f, r);
|
||||
if (s.ok()) {
|
||||
if (strstr(f.c_str(), ".ldb") != nullptr ||
|
||||
strstr(f.c_str(), ".log") != nullptr) {
|
||||
*r = new DataFile(this, *r);
|
||||
} else if (strstr(f.c_str(), "MANIFEST") != nullptr) {
|
||||
if (IsLdbFile(f) || IsLogFile(f)) {
|
||||
*r = new DataFile(this, *r, f);
|
||||
} else if (IsManifestFile(f)) {
|
||||
*r = new ManifestFile(this, *r);
|
||||
}
|
||||
}
|
||||
@@ -1693,8 +1722,14 @@ TEST_F(DBTest, DestroyEmptyDir) {
|
||||
ASSERT_TRUE(env.FileExists(dbname));
|
||||
std::vector<std::string> children;
|
||||
ASSERT_LEVELDB_OK(env.GetChildren(dbname, &children));
|
||||
#if defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
// TODO(https://crbug.com/1428746): Chromium's file system abstraction always
|
||||
// filters out '.' and '..'.
|
||||
ASSERT_EQ(0, children.size());
|
||||
#else
|
||||
// The stock Env's do not filter out '.' and '..' special files.
|
||||
ASSERT_EQ(2, children.size());
|
||||
#endif // defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
ASSERT_LEVELDB_OK(DestroyDB(dbname, opts));
|
||||
ASSERT_TRUE(!env.FileExists(dbname));
|
||||
|
||||
@@ -1942,6 +1977,33 @@ TEST_F(DBTest, BloomFilter) {
|
||||
delete options.filter_policy;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, LogCloseError) {
|
||||
// Regression test for bug where we could ignore log file
|
||||
// Close() error when switching to a new log file.
|
||||
const int kValueSize = 20000;
|
||||
const int kWriteCount = 10;
|
||||
const int kWriteBufferSize = (kValueSize * kWriteCount) / 2;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_size = kWriteBufferSize; // Small write buffer
|
||||
Reopen(&options);
|
||||
env_->log_file_close_.store(true, std::memory_order_release);
|
||||
|
||||
std::string value(kValueSize, 'x');
|
||||
Status s;
|
||||
for (int i = 0; i < kWriteCount && s.ok(); i++) {
|
||||
s = Put(Key(i), value);
|
||||
}
|
||||
ASSERT_TRUE(!s.ok()) << "succeeded even after log file Close failure";
|
||||
|
||||
// Future writes should also fail after an earlier error.
|
||||
s = Put("hello", "world");
|
||||
ASSERT_TRUE(!s.ok()) << "write succeeded after log file Close failure";
|
||||
|
||||
env_->log_file_close_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Multi-threaded test:
|
||||
namespace {
|
||||
|
||||
@@ -2295,72 +2357,4 @@ TEST_F(DBTest, Randomized) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
std::string MakeKey(unsigned int num) {
|
||||
char buf[30];
|
||||
std::snprintf(buf, sizeof(buf), "%016u", num);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static void BM_LogAndApply(benchmark::State& state) {
|
||||
const int num_base_files = state.range(0);
|
||||
|
||||
std::string dbname = testing::TempDir() + "leveldb_test_benchmark";
|
||||
DestroyDB(dbname, Options());
|
||||
|
||||
DB* db = nullptr;
|
||||
Options opts;
|
||||
opts.create_if_missing = true;
|
||||
Status s = DB::Open(opts, dbname, &db);
|
||||
ASSERT_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 leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -126,8 +126,3 @@ TEST(FormatTest, InternalKeyDebugString) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -548,8 +548,3 @@ TEST_F(FaultInjectionTest, FaultTestWithLogReuse) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -125,8 +125,3 @@ TEST(FileNameTest, Construction) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -556,8 +556,3 @@ TEST_F(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); }
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -78,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();
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace leveldb {
|
||||
class RecoveryTest : public testing::Test {
|
||||
public:
|
||||
RecoveryTest() : env_(Env::Default()), db_(nullptr) {
|
||||
dbname_ = testing::TempDir() + "/recovery_test";
|
||||
dbname_ = testing::TempDir() + "recovery_test";
|
||||
DestroyDB(dbname_, Options());
|
||||
Open();
|
||||
}
|
||||
@@ -328,12 +328,12 @@ TEST_F(RecoveryTest, ManifestMissing) {
|
||||
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) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -366,8 +366,3 @@ TEST(SkipTest, Concurrent4) { RunConcurrent(4); }
|
||||
TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -22,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();
|
||||
}
|
||||
|
||||
@@ -39,8 +39,3 @@ TEST(VersionEditTest, EncodeDecode) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -626,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;
|
||||
@@ -806,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_);
|
||||
@@ -1304,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,
|
||||
@@ -1392,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;
|
||||
@@ -1414,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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -329,8 +329,3 @@ TEST_F(AddBoundaryInputsTest, TestDisjoinFilePointers) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -130,8 +130,3 @@ TEST(WriteBatchTest, ApproximateSize) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -90,9 +90,9 @@ div.bsql {
|
||||
<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="https://github.com/google/leveldb/blob/master/benchmarks/db_bench.cc">benchmarks/db_bench.cc</a>.</li>
|
||||
<li> <b>SQLite:</b> <a href="https://github.com/google/leveldb/blob/master/benchmarks/db_bench_sqlite3.cc">benchmarks/db_bench_sqlite3.cc</a>.</li>
|
||||
<li> <b>Kyoto TreeDB:</b> <a href="https://github.com/google/leveldb/blob/master/benchmarks/db_bench_tree_db.cc">benchmarks/db_bench_tree_db.cc</a>.</li>
|
||||
<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>
|
||||
|
||||
11
doc/index.md
11
doc/index.md
@@ -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.data(), n, dst);
|
||||
builtin_policy_->CreateFilter(trimmed.data(), n, dst);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
@@ -257,8 +257,3 @@ TEST_F(MemEnvTest, DBTest) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -51,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 {
|
||||
|
||||
@@ -83,8 +83,3 @@ TEST(Issue178, Test) {
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -52,8 +52,3 @@ TEST(Issue200, Test) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -124,8 +124,3 @@ TEST(Issue320, Test) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -30,4 +30,9 @@
|
||||
#cmakedefine01 HAVE_SNAPPY
|
||||
#endif // !defined(HAVE_SNAPPY)
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
// 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_
|
||||
|
||||
@@ -55,7 +55,7 @@ class CondVar {
|
||||
void Signal();
|
||||
|
||||
// Wake up all waiting threads.
|
||||
void SignallAll();
|
||||
void SignalAll();
|
||||
};
|
||||
|
||||
// ------------------ Compression -------------------
|
||||
@@ -72,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
|
||||
@@ -81,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
|
||||
@@ -126,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;
|
||||
|
||||
@@ -120,8 +120,3 @@ TEST_F(FilterBlockTest, MultiChunk) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#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"
|
||||
@@ -784,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()) {
|
||||
std::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);
|
||||
@@ -807,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.
|
||||
@@ -827,8 +840,3 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
2
third_party/benchmark
vendored
2
third_party/benchmark
vendored
Submodule third_party/benchmark updated: bf585a2789...1a54956777
2
third_party/googletest
vendored
2
third_party/googletest
vendored
Submodule third_party/googletest updated: c27acebba3...a35bc7693c
@@ -59,8 +59,3 @@ TEST(ArenaTest, Simple) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -152,8 +152,3 @@ TEST_F(BloomTest, VaryingLengths) {
|
||||
// Different bits-per-byte
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -54,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);
|
||||
|
||||
|
||||
@@ -222,8 +222,3 @@ TEST_F(CacheTest, ZeroSizeCache) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -142,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) {
|
||||
|
||||
@@ -191,8 +191,3 @@ TEST(Coding, Strings) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -54,8 +54,3 @@ TEST(CRC, Mask) {
|
||||
|
||||
} // namespace crc32c
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#ifndef __Fuchsia__
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
@@ -72,7 +73,14 @@ Status PosixError(const std::string& context, int error_number) {
|
||||
class Limiter {
|
||||
public:
|
||||
// Limit maximum number of resources to |max_acquires|.
|
||||
Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
|
||||
Limiter(int max_acquires)
|
||||
:
|
||||
#if !defined(NDEBUG)
|
||||
max_acquires_(max_acquires),
|
||||
#endif // !defined(NDEBUG)
|
||||
acquires_allowed_(max_acquires) {
|
||||
assert(max_acquires >= 0);
|
||||
}
|
||||
|
||||
Limiter(const Limiter&) = delete;
|
||||
Limiter operator=(const Limiter&) = delete;
|
||||
@@ -85,15 +93,35 @@ class Limiter {
|
||||
|
||||
if (old_acquires_allowed > 0) return true;
|
||||
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
int pre_increment_acquires_allowed =
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Silence compiler warnings about unused arguments when NDEBUG is defined.
|
||||
(void)pre_increment_acquires_allowed;
|
||||
// If the check below fails, Release() was called more times than acquire.
|
||||
assert(pre_increment_acquires_allowed < max_acquires_);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Release a resource acquired by a previous call to Acquire() that returned
|
||||
// true.
|
||||
void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
|
||||
void Release() {
|
||||
int old_acquires_allowed =
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Silence compiler warnings about unused arguments when NDEBUG is defined.
|
||||
(void)old_acquires_allowed;
|
||||
// If the check below fails, Release() was called more times than acquire.
|
||||
assert(old_acquires_allowed < max_acquires_);
|
||||
}
|
||||
|
||||
private:
|
||||
#if !defined(NDEBUG)
|
||||
// Catches an excessive number of Release() calls.
|
||||
const int max_acquires_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
// The number of available resources.
|
||||
//
|
||||
// This is a counter and is not tied to the invariants of any other class, so
|
||||
@@ -108,7 +136,7 @@ class Limiter {
|
||||
class PosixSequentialFile final : public SequentialFile {
|
||||
public:
|
||||
PosixSequentialFile(std::string filename, int fd)
|
||||
: fd_(fd), filename_(filename) {}
|
||||
: fd_(fd), filename_(std::move(filename)) {}
|
||||
~PosixSequentialFile() override { close(fd_); }
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
@@ -214,7 +242,7 @@ class PosixMmapReadableFile final : public RandomAccessFile {
|
||||
// over the ownership of the region.
|
||||
//
|
||||
// |mmap_limiter| must outlive this instance. The caller must have already
|
||||
// aquired the right to use one mmap region, which will be released when this
|
||||
// acquired the right to use one mmap region, which will be released when this
|
||||
// instance is destroyed.
|
||||
PosixMmapReadableFile(std::string filename, char* mmap_base, size_t length,
|
||||
Limiter* mmap_limiter)
|
||||
@@ -728,7 +756,7 @@ class PosixEnv : public Env {
|
||||
// Instances are constructed on the thread calling Schedule() and used on the
|
||||
// background thread.
|
||||
//
|
||||
// This structure is thread-safe beacuse it is immutable.
|
||||
// This structure is thread-safe because it is immutable.
|
||||
struct BackgroundWorkItem {
|
||||
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
|
||||
: function(function), arg(arg) {}
|
||||
@@ -757,6 +785,10 @@ int MaxOpenFiles() {
|
||||
if (g_open_read_only_file_limit >= 0) {
|
||||
return g_open_read_only_file_limit;
|
||||
}
|
||||
#ifdef __Fuchsia__
|
||||
// Fuchsia doesn't implement getrlimit.
|
||||
g_open_read_only_file_limit = 50;
|
||||
#else
|
||||
struct ::rlimit rlim;
|
||||
if (::getrlimit(RLIMIT_NOFILE, &rlim)) {
|
||||
// getrlimit failed, fallback to hard-coded default.
|
||||
@@ -767,6 +799,7 @@ int MaxOpenFiles() {
|
||||
// Allow use of 20% of available file descriptors for read-only files.
|
||||
g_open_read_only_file_limit = rlim.rlim_cur / 5;
|
||||
}
|
||||
#endif
|
||||
return g_open_read_only_file_limit;
|
||||
}
|
||||
|
||||
@@ -837,13 +870,17 @@ class SingletonEnv {
|
||||
public:
|
||||
SingletonEnv() {
|
||||
#if !defined(NDEBUG)
|
||||
env_initialized_.store(true, std::memory_order::memory_order_relaxed);
|
||||
env_initialized_.store(true, std::memory_order_relaxed);
|
||||
#endif // !defined(NDEBUG)
|
||||
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
|
||||
"env_storage_ will not fit the Env");
|
||||
static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType),
|
||||
static_assert(std::is_standard_layout_v<SingletonEnv<EnvType>>);
|
||||
static_assert(
|
||||
offsetof(SingletonEnv<EnvType>, env_storage_) % alignof(EnvType) == 0,
|
||||
"env_storage_ does not meet the Env's alignment needs");
|
||||
static_assert(alignof(SingletonEnv<EnvType>) % alignof(EnvType) == 0,
|
||||
"env_storage_ does not meet the Env's alignment needs");
|
||||
new (&env_storage_) EnvType();
|
||||
new (env_storage_) EnvType();
|
||||
}
|
||||
~SingletonEnv() = default;
|
||||
|
||||
@@ -854,13 +891,12 @@ class SingletonEnv {
|
||||
|
||||
static void AssertEnvNotInitialized() {
|
||||
#if !defined(NDEBUG)
|
||||
assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
|
||||
assert(!env_initialized_.load(std::memory_order_relaxed));
|
||||
#endif // !defined(NDEBUG)
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
|
||||
env_storage_;
|
||||
alignas(EnvType) char env_storage_[sizeof(EnvType)];
|
||||
#if !defined(NDEBUG)
|
||||
static std::atomic<bool> env_initialized_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
@@ -243,8 +243,8 @@ TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
|
||||
// Exhaust the RandomAccessFile mmap limit. This way, the test
|
||||
// RandomAccessFile instance below is backed by a file descriptor, not by an
|
||||
// mmap region.
|
||||
leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr};
|
||||
for (int i = 0; i < kReadOnlyFileLimit; i++) {
|
||||
leveldb::RandomAccessFile* mmapped_files[kMMapLimit];
|
||||
for (int i = 0; i < kMMapLimit; i++) {
|
||||
ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
|
||||
CheckCloseOnExecDoesNotLeakFDs(open_fds);
|
||||
delete file;
|
||||
|
||||
for (int i = 0; i < kReadOnlyFileLimit; i++) {
|
||||
for (int i = 0; i < kMMapLimit; i++) {
|
||||
delete mmapped_files[i];
|
||||
}
|
||||
ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
static const int kDelayMicros = 100000;
|
||||
|
||||
class EnvTest : public testing::Test {
|
||||
public:
|
||||
EnvTest() : env_(Env::Default()) {}
|
||||
@@ -98,40 +96,45 @@ TEST_F(EnvTest, RunMany) {
|
||||
struct RunState {
|
||||
port::Mutex mu;
|
||||
port::CondVar cvar{&mu};
|
||||
int last_id = 0;
|
||||
int run_count = 0;
|
||||
};
|
||||
|
||||
struct Callback {
|
||||
RunState* state_; // Pointer to shared state.
|
||||
const int id_; // Order# for the execution of this callback.
|
||||
RunState* const state_; // Pointer to shared state.
|
||||
bool run = false;
|
||||
|
||||
Callback(RunState* s, int id) : state_(s), id_(id) {}
|
||||
Callback(RunState* s) : state_(s) {}
|
||||
|
||||
static void Run(void* arg) {
|
||||
Callback* callback = reinterpret_cast<Callback*>(arg);
|
||||
RunState* state = callback->state_;
|
||||
|
||||
MutexLock l(&state->mu);
|
||||
ASSERT_EQ(state->last_id, callback->id_ - 1);
|
||||
state->last_id = callback->id_;
|
||||
state->run_count++;
|
||||
callback->run = true;
|
||||
state->cvar.Signal();
|
||||
}
|
||||
};
|
||||
|
||||
RunState state;
|
||||
Callback callback1(&state, 1);
|
||||
Callback callback2(&state, 2);
|
||||
Callback callback3(&state, 3);
|
||||
Callback callback4(&state, 4);
|
||||
Callback callback1(&state);
|
||||
Callback callback2(&state);
|
||||
Callback callback3(&state);
|
||||
Callback callback4(&state);
|
||||
env_->Schedule(&Callback::Run, &callback1);
|
||||
env_->Schedule(&Callback::Run, &callback2);
|
||||
env_->Schedule(&Callback::Run, &callback3);
|
||||
env_->Schedule(&Callback::Run, &callback4);
|
||||
|
||||
MutexLock l(&state.mu);
|
||||
while (state.last_id != 4) {
|
||||
while (state.run_count != 4) {
|
||||
state.cvar.Wait();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(callback1.run);
|
||||
ASSERT_TRUE(callback2.run);
|
||||
ASSERT_TRUE(callback3.run);
|
||||
ASSERT_TRUE(callback4.run);
|
||||
}
|
||||
|
||||
struct State {
|
||||
@@ -177,11 +180,21 @@ TEST_F(EnvTest, TestOpenNonExistentFile) {
|
||||
RandomAccessFile* random_access_file;
|
||||
Status status =
|
||||
env_->NewRandomAccessFile(non_existent_file, &random_access_file);
|
||||
#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.IsNotFound());
|
||||
#endif // defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
|
||||
SequentialFile* sequential_file;
|
||||
status = env_->NewSequentialFile(non_existent_file, &sequential_file);
|
||||
#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.IsNotFound());
|
||||
#endif // defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
}
|
||||
|
||||
TEST_F(EnvTest, ReopenWritableFile) {
|
||||
@@ -233,8 +246,3 @@ TEST_F(EnvTest, ReopenAppendableFile) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -114,7 +114,14 @@ class ScopedHandle {
|
||||
class Limiter {
|
||||
public:
|
||||
// Limit maximum number of resources to |max_acquires|.
|
||||
Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
|
||||
Limiter(int max_acquires)
|
||||
:
|
||||
#if !defined(NDEBUG)
|
||||
max_acquires_(max_acquires),
|
||||
#endif // !defined(NDEBUG)
|
||||
acquires_allowed_(max_acquires) {
|
||||
assert(max_acquires >= 0);
|
||||
}
|
||||
|
||||
Limiter(const Limiter&) = delete;
|
||||
Limiter operator=(const Limiter&) = delete;
|
||||
@@ -133,9 +140,22 @@ class Limiter {
|
||||
|
||||
// Release a resource acquired by a previous call to Acquire() that returned
|
||||
// true.
|
||||
void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
|
||||
void Release() {
|
||||
int old_acquires_allowed =
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Silence compiler warnings about unused arguments when NDEBUG is defined.
|
||||
(void)old_acquires_allowed;
|
||||
// If the check below fails, Release() was called more times than acquire.
|
||||
assert(old_acquires_allowed < max_acquires_);
|
||||
}
|
||||
|
||||
private:
|
||||
#if !defined(NDEBUG)
|
||||
// Catches an excessive number of Release() calls.
|
||||
const int max_acquires_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
// The number of available resources.
|
||||
//
|
||||
// This is a counter and is not tied to the invariants of any other class, so
|
||||
@@ -622,7 +642,7 @@ class WindowsEnv : public Env {
|
||||
}
|
||||
|
||||
Status NewLogger(const std::string& filename, Logger** result) override {
|
||||
std::FILE* fp = std::fopen(filename.c_str(), "w");
|
||||
std::FILE* fp = std::fopen(filename.c_str(), "wN");
|
||||
if (fp == nullptr) {
|
||||
*result = nullptr;
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
@@ -661,7 +681,7 @@ class WindowsEnv : public Env {
|
||||
// Instances are constructed on the thread calling Schedule() and used on the
|
||||
// background thread.
|
||||
//
|
||||
// This structure is thread-safe beacuse it is immutable.
|
||||
// This structure is thread-safe because it is immutable.
|
||||
struct BackgroundWorkItem {
|
||||
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
|
||||
: function(function), arg(arg) {}
|
||||
@@ -745,13 +765,17 @@ class SingletonEnv {
|
||||
public:
|
||||
SingletonEnv() {
|
||||
#if !defined(NDEBUG)
|
||||
env_initialized_.store(true, std::memory_order::memory_order_relaxed);
|
||||
env_initialized_.store(true, std::memory_order_relaxed);
|
||||
#endif // !defined(NDEBUG)
|
||||
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
|
||||
"env_storage_ will not fit the Env");
|
||||
static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType),
|
||||
static_assert(std::is_standard_layout_v<SingletonEnv<EnvType>>);
|
||||
static_assert(
|
||||
offsetof(SingletonEnv<EnvType>, env_storage_) % alignof(EnvType) == 0,
|
||||
"env_storage_ does not meet the Env's alignment needs");
|
||||
static_assert(alignof(SingletonEnv<EnvType>) % alignof(EnvType) == 0,
|
||||
"env_storage_ does not meet the Env's alignment needs");
|
||||
new (&env_storage_) EnvType();
|
||||
new (env_storage_) EnvType();
|
||||
}
|
||||
~SingletonEnv() = default;
|
||||
|
||||
@@ -762,13 +786,12 @@ class SingletonEnv {
|
||||
|
||||
static void AssertEnvNotInitialized() {
|
||||
#if !defined(NDEBUG)
|
||||
assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
|
||||
assert(!env_initialized_.load(std::memory_order_relaxed));
|
||||
#endif // !defined(NDEBUG)
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
|
||||
env_storage_;
|
||||
alignas(EnvType) char env_storage_[sizeof(EnvType)];
|
||||
#if !defined(NDEBUG)
|
||||
static std::atomic<bool> env_initialized_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
@@ -27,7 +27,7 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) {
|
||||
uint32_t h = seed ^ (n * m);
|
||||
|
||||
// Pick up four bytes at a time
|
||||
while (data + 4 <= limit) {
|
||||
while (limit - data >= 4) {
|
||||
uint32_t w = DecodeFixed32(data);
|
||||
data += 4;
|
||||
h += w;
|
||||
|
||||
@@ -39,8 +39,3 @@ TEST(HASH, SignedUnsignedIssue) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -138,8 +138,3 @@ TEST(Logging, ConsumeDecimalNumberNoDigits) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_
|
||||
#define STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -20,10 +21,14 @@ class NoDestructor {
|
||||
explicit NoDestructor(ConstructorArgTypes&&... constructor_args) {
|
||||
static_assert(sizeof(instance_storage_) >= sizeof(InstanceType),
|
||||
"instance_storage_ is not large enough to hold the instance");
|
||||
static_assert(std::is_standard_layout_v<NoDestructor<InstanceType>>);
|
||||
static_assert(
|
||||
alignof(decltype(instance_storage_)) >= alignof(InstanceType),
|
||||
offsetof(NoDestructor, instance_storage_) % alignof(InstanceType) == 0,
|
||||
"instance_storage_ does not meet the instance's alignment requirement");
|
||||
new (&instance_storage_)
|
||||
static_assert(
|
||||
alignof(NoDestructor<InstanceType>) % alignof(InstanceType) == 0,
|
||||
"instance_storage_ does not meet the instance's alignment requirement");
|
||||
new (instance_storage_)
|
||||
InstanceType(std::forward<ConstructorArgTypes>(constructor_args)...);
|
||||
}
|
||||
|
||||
@@ -37,8 +42,7 @@ class NoDestructor {
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(InstanceType),
|
||||
alignof(InstanceType)>::type instance_storage_;
|
||||
alignas(InstanceType) char instance_storage_[sizeof(InstanceType)];
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
@@ -42,8 +42,3 @@ TEST(NoDestructorTest, StaticInstance) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,3 @@ TEST(Status, MoveConstructor) {
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user