mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03:00
Merge #17398: build: Update leveldb to 1.22+
677fb8e923
test: Add ubsan surpression for crc32c (Wladimir J. van der Laan)8e68bb1dde
build: Disable msvc warning 4722 for leveldb build (Aaron Clauson)be23949765
build: MSVC changes for leveldb update (Aaron Clauson)9ebdf04757
build: CRC32C build system integration (Wladimir J. van der Laan)402252a808
build: Add LCOV exception for crc32c (Wladimir J. van der Laan)3a037d0067
test: Add crc32c exception to various linters and generation scripts (Wladimir J. van der Laan)84ff1b2076
test: Add crc32c to subtree check linter (Wladimir J. van der Laan)7cf13a5134
doc: Add crc32c subtree to developer notes (Wladimir J. van der Laan)24d02a9ac0
build: Update build system for new leveldb (Wladimir J. van der Laan)2e1819311a
Squashed 'src/crc32c/' content from commit 224988680f7673cd7c769963d4035cb315aa3388 (Wladimir J. van der Laan)66480821b3
Squashed 'src/leveldb/' changes from f545dfabff4c2e9836efed094dba99a34fbc6b88..f8ae182c1e5176d12e816fb2217ae33a5472fdd7 (Wladimir J. van der Laan) Pull request description: This updates leveldb to currently newest upstream commit0c40829872
: - CRC32C hardware acceleration is now an external library [crc32c](https://github.com/google/crc32c). This adds acceleration on ARM, and should be faster on x86 because of using prefetch. It also makes it easy to support similar instruction sets on other platforms in the future. - Thread handling uses C++11, instead of platform specific code. - Native windows environment was added. No need to maintain our own hacky one, anymore. - Upstream now builds using CMake. This doesn't mean we need to use that (phew), but internal configuration changed to a a series of checks, instead of OS profiles. This means the blanket error "Cannot build leveldb for $host. Please file a bug report' is removed. All changes:a53934a3ae...0c40829872
Pretty much all our changes have been subsumed by upstream, so we figured it was cleaner to start over with a new branch from upstream with the still-relevant patches applied: https://github.com/bitcoin-core/leveldb/tree/bitcoin-fork-new There's quite some testing to be done (see below). See https://github.com/bitcoin-core/leveldb/issues/25 and https://github.com/bitcoin-core/leveldb/pull/26 for more history and context. TODO: - [x] Subtree `crc32c` - [x] Make linters happy about crc32 subtree - [x] Integrate `crc32c` library into build system - [x] MSVC build system ACKs for top commit: sipa: ACK677fb8e923
Tree-SHA512: 37ee92a750e053e924bc4626b12bb3fd81faa9f8c5ebaa343931fee810c45ba05aa6051fdea82535fa351bf2be7297801b98af9469865fc5ead771650a5d6240
This commit is contained in:
commit
22d11187ee
212 changed files with 11577 additions and 7994 deletions
|
@ -194,6 +194,7 @@ LCOV_FILTER_PATTERN = \
|
|||
-p "/usr/lib/" \
|
||||
-p "/usr/lib64/" \
|
||||
-p "src/leveldb/" \
|
||||
-p "src/crc32c/" \
|
||||
-p "src/bench/" \
|
||||
-p "src/univalue" \
|
||||
-p "src/crypto/ctaes" \
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
<ClCompile Include="..\..\src\leveldb\db\version_set.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\db\write_batch.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\helpers\memenv\memenv.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\port\port_posix_sse.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\port\port_win.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\table\block.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\table\block_builder.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\table\filter_block.cc" />
|
||||
|
@ -42,7 +40,7 @@
|
|||
<ClCompile Include="..\..\src\leveldb\util\comparator.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\crc32c.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\env.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\env_win.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\env_windows.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\filter_policy.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\hash.cc" />
|
||||
<ClCompile Include="..\..\src\leveldb\util\histogram.cc" />
|
||||
|
@ -51,11 +49,11 @@
|
|||
<ClCompile Include="..\..\src\leveldb\util\status.cc" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4244;4267;4312;</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>..\..\src\leveldb;..\..\src\leveldb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CRC32C=0;HAVE_SNAPPY=0;__STDC_LIMIT_MACROS;LEVELDB_IS_BIG_ENDIAN=0;_UNICODE;UNICODE;_CRT_NONSTDC_NO_DEPRECATE;LEVELDB_PLATFORM_WINDOWS;LEVELDB_ATOMIC_PRESENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4244;4267;4312;4722;</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>..\..\src\leveldb;..\..\src\leveldb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
|
|
@ -14,6 +14,7 @@ test/lint/git-subtree-check.sh src/crypto/ctaes
|
|||
test/lint/git-subtree-check.sh src/secp256k1
|
||||
test/lint/git-subtree-check.sh src/univalue
|
||||
test/lint/git-subtree-check.sh src/leveldb
|
||||
test/lint/git-subtree-check.sh src/crc32c
|
||||
test/lint/check-doc.py
|
||||
test/lint/check-rpc-mappings.py .
|
||||
test/lint/lint-all.sh
|
||||
|
|
133
configure.ac
133
configure.ac
|
@ -355,7 +355,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
|
|||
AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-implicit-fallthrough"],,[[$CXXFLAG_WERROR]])
|
||||
fi
|
||||
|
||||
enable_hwcrc32=no
|
||||
enable_sse42=no
|
||||
enable_sse41=no
|
||||
enable_avx2=no
|
||||
enable_shani=no
|
||||
|
@ -365,6 +365,8 @@ if test "x$use_asm" = "xyes"; then
|
|||
dnl Check for optional instruction set support. Enabling these does _not_ imply that all code will
|
||||
dnl be compiled with them, rather that specific objects/libs may use them after checking for runtime
|
||||
dnl compatibility.
|
||||
|
||||
dnl x86
|
||||
AX_CHECK_COMPILE_FLAG([-msse4.2],[[SSE42_CXXFLAGS="-msse4.2"]],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-msse4.1],[[SSE41_CXXFLAGS="-msse4.1"]],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-mavx -mavx2],[[AVX2_CXXFLAGS="-mavx -mavx2"]],,[[$CXXFLAG_WERROR]])
|
||||
|
@ -372,7 +374,7 @@ AX_CHECK_COMPILE_FLAG([-msse4 -msha],[[SHANI_CXXFLAGS="-msse4 -msha"]],,[[$CXXFL
|
|||
|
||||
TEMP_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $SSE42_CXXFLAGS"
|
||||
AC_MSG_CHECKING(for assembler crc32 support)
|
||||
AC_MSG_CHECKING(for SSE4.2 intrinsics)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <stdint.h>
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -387,7 +389,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
|||
l = _mm_crc32_u64(l, 0);
|
||||
return l;
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); enable_hwcrc32=yes],
|
||||
[ AC_MSG_RESULT(yes); enable_sse42=yes],
|
||||
[ AC_MSG_RESULT(no)]
|
||||
)
|
||||
CXXFLAGS="$TEMP_CXXFLAGS"
|
||||
|
@ -439,6 +441,24 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
|||
)
|
||||
CXXFLAGS="$TEMP_CXXFLAGS"
|
||||
|
||||
# ARM
|
||||
AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc+crypto],[[ARM_CRC_CXXFLAGS="-march=armv8-a+crc+crypto"]],,[[$CXXFLAG_WERROR]])
|
||||
|
||||
TEMP_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $ARM_CRC_CXXFLAGS"
|
||||
AC_MSG_CHECKING(for ARM CRC32 intrinsics)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <arm_acle.h>
|
||||
#include <arm_neon.h>
|
||||
]],[[
|
||||
__crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0);
|
||||
vmull_p64(0, 0);
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); enable_arm_crc=yes; ],
|
||||
[ AC_MSG_RESULT(no)]
|
||||
)
|
||||
CXXFLAGS="$TEMP_CXXFLAGS"
|
||||
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS"
|
||||
|
@ -517,7 +537,6 @@ case $host in
|
|||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0601"
|
||||
LEVELDB_TARGET_FLAGS="-DOS_WINDOWS"
|
||||
if test "x$CXXFLAGS_overridden" = "xno"; then
|
||||
CXXFLAGS="$CXXFLAGS -w"
|
||||
fi
|
||||
|
@ -533,7 +552,6 @@ case $host in
|
|||
;;
|
||||
*darwin*)
|
||||
TARGET_OS=darwin
|
||||
LEVELDB_TARGET_FLAGS="-DOS_MACOSX"
|
||||
if test x$cross_compiling != xyes; then
|
||||
BUILD_OS=darwin
|
||||
AC_PATH_PROGS([RSVG_CONVERT], [rsvg-convert rsvg],rsvg-convert)
|
||||
|
@ -585,35 +603,9 @@ case $host in
|
|||
*android*)
|
||||
dnl make sure android stays above linux for hosts like *linux-android*
|
||||
TARGET_OS=android
|
||||
LEVELDB_TARGET_FLAGS="-DOS_ANDROID"
|
||||
;;
|
||||
*linux*)
|
||||
TARGET_OS=linux
|
||||
LEVELDB_TARGET_FLAGS="-DOS_LINUX"
|
||||
;;
|
||||
*kfreebsd*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_KFREEBSD"
|
||||
;;
|
||||
*freebsd*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_FREEBSD"
|
||||
;;
|
||||
*openbsd*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_OPENBSD"
|
||||
;;
|
||||
*netbsd*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_NETBSD"
|
||||
;;
|
||||
*dragonfly*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_DRAGONFLYBSD"
|
||||
;;
|
||||
*solaris*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_SOLARIS"
|
||||
;;
|
||||
*hpux*)
|
||||
LEVELDB_TARGET_FLAGS="-DOS_HPUX"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(Cannot build leveldb for $host. Please file a bug report.)
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -960,6 +952,72 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdint.h>
|
|||
[ AC_MSG_RESULT(no)]
|
||||
)
|
||||
|
||||
dnl LevelDB platform checks
|
||||
AC_MSG_CHECKING(for fdatasync)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>]],
|
||||
[[ fdatasync(0); ]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_FDATASYNC=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_FDATASYNC=0 ]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for F_FULLFSYNC)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>]],
|
||||
[[ fcntl(0, F_FULLFSYNC, 0); ]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_FULLFSYNC=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_FULLFSYNC=0 ]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for O_CLOEXEC)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>]],
|
||||
[[ open("", O_CLOEXEC); ]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_O_CLOEXEC=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_O_CLOEXEC=0 ]
|
||||
)
|
||||
|
||||
dnl crc32c platform checks
|
||||
AC_MSG_CHECKING(for __builtin_prefetch)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[
|
||||
char data = 0;
|
||||
const char* address = &data;
|
||||
__builtin_prefetch(address, 0, 0);
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_BUILTIN_PREFETCH=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_BUILTIN_PREFETCH=0 ]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for _mm_prefetch)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <xmmintrin.h>]], [[
|
||||
char data = 0;
|
||||
const char* address = &data;
|
||||
_mm_prefetch(address, _MM_HINT_NTA);
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_MM_PREFETCH=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_MM_PREFETCH=0 ]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for strong getauxval support in the system headers)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <arm_acle.h>
|
||||
#include <arm_neon.h>
|
||||
#include <sys/auxv.h>
|
||||
]], [[
|
||||
getauxval(AT_HWCAP);
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_STRONG_GETAUXVAL=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_STRONG_GETAUXVAL=0 ]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for weak getauxval support in the compiler)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
unsigned long getauxval(unsigned long type) __attribute__((weak));
|
||||
#define AT_HWCAP 16
|
||||
]], [[
|
||||
getauxval(AT_HWCAP);
|
||||
]])],
|
||||
[ AC_MSG_RESULT(yes); HAVE_WEAK_GETAUXVAL=1 ],
|
||||
[ AC_MSG_RESULT(no); HAVE_WEAK_GETAUXVAL=0 ]
|
||||
)
|
||||
|
||||
dnl Check for reduced exports
|
||||
if test x$use_reduce_exports = xyes; then
|
||||
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"],
|
||||
|
@ -1515,11 +1573,13 @@ AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes])
|
|||
AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes])
|
||||
AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes])
|
||||
AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes])
|
||||
AM_CONDITIONAL([ENABLE_HWCRC32],[test x$enable_hwcrc32 = xyes])
|
||||
AM_CONDITIONAL([ENABLE_SSE42],[test x$enable_sse42 = xyes])
|
||||
AM_CONDITIONAL([ENABLE_SSE41],[test x$enable_sse41 = xyes])
|
||||
AM_CONDITIONAL([ENABLE_AVX2],[test x$enable_avx2 = xyes])
|
||||
AM_CONDITIONAL([ENABLE_SHANI],[test x$enable_shani = xyes])
|
||||
AM_CONDITIONAL([ENABLE_ARM_CRC],[test x$enable_arm_crc = xyes])
|
||||
AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes])
|
||||
AM_CONDITIONAL([WORDS_BIGENDIAN],[test x$ac_cv_c_bigendian = xyes])
|
||||
|
||||
AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version])
|
||||
AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version])
|
||||
|
@ -1566,18 +1626,25 @@ AC_SUBST(SSE42_CXXFLAGS)
|
|||
AC_SUBST(SSE41_CXXFLAGS)
|
||||
AC_SUBST(AVX2_CXXFLAGS)
|
||||
AC_SUBST(SHANI_CXXFLAGS)
|
||||
AC_SUBST(ARM_CRC_CXXFLAGS)
|
||||
AC_SUBST(LIBTOOL_APP_LDFLAGS)
|
||||
AC_SUBST(USE_UPNP)
|
||||
AC_SUBST(USE_QRCODE)
|
||||
AC_SUBST(BOOST_LIBS)
|
||||
AC_SUBST(TESTDEFS)
|
||||
AC_SUBST(LEVELDB_TARGET_FLAGS)
|
||||
AC_SUBST(MINIUPNPC_CPPFLAGS)
|
||||
AC_SUBST(MINIUPNPC_LIBS)
|
||||
AC_SUBST(EVENT_LIBS)
|
||||
AC_SUBST(EVENT_PTHREADS_LIBS)
|
||||
AC_SUBST(ZMQ_LIBS)
|
||||
AC_SUBST(QR_LIBS)
|
||||
AC_SUBST(HAVE_FDATASYNC)
|
||||
AC_SUBST(HAVE_FULLFSYNC)
|
||||
AC_SUBST(HAVE_O_CLOEXEC)
|
||||
AC_SUBST(HAVE_BUILTIN_PREFETCH)
|
||||
AC_SUBST(HAVE_MM_PREFETCH)
|
||||
AC_SUBST(HAVE_STRONG_GETAUXVAL)
|
||||
AC_SUBST(HAVE_WEAK_GETAUXVAL)
|
||||
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
|
||||
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
|
||||
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])])
|
||||
|
|
|
@ -34,6 +34,7 @@ EXCLUDE_DIRS = [
|
|||
"src/leveldb/",
|
||||
"src/secp256k1/",
|
||||
"src/univalue/",
|
||||
"src/crc32c/",
|
||||
]
|
||||
|
||||
INCLUDE = ['*.h', '*.cpp', '*.cc', '*.c', '*.mm', '*.py', '*.sh', '*.bash-completion']
|
||||
|
|
|
@ -861,7 +861,8 @@ RECURSIVE = YES
|
|||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE = src/leveldb \
|
||||
EXCLUDE = src/crc32c \
|
||||
src/leveldb \
|
||||
src/json \
|
||||
src/test \
|
||||
src/qt/test
|
||||
|
|
|
@ -858,6 +858,10 @@ Current subtrees include:
|
|||
- **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when
|
||||
merging upstream changes to the LevelDB subtree.
|
||||
|
||||
- src/crc32c
|
||||
- Used by leveldb for hardware acceleration of CRC32C checksums for data integrity.
|
||||
- Upstream at https://github.com/google/crc32c ; Maintained by Google.
|
||||
|
||||
- src/secp256k1
|
||||
- Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained by Core contributors.
|
||||
|
||||
|
|
|
@ -718,6 +718,7 @@ if HARDEN
|
|||
endif
|
||||
|
||||
if EMBEDDED_LEVELDB
|
||||
include Makefile.crc32c.include
|
||||
include Makefile.leveldb.include
|
||||
endif
|
||||
|
||||
|
|
75
src/Makefile.crc32c.include
Normal file
75
src/Makefile.crc32c.include
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Copyright (c) 2019 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
LIBCRC32C_INT = crc32c/libcrc32c.a
|
||||
LIBLEVELDB_SSE42_INT = leveldb/libleveldb_sse42.a
|
||||
|
||||
EXTRA_LIBRARIES += $(LIBCRC32C_INT)
|
||||
|
||||
LIBCRC32C = $(LIBCRC32C_INT)
|
||||
|
||||
CRC32C_CPPFLAGS_INT =
|
||||
CRC32C_CPPFLAGS_INT += -I$(srcdir)/crc32c/include
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_BUILTIN_PREFETCH=@HAVE_BUILTIN_PREFETCH@
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_MM_PREFETCH=@HAVE_MM_PREFETCH@
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_STRONG_GETAUXVAL=@HAVE_STRONG_GETAUXVAL@
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_WEAK_GETAUXVAL=@HAVE_WEAK_GETAUXVAL@
|
||||
CRC32C_CPPFLAGS_INT += -DCRC32C_TESTS_BUILT_WITH_GLOG=0
|
||||
|
||||
if ENABLE_SSE42
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_SSE42=1
|
||||
else
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_SSE42=0
|
||||
endif
|
||||
|
||||
if ENABLE_ARM_CRC
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_ARM64_CRC32C=1
|
||||
else
|
||||
CRC32C_CPPFLAGS_INT += -DHAVE_ARM64_CRC32C=0
|
||||
endif
|
||||
|
||||
if WORDS_BIGENDIAN
|
||||
CRC32C_CPPFLAGS_INT += -DBYTE_ORDER_BIG_ENDIAN=1
|
||||
else
|
||||
CRC32C_CPPFLAGS_INT += -DBYTE_ORDER_BIG_ENDIAN=0
|
||||
endif
|
||||
|
||||
crc32c_libcrc32c_a_CPPFLAGS = $(AM_CPPFLAGS) $(CRC32C_CPPFLAGS_INT) $(CRC32C_CPPFLAGS)
|
||||
crc32c_libcrc32c_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
crc32c_libcrc32c_a_SOURCES =
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/include/crc32c/crc32c.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_arm64.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_arm64_linux_check.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_internal.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_prefetch.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_read_le.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_round_up.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_sse42_check.h
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_sse42.h
|
||||
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c.cc
|
||||
crc32c_libcrc32c_a_SOURCES += crc32c/src/crc32c_portable.cc
|
||||
|
||||
if ENABLE_SSE42
|
||||
LIBCRC32C_SSE42_INT = crc32c/libcrc32c_sse42.a
|
||||
EXTRA_LIBRARIES += $(LIBCRC32C_SSE42_INT)
|
||||
LIBCRC32C += $(LIBCRC32C_SSE42_INT)
|
||||
|
||||
crc32c_libcrc32c_sse42_a_CPPFLAGS = $(crc32c_libcrc32c_a_CPPFLAGS)
|
||||
crc32c_libcrc32c_sse42_a_CXXFLAGS = $(crc32c_libcrc32c_a_CXXFLAGS) $(SSE42_CXXFLAGS)
|
||||
|
||||
crc32c_libcrc32c_sse42_a_SOURCES = crc32c/src/crc32c_sse42.cc
|
||||
endif
|
||||
|
||||
if ENABLE_ARM_CRC
|
||||
LIBCRC32C_ARM_CRC_INT = crc32c/libcrc32c_arm_crc.a
|
||||
EXTRA_LIBRARIES += $(LIBCRC32C_ARM_CRC_INT)
|
||||
LIBCRC32C += $(LIBCRC32C_ARM_CRC_INT)
|
||||
|
||||
crc32c_libcrc32c_arm_crc_a_CPPFLAGS = $(crc32c_libcrc32c_a_CPPFLAGS)
|
||||
crc32c_libcrc32c_arm_crc_a_CXXFLAGS = $(crc32c_libcrc32c_a_CXXFLAGS) $(ARM_CRC_CXXFLAGS)
|
||||
|
||||
crc32c_libcrc32c_arm_crc_a_SOURCES = crc32c/src/crc32c_arm64.cc
|
||||
endif
|
|
@ -4,27 +4,33 @@
|
|||
|
||||
LIBLEVELDB_INT = leveldb/libleveldb.a
|
||||
LIBMEMENV_INT = leveldb/libmemenv.a
|
||||
LIBLEVELDB_SSE42_INT = leveldb/libleveldb_sse42.a
|
||||
|
||||
EXTRA_LIBRARIES += $(LIBLEVELDB_INT)
|
||||
EXTRA_LIBRARIES += $(LIBMEMENV_INT)
|
||||
EXTRA_LIBRARIES += $(LIBLEVELDB_SSE42_INT)
|
||||
|
||||
LIBLEVELDB += $(LIBLEVELDB_INT)
|
||||
LIBLEVELDB += $(LIBLEVELDB_INT) $(LIBCRC32C)
|
||||
LIBMEMENV += $(LIBMEMENV_INT)
|
||||
LIBLEVELDB_SSE42 = $(LIBLEVELDB_SSE42_INT)
|
||||
|
||||
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
|
||||
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv
|
||||
|
||||
LEVELDB_CPPFLAGS_INT =
|
||||
LEVELDB_CPPFLAGS_INT += -I$(srcdir)/leveldb
|
||||
LEVELDB_CPPFLAGS_INT += $(LEVELDB_TARGET_FLAGS)
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_ATOMIC_PRESENT
|
||||
LEVELDB_CPPFLAGS_INT += -I$(srcdir)/crc32c/include
|
||||
LEVELDB_CPPFLAGS_INT += -D__STDC_LIMIT_MACROS
|
||||
LEVELDB_CPPFLAGS_INT += -DHAVE_SNAPPY=0 -DHAVE_CRC32C=1
|
||||
LEVELDB_CPPFLAGS_INT += -DHAVE_FDATASYNC=@HAVE_FDATASYNC@
|
||||
LEVELDB_CPPFLAGS_INT += -DHAVE_FULLFSYNC=@HAVE_FULLFSYNC@
|
||||
LEVELDB_CPPFLAGS_INT += -DHAVE_O_CLOEXEC=@HAVE_O_CLOEXEC@
|
||||
|
||||
if WORDS_BIGENDIAN
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_IS_BIG_ENDIAN=1
|
||||
else
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_IS_BIG_ENDIAN=0
|
||||
endif
|
||||
|
||||
if TARGET_WINDOWS
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_WINDOWS -D__USE_MINGW_ANSI_STDIO=1
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_WINDOWS -D_UNICODE -DUNICODE -D__USE_MINGW_ANSI_STDIO=1
|
||||
else
|
||||
LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_POSIX
|
||||
endif
|
||||
|
@ -33,12 +39,8 @@ leveldb_libleveldb_a_CPPFLAGS = $(AM_CPPFLAGS) $(LEVELDB_CPPFLAGS_INT) $(LEVELDB
|
|||
leveldb_libleveldb_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
|
||||
leveldb_libleveldb_a_SOURCES=
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/atomic_pointer.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_example.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_posix.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/win/stdint.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_stdcxx.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_win.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/thread_annotations.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/db.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/options.h
|
||||
|
@ -47,6 +49,7 @@ leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/filter_policy.h
|
|||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/slice.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/table_builder.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/env.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/export.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/c.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/iterator.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/cache.h
|
||||
|
@ -78,6 +81,7 @@ leveldb_libleveldb_a_SOURCES += leveldb/table/format.h
|
|||
leveldb_libleveldb_a_SOURCES += leveldb/table/iterator_wrapper.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/crc32c.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_posix_test_helper.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_windows_test_helper.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/arena.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/random.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/posix_logger.h
|
||||
|
@ -87,7 +91,9 @@ leveldb_libleveldb_a_SOURCES += leveldb/util/coding.h
|
|||
leveldb_libleveldb_a_SOURCES += leveldb/util/testutil.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/mutexlock.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/logging.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/no_destructor.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/testharness.h
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/windows_logger.h
|
||||
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/db/builder.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/db/c.cc
|
||||
|
@ -120,7 +126,6 @@ leveldb_libleveldb_a_SOURCES += leveldb/util/coding.cc
|
|||
leveldb_libleveldb_a_SOURCES += leveldb/util/comparator.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/crc32c.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_posix.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/filter_policy.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/hash.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/histogram.cc
|
||||
|
@ -129,21 +134,12 @@ leveldb_libleveldb_a_SOURCES += leveldb/util/options.cc
|
|||
leveldb_libleveldb_a_SOURCES += leveldb/util/status.cc
|
||||
|
||||
if TARGET_WINDOWS
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_win.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_win.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_windows.cc
|
||||
else
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/port/port_posix.cc
|
||||
leveldb_libleveldb_a_SOURCES += leveldb/util/env_posix.cc
|
||||
endif
|
||||
|
||||
leveldb_libmemenv_a_CPPFLAGS = $(leveldb_libleveldb_a_CPPFLAGS)
|
||||
leveldb_libmemenv_a_CXXFLAGS = $(leveldb_libleveldb_a_CXXFLAGS)
|
||||
leveldb_libmemenv_a_SOURCES = leveldb/helpers/memenv/memenv.cc
|
||||
leveldb_libmemenv_a_SOURCES += leveldb/helpers/memenv/memenv.h
|
||||
|
||||
leveldb_libleveldb_sse42_a_CPPFLAGS = $(leveldb_libleveldb_a_CPPFLAGS)
|
||||
leveldb_libleveldb_sse42_a_CXXFLAGS = $(leveldb_libleveldb_a_CXXFLAGS)
|
||||
if ENABLE_HWCRC32
|
||||
leveldb_libleveldb_sse42_a_CPPFLAGS += -DLEVELDB_PLATFORM_POSIX_SSE
|
||||
leveldb_libleveldb_sse42_a_CXXFLAGS += $(SSE42_CXXFLAGS)
|
||||
endif
|
||||
leveldb_libleveldb_sse42_a_SOURCES = leveldb/port/port_posix_sse.cc
|
||||
|
|
37
src/crc32c/.appveyor.yml
Normal file
37
src/crc32c/.appveyor.yml
Normal file
|
@ -0,0 +1,37 @@
|
|||
# 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%" -DCRC32C_USE_GLOG=0
|
||||
-DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%"
|
||||
- cmake --build . --config "%CONFIGURATION%"
|
||||
- cd ..
|
||||
|
||||
test_script:
|
||||
- build\%CONFIGURATION%\crc32c_tests.exe
|
||||
- build\%CONFIGURATION%\crc32c_capi_tests.exe
|
||||
- build\%CONFIGURATION%\crc32c_bench.exe
|
3
src/crc32c/.clang-format
Normal file
3
src/crc32c/.clang-format
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
8
src/crc32c/.clang_complete
Normal file
8
src/crc32c/.clang_complete
Normal file
|
@ -0,0 +1,8 @@
|
|||
-Ibuild/include/
|
||||
-Ibuild/third_party/glog/
|
||||
-Iinclude/
|
||||
-Ithird_party/benchmark/include/
|
||||
-Ithird_party/googletest/googletest/include/
|
||||
-Ithird_party/googletest/googlemock/include/
|
||||
-Ithird_party/glog/src/
|
||||
-std=c++11
|
8
src/crc32c/.gitignore
vendored
Normal file
8
src/crc32c/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Editors.
|
||||
*.sw*
|
||||
.DS_Store
|
||||
/.vscode
|
||||
|
||||
# Build directory.
|
||||
build/
|
||||
out/
|
0
src/crc32c/.gitmodules
vendored
Normal file
0
src/crc32c/.gitmodules
vendored
Normal file
76
src/crc32c/.travis.yml
Normal file
76
src/crc32c/.travis.yml
Normal file
|
@ -0,0 +1,76 @@
|
|||
# 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: xcode10.3
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
- GLOG=1 SHARED_LIB=0 BUILD_TYPE=Debug
|
||||
- GLOG=1 SHARED_LIB=0 BUILD_TYPE=RelWithDebInfo
|
||||
- GLOG=0 SHARED_LIB=0 BUILD_TYPE=Debug
|
||||
- GLOG=0 SHARED_LIB=0 BUILD_TYPE=RelWithDebInfo
|
||||
- GLOG=0 SHARED_LIB=1 BUILD_TYPE=Debug
|
||||
- GLOG=0 SHARED_LIB=1 BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
- sourceline: 'ppa:ubuntu-toolchain-r/test'
|
||||
packages:
|
||||
- clang-9
|
||||
- cmake
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- ninja-build
|
||||
homebrew:
|
||||
packages:
|
||||
- cmake
|
||||
- gcc@9
|
||||
- llvm@9
|
||||
- ninja
|
||||
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++-9" CC="gcc-9"; 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++-9" CC="clang-9"; fi;
|
||||
fi
|
||||
- echo ${CC}
|
||||
- echo ${CXX}
|
||||
- ${CXX} --version
|
||||
- cmake --version
|
||||
|
||||
before_script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. -G Ninja -DCRC32C_USE_GLOG=$GLOG -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
-DBUILD_SHARED_LIBS=$SHARED_LIB -DCMAKE_INSTALL_PREFIX=$HOME/.local
|
||||
- cmake --build .
|
||||
- cd ..
|
||||
|
||||
script:
|
||||
- build/crc32c_tests
|
||||
- build/crc32c_capi_tests
|
||||
- build/crc32c_bench
|
||||
- cd build && cmake --build . --target install
|
142
src/crc32c/.ycm_extra_conf.py
Normal file
142
src/crc32c/.ycm_extra_conf.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
# Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
"""YouCompleteMe configuration that interprets a .clang_complete file.
|
||||
|
||||
This module implementes the YouCompleteMe configuration API documented at:
|
||||
https://github.com/Valloric/ycmd#ycm_extra_confpy-specification
|
||||
|
||||
The implementation loads and processes a .clang_complete file, documented at:
|
||||
https://github.com/Rip-Rip/clang_complete/blob/master/README.md
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Flags added to the list in .clang_complete.
|
||||
BASE_FLAGS = [
|
||||
'-Werror', # Unlike clang_complete, YCM can also be used as a linter.
|
||||
'-DUSE_CLANG_COMPLETER', # YCM needs this.
|
||||
'-xc++', # YCM needs this to avoid compiling headers as C code.
|
||||
]
|
||||
|
||||
# Clang flags that take in paths.
|
||||
# See https://clang.llvm.org/docs/ClangCommandLineReference.html
|
||||
PATH_FLAGS = [
|
||||
'-isystem',
|
||||
'-I',
|
||||
'-iquote',
|
||||
'--sysroot='
|
||||
]
|
||||
|
||||
|
||||
def DirectoryOfThisScript():
|
||||
"""Returns the absolute path to the directory containing this script."""
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def MakeRelativePathsInFlagsAbsolute(flags, build_root):
|
||||
"""Expands relative paths in a list of Clang command-line flags.
|
||||
|
||||
Args:
|
||||
flags: The list of flags passed to Clang.
|
||||
build_root: The current directory when running the Clang compiler. Should be
|
||||
an absolute path.
|
||||
|
||||
Returns:
|
||||
A list of flags with relative paths replaced by absolute paths.
|
||||
"""
|
||||
new_flags = []
|
||||
make_next_absolute = False
|
||||
for flag in flags:
|
||||
new_flag = flag
|
||||
|
||||
if make_next_absolute:
|
||||
make_next_absolute = False
|
||||
if not flag.startswith('/'):
|
||||
new_flag = os.path.join(build_root, flag)
|
||||
|
||||
for path_flag in PATH_FLAGS:
|
||||
if flag == path_flag:
|
||||
make_next_absolute = True
|
||||
break
|
||||
|
||||
if flag.startswith(path_flag):
|
||||
path = flag[len(path_flag):]
|
||||
new_flag = path_flag + os.path.join(build_root, path)
|
||||
break
|
||||
|
||||
if new_flag:
|
||||
new_flags.append(new_flag)
|
||||
return new_flags
|
||||
|
||||
|
||||
def FindNearest(target, path, build_root):
|
||||
"""Looks for a file with a specific name closest to a project path.
|
||||
|
||||
This is similar to the logic used by a version-control system (like git) to
|
||||
find its configuration directory (.git) based on the current directory when a
|
||||
command is invoked.
|
||||
|
||||
Args:
|
||||
target: The file name to search for.
|
||||
path: The directory where the search starts. The search will explore the
|
||||
given directory's ascendants using the parent relationship. Should be an
|
||||
absolute path.
|
||||
build_root: A directory that acts as a fence for the search. If the search
|
||||
reaches this directory, it will not advance to its parent. Should be an
|
||||
absolute path.
|
||||
|
||||
Returns:
|
||||
The path to a file with the desired name. None if the search failed.
|
||||
"""
|
||||
candidate = os.path.join(path, target)
|
||||
if os.path.isfile(candidate):
|
||||
return candidate
|
||||
|
||||
if path == build_root:
|
||||
return None
|
||||
|
||||
parent = os.path.dirname(path)
|
||||
if parent == path:
|
||||
return None
|
||||
|
||||
return FindNearest(target, parent, build_root)
|
||||
|
||||
|
||||
def FlagsForClangComplete(file_path, build_root):
|
||||
"""Reads the .clang_complete flags for a source file.
|
||||
|
||||
Args:
|
||||
file_path: The path to the source file. Should be inside the project. Used
|
||||
to locate the relevant .clang_complete file.
|
||||
build_root: The current directory when running the Clang compiler for this
|
||||
file. Should be an absolute path.
|
||||
|
||||
Returns:
|
||||
A list of strings, where each element is a Clang command-line flag.
|
||||
"""
|
||||
clang_complete_path = FindNearest('.clang_complete', file_path, build_root)
|
||||
if clang_complete_path is None:
|
||||
return None
|
||||
clang_complete_flags = open(clang_complete_path, 'r').read().splitlines()
|
||||
return clang_complete_flags
|
||||
|
||||
|
||||
def FlagsForFile(filename, **kwargs):
|
||||
"""Implements the YouCompleteMe API."""
|
||||
|
||||
# kwargs can be used to pass 'client_data' to the YCM configuration. This
|
||||
# configuration script does not need any extra information, so
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
build_root = DirectoryOfThisScript()
|
||||
file_path = os.path.realpath(filename)
|
||||
|
||||
flags = BASE_FLAGS
|
||||
clang_flags = FlagsForClangComplete(file_path, build_root)
|
||||
if clang_flags:
|
||||
flags += clang_flags
|
||||
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute(flags, build_root)
|
||||
|
||||
return {'flags': final_flags}
|
9
src/crc32c/AUTHORS
Normal file
9
src/crc32c/AUTHORS
Normal file
|
@ -0,0 +1,9 @@
|
|||
# This is the list of CRC32C authors for copyright purposes.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code, since in
|
||||
# some cases, their employer may be the copyright holder. To see the full list
|
||||
# of contributors, see the revision history in source control.
|
||||
Google Inc.
|
||||
|
||||
Fangming Fang <Fangming.Fang@arm.com>
|
||||
Vadim Skipin <vadim.skipin@gmail.com>
|
423
src/crc32c/CMakeLists.txt
Normal file
423
src/crc32c/CMakeLists.txt
Normal file
|
@ -0,0 +1,423 @@
|
|||
# Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(Crc32c VERSION 1.1.0 LANGUAGES C CXX)
|
||||
|
||||
# This project can use C11, but will gracefully decay down to C89.
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED OFF)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
# This project requires C++11.
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# https://github.com/izenecloud/cmake/blob/master/SetCompilerWarningAll.cmake
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# Use the highest warning level for Visual Studio.
|
||||
set(CMAKE_CXX_WARNING_LEVEL 4)
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
|
||||
# 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")
|
||||
# Use -Wall for clang and gcc.
|
||||
if(NOT CMAKE_CXX_FLAGS MATCHES "-Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif(NOT CMAKE_CXX_FLAGS MATCHES "-Wall")
|
||||
|
||||
# Use -Wextra for clang and gcc.
|
||||
if(NOT CMAKE_CXX_FLAGS MATCHES "-Wextra")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
|
||||
endif(NOT CMAKE_CXX_FLAGS MATCHES "-Wextra")
|
||||
|
||||
# Use -Werror for clang and gcc.
|
||||
if(NOT CMAKE_CXX_FLAGS MATCHES "-Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
endif(NOT CMAKE_CXX_FLAGS MATCHES "-Werror")
|
||||
|
||||
# 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")
|
||||
|
||||
option(CRC32C_BUILD_TESTS "Build CRC32C's unit tests" ON)
|
||||
option(CRC32C_BUILD_BENCHMARKS "Build CRC32C's benchmarks" ON)
|
||||
option(CRC32C_USE_GLOG "Build CRC32C's tests with Google Logging" ON)
|
||||
option(CRC32C_INSTALL "Install CRC32C's header and library" ON)
|
||||
|
||||
include(TestBigEndian)
|
||||
test_big_endian(BYTE_ORDER_BIG_ENDIAN)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
# Used by glog.
|
||||
check_cxx_compiler_flag(-Wno-deprecated CRC32C_HAVE_NO_DEPRECATED)
|
||||
# Used by glog.
|
||||
check_cxx_compiler_flag(-Wno-sign-compare CRC32C_HAVE_NO_SIGN_COMPARE)
|
||||
# Used by glog.
|
||||
check_cxx_compiler_flag(-Wno-unused-parameter CRC32C_HAVE_NO_UNUSED_PARAMETER)
|
||||
# Used by googletest.
|
||||
check_cxx_compiler_flag(-Wno-missing-field-initializers
|
||||
CRC32C_HAVE_NO_MISSING_FIELD_INITIALIZERS)
|
||||
|
||||
# Check for __builtin_prefetch support in the compiler.
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
char data = 0;
|
||||
const char* address = &data;
|
||||
__builtin_prefetch(address, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_BUILTIN_PREFETCH)
|
||||
|
||||
# Check for _mm_prefetch support in the compiler.
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else // !defined(_MSC_VER)
|
||||
#include <xmmintrin.h>
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
int main() {
|
||||
char data = 0;
|
||||
const char* address = &data;
|
||||
_mm_prefetch(address, _MM_HINT_NTA);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MM_PREFETCH)
|
||||
|
||||
# Check for SSE4.2 support in the compiler.
|
||||
set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /arch:AVX")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -msse4.2")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
check_cxx_source_compiles("
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else // !defined(_MSC_VER)
|
||||
#include <cpuid.h>
|
||||
#include <nmmintrin.h>
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
int main() {
|
||||
_mm_crc32_u8(0, 0); _mm_crc32_u32(0, 0);
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
_mm_crc32_u64(0, 0);
|
||||
#endif // defined(_M_X64) || defined(__x86_64__)
|
||||
return 0;
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS})
|
||||
|
||||
# Check for ARMv8 w/ CRC and CRYPTO extensions support in the compiler.
|
||||
set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# TODO(pwnall): Insert correct flag when VS gets ARM CRC32C support.
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /arch:NOTYET")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=armv8-a+crc+crypto")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
check_cxx_source_compiles("
|
||||
#include <arm_acle.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
int main() {
|
||||
__crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0);
|
||||
vmull_p64(0, 0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_ARM64_CRC32C)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS})
|
||||
|
||||
# Check for strong getauxval() support in the system headers.
|
||||
check_cxx_source_compiles("
|
||||
#include <arm_acle.h>
|
||||
#include <arm_neon.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
int main() {
|
||||
getauxval(AT_HWCAP);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STRONG_GETAUXVAL)
|
||||
|
||||
# Check for weak getauxval() support in the compiler.
|
||||
check_cxx_source_compiles("
|
||||
unsigned long getauxval(unsigned long type) __attribute__((weak));
|
||||
#define AT_HWCAP 16
|
||||
|
||||
int main() {
|
||||
getauxval(AT_HWCAP);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WEAK_GETAUXVAL)
|
||||
|
||||
if(CRC32C_USE_GLOG)
|
||||
# glog requires this setting to avoid using dynamic_cast.
|
||||
set(DISABLE_RTTI ON CACHE BOOL "" FORCE)
|
||||
|
||||
# glog's test targets trigger deprecation warnings, and compiling them burns
|
||||
# CPU cycles on the CI.
|
||||
set(BUILD_TESTING_SAVED "${BUILD_TESTING}")
|
||||
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory("third_party/glog" EXCLUDE_FROM_ALL)
|
||||
set(BUILD_TESTING "${BUILD_TESTING_SAVED}" CACHE BOOL "" FORCE)
|
||||
|
||||
# glog triggers deprecation warnings on OSX.
|
||||
# https://github.com/google/glog/issues/185
|
||||
if(CRC32C_HAVE_NO_DEPRECATED)
|
||||
set_property(TARGET glog APPEND PROPERTY COMPILE_OPTIONS -Wno-deprecated)
|
||||
endif(CRC32C_HAVE_NO_DEPRECATED)
|
||||
|
||||
# glog triggers sign comparison warnings on gcc.
|
||||
if(CRC32C_HAVE_NO_SIGN_COMPARE)
|
||||
set_property(TARGET glog APPEND PROPERTY COMPILE_OPTIONS -Wno-sign-compare)
|
||||
endif(CRC32C_HAVE_NO_SIGN_COMPARE)
|
||||
|
||||
# glog triggers unused parameter warnings on clang.
|
||||
if(CRC32C_HAVE_NO_UNUSED_PARAMETER)
|
||||
set_property(TARGET glog
|
||||
APPEND PROPERTY COMPILE_OPTIONS -Wno-unused-parameter)
|
||||
endif(CRC32C_HAVE_NO_UNUSED_PARAMETER)
|
||||
|
||||
set(CRC32C_TESTS_BUILT_WITH_GLOG 1)
|
||||
endif(CRC32C_USE_GLOG)
|
||||
|
||||
configure_file(
|
||||
"src/crc32c_config.h.in"
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
)
|
||||
|
||||
include_directories("${PROJECT_BINARY_DIR}/include")
|
||||
|
||||
# ARM64 CRC32C code is built separately, so we don't accidentally compile
|
||||
# unsupported instructions into code that gets run without ARM32 support.
|
||||
add_library(crc32c_arm64 OBJECT "")
|
||||
target_sources(crc32c_arm64
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
"src/crc32c_arm64.cc"
|
||||
"src/crc32c_arm64.h"
|
||||
)
|
||||
if(HAVE_ARM64_CRC32C)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# TODO(pwnall): Insert correct flag when VS gets ARM64 CRC32C support.
|
||||
target_compile_options(crc32c_arm64 PRIVATE "/arch:NOTYET")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(crc32c_arm64 PRIVATE "-march=armv8-a+crc+crypto")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
endif(HAVE_ARM64_CRC32C)
|
||||
|
||||
# CMake only enables PIC by default in SHARED and MODULE targets.
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_property(TARGET crc32c_arm64 PROPERTY POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif(BUILD_SHARED_LIBS)
|
||||
|
||||
# SSE4.2 code is built separately, so we don't accidentally compile unsupported
|
||||
# instructions into code that gets run without SSE4.2 support.
|
||||
add_library(crc32c_sse42 OBJECT "")
|
||||
target_sources(crc32c_sse42
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
"src/crc32c_sse42.cc"
|
||||
"src/crc32c_sse42.h"
|
||||
)
|
||||
if(HAVE_SSE42)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(crc32c_sse42 PRIVATE "/arch:AVX")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(crc32c_sse42 PRIVATE "-msse4.2")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
endif(HAVE_SSE42)
|
||||
|
||||
# CMake only enables PIC by default in SHARED and MODULE targets.
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_property(TARGET crc32c_sse42 PROPERTY POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif(BUILD_SHARED_LIBS)
|
||||
|
||||
# Must be included before CMAKE_INSTALL_INCLUDEDIR is used.
|
||||
include(GNUInstallDirs)
|
||||
|
||||
add_library(crc32c ""
|
||||
# TODO(pwnall): Move the TARGET_OBJECTS generator expressions to the PRIVATE
|
||||
# section of target_sources when cmake_minimum_required becomes 3.9 or above.
|
||||
$<TARGET_OBJECTS:crc32c_arm64>
|
||||
$<TARGET_OBJECTS:crc32c_sse42>
|
||||
)
|
||||
target_sources(crc32c
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
"src/crc32c_arm64.h"
|
||||
"src/crc32c_arm64_linux_check.h"
|
||||
"src/crc32c_internal.h"
|
||||
"src/crc32c_portable.cc"
|
||||
"src/crc32c_prefetch.h"
|
||||
"src/crc32c_read_le.h"
|
||||
"src/crc32c_round_up.h"
|
||||
"src/crc32c_sse42.h"
|
||||
"src/crc32c_sse42_check.h"
|
||||
"src/crc32c.cc"
|
||||
|
||||
# Only CMake 3.3+ supports PUBLIC sources in targets exported by "install".
|
||||
$<$<VERSION_GREATER:CMAKE_VERSION,3.2>:PUBLIC>
|
||||
"include/crc32c/crc32c.h"
|
||||
)
|
||||
|
||||
target_include_directories(crc32c
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
target_compile_definitions(crc32c
|
||||
PRIVATE
|
||||
CRC32C_HAVE_CONFIG_H=1
|
||||
)
|
||||
|
||||
set_target_properties(crc32c
|
||||
PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
# Warnings as errors in Visual Studio for this project's targets.
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set_property(TARGET crc32c APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
set_property(TARGET crc32c_arm64 APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
set_property(TARGET crc32c_sse42 APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
if(CRC32C_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)
|
||||
|
||||
# This project is tested using GoogleTest.
|
||||
add_subdirectory("third_party/googletest")
|
||||
|
||||
# GoogleTest triggers a missing field initializers warning.
|
||||
if(CRC32C_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(CRC32C_HAVE_NO_MISSING_FIELD_INITIALIZERS)
|
||||
|
||||
add_executable(crc32c_tests "")
|
||||
target_sources(crc32c_tests
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
"src/crc32c_arm64_unittest.cc"
|
||||
"src/crc32c_extend_unittests.h"
|
||||
"src/crc32c_portable_unittest.cc"
|
||||
"src/crc32c_prefetch_unittest.cc"
|
||||
"src/crc32c_read_le_unittest.cc"
|
||||
"src/crc32c_round_up_unittest.cc"
|
||||
"src/crc32c_sse42_unittest.cc"
|
||||
"src/crc32c_unittest.cc"
|
||||
"src/crc32c_test_main.cc"
|
||||
)
|
||||
target_link_libraries(crc32c_tests crc32c gtest)
|
||||
|
||||
# Warnings as errors in Visual Studio for this project's targets.
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set_property(TARGET crc32c_tests APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
if(CRC32C_USE_GLOG)
|
||||
target_link_libraries(crc32c_tests glog)
|
||||
endif(CRC32C_USE_GLOG)
|
||||
|
||||
add_test(NAME crc32c_tests COMMAND crc32c_tests)
|
||||
|
||||
add_executable(crc32c_capi_tests "")
|
||||
target_sources(crc32c_capi_tests
|
||||
PRIVATE
|
||||
"src/crc32c_capi_unittest.c"
|
||||
)
|
||||
target_link_libraries(crc32c_capi_tests crc32c)
|
||||
|
||||
# Warnings as errors in Visual Studio for this project's targets.
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set_property(TARGET crc32c_capi_tests APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
add_test(NAME crc32c_capi_tests COMMAND crc32c_capi_tests)
|
||||
endif(CRC32C_BUILD_TESTS)
|
||||
|
||||
if(CRC32C_BUILD_BENCHMARKS)
|
||||
add_executable(crc32c_bench "")
|
||||
target_sources(crc32c_bench
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
|
||||
"src/crc32c_benchmark.cc"
|
||||
)
|
||||
target_link_libraries(crc32c_bench crc32c)
|
||||
|
||||
# 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")
|
||||
target_link_libraries(crc32c_bench benchmark)
|
||||
|
||||
if(CRC32C_USE_GLOG)
|
||||
target_link_libraries(crc32c_bench glog)
|
||||
endif(CRC32C_USE_GLOG)
|
||||
|
||||
# Warnings as errors in Visual Studio for this project's targets.
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set_property(TARGET crc32c_bench APPEND PROPERTY COMPILE_OPTIONS "/WX")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
endif(CRC32C_BUILD_BENCHMARKS)
|
||||
|
||||
if(CRC32C_INSTALL)
|
||||
install(TARGETS crc32c
|
||||
EXPORT Crc32cTargets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"include/crc32c/crc32c.h"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crc32c"
|
||||
)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_BINARY_DIR}/Crc32cConfigVersion.cmake"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
install(
|
||||
EXPORT Crc32cTargets
|
||||
NAMESPACE Crc32c::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crc32c"
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"Crc32cConfig.cmake"
|
||||
"${PROJECT_BINARY_DIR}/Crc32cConfigVersion.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Crc32c"
|
||||
)
|
||||
endif(CRC32C_INSTALL)
|
23
src/crc32c/CONTRIBUTING.md
Normal file
23
src/crc32c/CONTRIBUTING.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# How to Contribute
|
||||
|
||||
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 Agreement
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Code reviews
|
||||
|
||||
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.
|
5
src/crc32c/Crc32cConfig.cmake
Normal file
5
src/crc32c/Crc32cConfig.cmake
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright 2017 The CRC32C 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("${CMAKE_CURRENT_LIST_DIR}/Crc32cTargets.cmake")
|
28
src/crc32c/LICENSE
Normal file
28
src/crc32c/LICENSE
Normal file
|
@ -0,0 +1,28 @@
|
|||
Copyright 2017, The CRC32C Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
125
src/crc32c/README.md
Normal file
125
src/crc32c/README.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
# CRC32C
|
||||
|
||||
[![Build Status](https://travis-ci.org/google/crc32c.svg?branch=master)](https://travis-ci.org/google/crc32c)
|
||||
[![Build Status](https://ci.appveyor.com/api/projects/status/moiq7331pett4xuj/branch/master?svg=true)](https://ci.appveyor.com/project/pwnall/crc32c)
|
||||
|
||||
New file format authors should consider
|
||||
[HighwayHash](https://github.com/google/highwayhash). The initial version of
|
||||
this code was extracted from [LevelDB](https://github.com/google/leveldb), which
|
||||
is a stable key-value store that is widely used at Google.
|
||||
|
||||
This project collects a few CRC32C implementations under an umbrella that
|
||||
dispatches to a suitable implementation based on the host computer's hardware
|
||||
capabilities.
|
||||
|
||||
CRC32C is specified as the CRC that uses the iSCSI polynomial in
|
||||
[RFC 3720](https://tools.ietf.org/html/rfc3720#section-12.1). The polynomial was
|
||||
introduced by G. Castagnoli, S. Braeuer and M. Herrmann. CRC32C is used in
|
||||
software such as Btrfs, ext4, Ceph and leveldb.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```cpp
|
||||
#include "crc32c/crc32c.h"
|
||||
|
||||
int main() {
|
||||
const std::uint8_t buffer[] = {0, 0, 0, 0};
|
||||
std::uint32_t result;
|
||||
|
||||
// Process a raw buffer.
|
||||
result = crc32c::Crc32c(buffer, 4);
|
||||
|
||||
// Process a std::string.
|
||||
std::string string;
|
||||
string.resize(4);
|
||||
result = crc32c::Crc32c(string);
|
||||
|
||||
// If you have C++17 support, process a std::string_view.
|
||||
std::string_view string_view(string);
|
||||
result = crc32c::Crc32c(string_view);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This project uses [CMake](https://cmake.org/) for building and testing. CMake is
|
||||
available in all popular Linux distributions, as well as in
|
||||
[Homebrew](https://brew.sh/).
|
||||
|
||||
This project uses submodules for dependency management.
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
If you're using [Atom](https://atom.io/), the following packages can help.
|
||||
|
||||
```bash
|
||||
apm install autocomplete-clang build build-cmake clang-format language-cmake \
|
||||
linter linter-clang
|
||||
```
|
||||
|
||||
If you don't mind more setup in return for more speed, replace
|
||||
`autocomplete-clang` and `linter-clang` with `you-complete-me`. This requires
|
||||
[setting up ycmd](https://github.com/Valloric/ycmd#building).
|
||||
|
||||
```bash
|
||||
apm install autocomplete-plus build build-cmake clang-format language-cmake \
|
||||
linter you-complete-me
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
The following commands build and install the project.
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCRC32C_BUILD_TESTS=0 -DCRC32C_BUILD_BENCHMARKS=0 .. && make all install
|
||||
```
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
The following command (when executed from `build/`) (re)builds the project and
|
||||
runs the tests.
|
||||
|
||||
```bash
|
||||
cmake .. && cmake --build . && ctest --output-on-failure
|
||||
```
|
||||
|
||||
|
||||
### Android testing
|
||||
|
||||
The following command builds the project against the Android NDK, which is
|
||||
useful for benchmarking against ARM processors.
|
||||
|
||||
```bash
|
||||
cmake .. -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
|
||||
-DCMAKE_ANDROID_NDK=$HOME/Library/Android/sdk/ndk-bundle \
|
||||
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \
|
||||
-DCMAKE_ANDROID_STL_TYPE=c++_static -DCRC32C_USE_GLOG=0 \
|
||||
-DCMAKE_BUILD_TYPE=Release && cmake --build .
|
||||
```
|
||||
|
||||
The following commands install and run the benchmarks.
|
||||
|
||||
```bash
|
||||
adb push crc32c_bench /data/local/tmp
|
||||
adb shell chmod +x /data/local/tmp/crc32c_bench
|
||||
adb shell 'cd /data/local/tmp && ./crc32c_bench'
|
||||
adb shell rm /data/local/tmp/crc32c_bench
|
||||
```
|
||||
|
||||
The following commands install and run the tests.
|
||||
|
||||
```bash
|
||||
adb push crc32c_tests /data/local/tmp
|
||||
adb shell chmod +x /data/local/tmp/crc32c_tests
|
||||
adb shell 'cd /data/local/tmp && ./crc32c_tests'
|
||||
adb shell rm /data/local/tmp/crc32c_tests
|
||||
```
|
89
src/crc32c/include/crc32c/crc32c.h
Normal file
89
src/crc32c/include/crc32c/crc32c.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. */
|
||||
|
||||
#ifndef CRC32C_CRC32C_H_
|
||||
#define CRC32C_CRC32C_H_
|
||||
|
||||
/* The API exported by the CRC32C project. */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#else /* !defined(__cplusplus) */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#endif /* !defined(__cplusplus) */
|
||||
|
||||
|
||||
/* The C API. */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* defined(__cplusplus) */
|
||||
|
||||
/* Extends "crc" with the CRC32C of "count" bytes in the buffer pointed by
|
||||
"data" */
|
||||
uint32_t crc32c_extend(uint32_t crc, const uint8_t* data, size_t count);
|
||||
|
||||
/* Computes the CRC32C of "count" bytes in the buffer pointed by "data". */
|
||||
uint32_t crc32c_value(const uint8_t* data, size_t count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif /* defined(__cplusplus) */
|
||||
|
||||
|
||||
/* The C++ API. */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Extends "crc" with the CRC32C of "count" bytes in the buffer pointed by
|
||||
// "data".
|
||||
uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count);
|
||||
|
||||
// Computes the CRC32C of "count" bytes in the buffer pointed by "data".
|
||||
inline uint32_t Crc32c(const uint8_t* data, size_t count) {
|
||||
return Extend(0, data, count);
|
||||
}
|
||||
|
||||
// Computes the CRC32C of "count" bytes in the buffer pointed by "data".
|
||||
inline uint32_t Crc32c(const char* data, size_t count) {
|
||||
return Extend(0, reinterpret_cast<const uint8_t*>(data), count);
|
||||
}
|
||||
|
||||
// Computes the CRC32C of the string's content.
|
||||
inline uint32_t Crc32c(const std::string& string) {
|
||||
return Crc32c(reinterpret_cast<const uint8_t*>(string.data()),
|
||||
string.size());
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
#if __has_include(<string_view>)
|
||||
#include <string_view>
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Computes the CRC32C of the bytes in the string_view.
|
||||
inline uint32_t Crc32c(const std::string_view& string_view) {
|
||||
return Crc32c(reinterpret_cast<const uint8_t*>(string_view.data()),
|
||||
string_view.size());
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // __has_include(<string_view>)
|
||||
#endif // __cplusplus > 201402L
|
||||
|
||||
#endif /* defined(__cplusplus) */
|
||||
|
||||
#endif // CRC32C_CRC32C_H_
|
39
src/crc32c/src/crc32c.cc
Normal file
39
src/crc32c/src/crc32c.cc
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2017 The CRC32C 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 "crc32c/crc32c.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "./crc32c_arm64.h"
|
||||
#include "./crc32c_arm64_linux_check.h"
|
||||
#include "./crc32c_internal.h"
|
||||
#include "./crc32c_sse42.h"
|
||||
#include "./crc32c_sse42_check.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
static bool can_use_sse42 = CanUseSse42();
|
||||
if (can_use_sse42) return ExtendSse42(crc, data, count);
|
||||
#elif HAVE_ARM64_CRC32C
|
||||
static bool can_use_arm_linux = CanUseArm64Linux();
|
||||
if (can_use_arm_linux) return ExtendArm64(crc, data, count);
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
return ExtendPortable(crc, data, count);
|
||||
}
|
||||
|
||||
extern "C" uint32_t crc32c_extend(uint32_t crc, const uint8_t* data,
|
||||
size_t count) {
|
||||
return crc32c::Extend(crc, data, count);
|
||||
}
|
||||
|
||||
extern "C" uint32_t crc32c_value(const uint8_t* data, size_t count) {
|
||||
return crc32c::Crc32c(data, count);
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
126
src/crc32c/src/crc32c_arm64.cc
Normal file
126
src/crc32c/src/crc32c_arm64.cc
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2017 The CRC32C 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 "./crc32c_arm64.h"
|
||||
|
||||
// In a separate source file to allow this accelerated CRC32C function to be
|
||||
// compiled with the appropriate compiler flags to enable ARM NEON CRC32C
|
||||
// instructions.
|
||||
|
||||
// This implementation is based on https://github.com/google/leveldb/pull/490.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "./crc32c_internal.h"
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_ARM64_CRC32C
|
||||
|
||||
#include <arm_acle.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
#define KBYTES 1032
|
||||
#define SEGMENTBYTES 256
|
||||
|
||||
// compute 8bytes for each segment parallelly
|
||||
#define CRC32C32BYTES(P, IND) \
|
||||
do { \
|
||||
crc1 = __crc32cd( \
|
||||
crc1, *((const uint64_t *)(P) + (SEGMENTBYTES / 8) * 1 + (IND))); \
|
||||
crc2 = __crc32cd( \
|
||||
crc2, *((const uint64_t *)(P) + (SEGMENTBYTES / 8) * 2 + (IND))); \
|
||||
crc3 = __crc32cd( \
|
||||
crc3, *((const uint64_t *)(P) + (SEGMENTBYTES / 8) * 3 + (IND))); \
|
||||
crc0 = __crc32cd( \
|
||||
crc0, *((const uint64_t *)(P) + (SEGMENTBYTES / 8) * 0 + (IND))); \
|
||||
} while (0);
|
||||
|
||||
// compute 8*8 bytes for each segment parallelly
|
||||
#define CRC32C256BYTES(P, IND) \
|
||||
do { \
|
||||
CRC32C32BYTES((P), (IND)*8 + 0) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 1) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 2) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 3) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 4) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 5) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 6) \
|
||||
CRC32C32BYTES((P), (IND)*8 + 7) \
|
||||
} while (0);
|
||||
|
||||
// compute 4*8*8 bytes for each segment parallelly
|
||||
#define CRC32C1024BYTES(P) \
|
||||
do { \
|
||||
CRC32C256BYTES((P), 0) \
|
||||
CRC32C256BYTES((P), 1) \
|
||||
CRC32C256BYTES((P), 2) \
|
||||
CRC32C256BYTES((P), 3) \
|
||||
(P) += 4 * SEGMENTBYTES; \
|
||||
} while (0)
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) {
|
||||
int64_t length = size;
|
||||
uint32_t crc0, crc1, crc2, crc3;
|
||||
uint64_t t0, t1, t2;
|
||||
|
||||
// k0=CRC(x^(3*SEGMENTBYTES*8)), k1=CRC(x^(2*SEGMENTBYTES*8)),
|
||||
// k2=CRC(x^(SEGMENTBYTES*8))
|
||||
const poly64_t k0 = 0x8d96551c, k1 = 0xbd6f81f8, k2 = 0xdcb17aa4;
|
||||
|
||||
crc = crc ^ kCRC32Xor;
|
||||
const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);
|
||||
|
||||
while (length >= KBYTES) {
|
||||
crc0 = crc;
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
crc3 = 0;
|
||||
|
||||
// Process 1024 bytes in parallel.
|
||||
CRC32C1024BYTES(p);
|
||||
|
||||
// Merge the 4 partial CRC32C values.
|
||||
t2 = (uint64_t)vmull_p64(crc2, k2);
|
||||
t1 = (uint64_t)vmull_p64(crc1, k1);
|
||||
t0 = (uint64_t)vmull_p64(crc0, k0);
|
||||
crc = __crc32cd(crc3, *(uint64_t *)p);
|
||||
p += sizeof(uint64_t);
|
||||
crc ^= __crc32cd(0, t2);
|
||||
crc ^= __crc32cd(0, t1);
|
||||
crc ^= __crc32cd(0, t0);
|
||||
|
||||
length -= KBYTES;
|
||||
}
|
||||
|
||||
while (length >= 8) {
|
||||
crc = __crc32cd(crc, *(uint64_t *)p);
|
||||
p += 8;
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
if (length & 4) {
|
||||
crc = __crc32cw(crc, *(uint32_t *)p);
|
||||
p += 4;
|
||||
}
|
||||
|
||||
if (length & 2) {
|
||||
crc = __crc32ch(crc, *(uint16_t *)p);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
if (length & 1) {
|
||||
crc = __crc32cb(crc, *p);
|
||||
}
|
||||
|
||||
return crc ^ kCRC32Xor;
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // HAVE_ARM64_CRC32C
|
27
src/crc32c/src/crc32c_arm64.h
Normal file
27
src/crc32c/src/crc32c_arm64.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2017 The CRC32C 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.
|
||||
|
||||
// Linux-specific code checking the availability for ARM CRC32C instructions.
|
||||
|
||||
#ifndef CRC32C_CRC32C_ARM_LINUX_H_
|
||||
#define CRC32C_CRC32C_ARM_LINUX_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_ARM64_CRC32C
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
uint32_t ExtendArm64(uint32_t crc, const uint8_t* data, size_t count);
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // HAVE_ARM64_CRC32C
|
||||
|
||||
#endif // CRC32C_CRC32C_ARM_LINUX_H_
|
50
src/crc32c/src/crc32c_arm64_linux_check.h
Normal file
50
src/crc32c/src/crc32c_arm64_linux_check.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2017 The CRC32C 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.
|
||||
|
||||
// ARM Linux-specific code checking for the availability of CRC32C instructions.
|
||||
|
||||
#ifndef CRC32C_CRC32C_ARM_LINUX_CHECK_H_
|
||||
#define CRC32C_CRC32C_ARM_LINUX_CHECK_H_
|
||||
|
||||
// X86-specific code checking for the availability of SSE4.2 instructions.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_ARM64_CRC32C
|
||||
|
||||
#if HAVE_STRONG_GETAUXVAL
|
||||
#include <sys/auxv.h>
|
||||
#elif HAVE_WEAK_GETAUXVAL
|
||||
// getauxval() is not available on Android until API level 20. Link it as a weak
|
||||
// symbol.
|
||||
extern "C" unsigned long getauxval(unsigned long type) __attribute__((weak));
|
||||
|
||||
#define AT_HWCAP 16
|
||||
#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
inline bool CanUseArm64Linux() {
|
||||
#if HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
|
||||
// From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code.
|
||||
constexpr unsigned long kHWCAP_PMULL = 1 << 4;
|
||||
constexpr unsigned long kHWCAP_CRC32 = 1 << 7;
|
||||
unsigned long hwcap = (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
|
||||
return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) ==
|
||||
(kHWCAP_PMULL | kHWCAP_CRC32);
|
||||
#else
|
||||
return false;
|
||||
#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // HAVE_ARM64_CRC32C
|
||||
|
||||
#endif // CRC32C_CRC32C_ARM_LINUX_CHECK_H_
|
24
src/crc32c/src/crc32c_arm64_unittest.cc
Normal file
24
src/crc32c/src/crc32c_arm64_unittest.cc
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 The CRC32C 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 "gtest/gtest.h"
|
||||
|
||||
#include "./crc32c_arm64.h"
|
||||
#include "./crc32c_extend_unittests.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
#if HAVE_ARM64_CRC32C
|
||||
|
||||
struct Arm64TestTraits {
|
||||
static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
|
||||
return ExtendArm64(crc, data, count);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(Arm64, ExtendTest, Arm64TestTraits);
|
||||
|
||||
#endif // HAVE_ARM64_CRC32C
|
||||
|
||||
} // namespace crc32c
|
106
src/crc32c/src/crc32c_benchmark.cc
Normal file
106
src/crc32c/src/crc32c_benchmark.cc
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2017 The CRC32C 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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#if CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
#include "glog/logging.h"
|
||||
#endif // CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
|
||||
#include "./crc32c_arm64.h"
|
||||
#include "./crc32c_arm64_linux_check.h"
|
||||
#include "./crc32c_internal.h"
|
||||
#include "./crc32c_sse42.h"
|
||||
#include "./crc32c_sse42_check.h"
|
||||
#include "crc32c/crc32c.h"
|
||||
|
||||
class CRC32CBenchmark : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const benchmark::State& state) override {
|
||||
block_size_ = static_cast<size_t>(state.range(0));
|
||||
block_data_ = std::string(block_size_, 'x');
|
||||
block_buffer_ = reinterpret_cast<const uint8_t*>(block_data_.data());
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string block_data_;
|
||||
const uint8_t* block_buffer_;
|
||||
size_t block_size_;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(CRC32CBenchmark, Public)(benchmark::State& state) {
|
||||
uint32_t crc = 0;
|
||||
for (auto _ : state)
|
||||
crc = crc32c::Extend(crc, block_buffer_, block_size_);
|
||||
state.SetBytesProcessed(state.iterations() * block_size_);
|
||||
}
|
||||
BENCHMARK_REGISTER_F(CRC32CBenchmark, Public)
|
||||
->RangeMultiplier(16)
|
||||
->Range(256, 16777216); // Block size.
|
||||
|
||||
BENCHMARK_DEFINE_F(CRC32CBenchmark, Portable)(benchmark::State& state) {
|
||||
uint32_t crc = 0;
|
||||
for (auto _ : state)
|
||||
crc = crc32c::ExtendPortable(crc, block_buffer_, block_size_);
|
||||
state.SetBytesProcessed(state.iterations() * block_size_);
|
||||
}
|
||||
BENCHMARK_REGISTER_F(CRC32CBenchmark, Portable)
|
||||
->RangeMultiplier(16)
|
||||
->Range(256, 16777216); // Block size.
|
||||
|
||||
#if HAVE_ARM64_CRC32C
|
||||
|
||||
BENCHMARK_DEFINE_F(CRC32CBenchmark, ArmLinux)(benchmark::State& state) {
|
||||
if (!crc32c::CanUseArm64Linux()) {
|
||||
state.SkipWithError("ARM CRC32C instructions not available or not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t crc = 0;
|
||||
for (auto _ : state)
|
||||
crc = crc32c::ExtendArm64(crc, block_buffer_, block_size_);
|
||||
state.SetBytesProcessed(state.iterations() * block_size_);
|
||||
}
|
||||
BENCHMARK_REGISTER_F(CRC32CBenchmark, ArmLinux)
|
||||
->RangeMultiplier(16)
|
||||
->Range(256, 16777216); // Block size.
|
||||
|
||||
#endif // HAVE_ARM64_CRC32C
|
||||
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
BENCHMARK_DEFINE_F(CRC32CBenchmark, Sse42)(benchmark::State& state) {
|
||||
if (!crc32c::CanUseSse42()) {
|
||||
state.SkipWithError("SSE4.2 instructions not available or not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t crc = 0;
|
||||
for (auto _ : state)
|
||||
crc = crc32c::ExtendSse42(crc, block_buffer_, block_size_);
|
||||
state.SetBytesProcessed(state.iterations() * block_size_);
|
||||
}
|
||||
BENCHMARK_REGISTER_F(CRC32CBenchmark, Sse42)
|
||||
->RangeMultiplier(16)
|
||||
->Range(256, 16777216); // Block size.
|
||||
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#if CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
google::InstallFailureSignalHandler();
|
||||
#endif // CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
return 0;
|
||||
}
|
66
src/crc32c/src/crc32c_capi_unittest.c
Normal file
66
src/crc32c/src/crc32c_capi_unittest.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2017 The CRC32C 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 "crc32c/crc32c.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
/* From rfc3720 section B.4. */
|
||||
uint8_t buf[32];
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if ((uint32_t)0x8a9136aa != crc32c_value(buf, sizeof(buf))) {
|
||||
printf("crc32c_value(zeros) test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0xff, sizeof(buf));
|
||||
if ((uint32_t)0x62a8ab43 != crc32c_value(buf, sizeof(buf))) {
|
||||
printf("crc32c_value(0xff) test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = (uint8_t)i;
|
||||
if ((uint32_t)0x46dd794e != crc32c_value(buf, sizeof(buf))) {
|
||||
printf("crc32c_value(0..31) test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = (uint8_t)(31 - i);
|
||||
if ((uint32_t)0x113fdb5c != crc32c_value(buf, sizeof(buf))) {
|
||||
printf("crc32c_value(31..0) test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
if ((uint32_t)0xd9963a56 != crc32c_value(data, sizeof(data))) {
|
||||
printf("crc32c_value(31..0) test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const uint8_t* hello_space_world = (const uint8_t*)"hello world";
|
||||
const uint8_t* hello_space = (const uint8_t*)"hello ";
|
||||
const uint8_t* world = (const uint8_t*)"world";
|
||||
|
||||
if (crc32c_value(hello_space_world, 11) !=
|
||||
crc32c_extend(crc32c_value(hello_space, 6), world, 5)) {
|
||||
printf("crc32c_extend test failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
36
src/crc32c/src/crc32c_config.h.in
Normal file
36
src/crc32c/src/crc32c_config.h.in
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_CONFIG_H_
|
||||
#define CRC32C_CRC32C_CONFIG_H_
|
||||
|
||||
// Define to 1 if building for a big-endian platform.
|
||||
#cmakedefine01 BYTE_ORDER_BIG_ENDIAN
|
||||
|
||||
// Define to 1 if the compiler has the __builtin_prefetch intrinsic.
|
||||
#cmakedefine01 HAVE_BUILTIN_PREFETCH
|
||||
|
||||
// Define to 1 if targeting X86 and the compiler has the _mm_prefetch intrinsic.
|
||||
#cmakedefine01 HAVE_MM_PREFETCH
|
||||
|
||||
// Define to 1 if targeting X86 and the compiler has the _mm_crc32_u{8,32,64}
|
||||
// intrinsics.
|
||||
#cmakedefine01 HAVE_SSE42
|
||||
|
||||
// Define to 1 if targeting ARM and the compiler has the __crc32c{b,h,w,d} and
|
||||
// the vmull_p64 intrinsics.
|
||||
#cmakedefine01 HAVE_ARM64_CRC32C
|
||||
|
||||
// Define to 1 if the system libraries have the getauxval function in the
|
||||
// <sys/auxv.h> header. Should be true on Linux and Android API level 20+.
|
||||
#cmakedefine01 HAVE_STRONG_GETAUXVAL
|
||||
|
||||
// Define to 1 if the compiler supports defining getauxval as a weak symbol.
|
||||
// Should be true for any compiler that supports __attribute__((weak)).
|
||||
#cmakedefine01 HAVE_WEAK_GETAUXVAL
|
||||
|
||||
// Define to 1 if CRC32C tests have been built with Google Logging.
|
||||
#cmakedefine01 CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
|
||||
#endif // CRC32C_CRC32C_CONFIG_H_
|
112
src/crc32c/src/crc32c_extend_unittests.h
Normal file
112
src/crc32c/src/crc32c_extend_unittests.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_EXTEND_UNITTESTS_H_
|
||||
#define CRC32C_CRC32C_EXTEND_UNITTESTS_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Common test cases for all implementations of CRC32C_Extend().
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
template<typename TestTraits>
|
||||
class ExtendTest : public testing::Test {};
|
||||
|
||||
TYPED_TEST_SUITE_P(ExtendTest);
|
||||
|
||||
TYPED_TEST_P(ExtendTest, StandardResults) {
|
||||
// From rfc3720 section B.4.
|
||||
uint8_t buf[32];
|
||||
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x8a9136aa),
|
||||
TypeParam::Extend(0, buf, sizeof(buf)));
|
||||
|
||||
std::memset(buf, 0xff, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x62a8ab43),
|
||||
TypeParam::Extend(0, buf, sizeof(buf)));
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x46dd794e),
|
||||
TypeParam::Extend(0, buf, sizeof(buf)));
|
||||
|
||||
for (int i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(31 - i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x113fdb5c),
|
||||
TypeParam::Extend(0, buf, sizeof(buf)));
|
||||
|
||||
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,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
EXPECT_EQ(static_cast<uint32_t>(0xd9963a56),
|
||||
TypeParam::Extend(0, data, sizeof(data)));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(ExtendTest, HelloWorld) {
|
||||
const uint8_t* hello_space_world =
|
||||
reinterpret_cast<const uint8_t*>("hello world");
|
||||
const uint8_t* hello_space = reinterpret_cast<const uint8_t*>("hello ");
|
||||
const uint8_t* world = reinterpret_cast<const uint8_t*>("world");
|
||||
|
||||
EXPECT_EQ(TypeParam::Extend(0, hello_space_world, 11),
|
||||
TypeParam::Extend(TypeParam::Extend(0, hello_space, 6), world, 5));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(ExtendTest, BufferSlicing) {
|
||||
uint8_t buffer[48] = {
|
||||
0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < 48; ++i) {
|
||||
for (size_t j = i + 1; j <= 48; ++j) {
|
||||
uint32_t crc = 0;
|
||||
|
||||
if (i > 0) crc = TypeParam::Extend(crc, buffer, i);
|
||||
crc = TypeParam::Extend(crc, buffer + i, j - i);
|
||||
if (j < 48) crc = TypeParam::Extend(crc, buffer + j, 48 - j);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>(0xd9963a56), crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(ExtendTest, LargeBufferSlicing) {
|
||||
uint8_t buffer[2048];
|
||||
for (size_t i = 0; i < 2048; i++)
|
||||
buffer[i] = static_cast<uint8_t>(3 * i * i + 7 * i + 11);
|
||||
|
||||
for (size_t i = 0; i < 2048; ++i) {
|
||||
for (size_t j = i + 1; j <= 2048; ++j) {
|
||||
uint32_t crc = 0;
|
||||
|
||||
if (i > 0) crc = TypeParam::Extend(crc, buffer, i);
|
||||
crc = TypeParam::Extend(crc, buffer + i, j - i);
|
||||
if (j < 2048) crc = TypeParam::Extend(crc, buffer + j, 2048 - j);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x36dcc753), crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(ExtendTest,
|
||||
StandardResults,
|
||||
HelloWorld,
|
||||
BufferSlicing,
|
||||
LargeBufferSlicing);
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // CRC32C_CRC32C_EXTEND_UNITTESTS_H_
|
23
src/crc32c/src/crc32c_internal.h
Normal file
23
src/crc32c/src/crc32c_internal.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_INTERNAL_H_
|
||||
#define CRC32C_CRC32C_INTERNAL_H_
|
||||
|
||||
// Internal functions that may change between releases.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Un-accelerated implementation that works on all CPUs.
|
||||
uint32_t ExtendPortable(uint32_t crc, const uint8_t* data, size_t count);
|
||||
|
||||
// CRCs are pre- and post- conditioned by xoring with all ones.
|
||||
static constexpr const uint32_t kCRC32Xor = static_cast<uint32_t>(0xffffffffU);
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // CRC32C_CRC32C_INTERNAL_H_
|
351
src/crc32c/src/crc32c_portable.cc
Normal file
351
src/crc32c/src/crc32c_portable.cc
Normal file
|
@ -0,0 +1,351 @@
|
|||
// Copyright 2008 The CRC32C 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 "./crc32c_internal.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "./crc32c_prefetch.h"
|
||||
#include "./crc32c_read_le.h"
|
||||
#include "./crc32c_round_up.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kByteExtensionTable[256] = {
|
||||
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c,
|
||||
0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
|
||||
0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c,
|
||||
0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
|
||||
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc,
|
||||
0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
|
||||
0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512,
|
||||
0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
|
||||
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad,
|
||||
0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
|
||||
0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf,
|
||||
0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
|
||||
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f,
|
||||
0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
|
||||
0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f,
|
||||
0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
|
||||
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e,
|
||||
0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
|
||||
0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e,
|
||||
0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
|
||||
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de,
|
||||
0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
|
||||
0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4,
|
||||
0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
|
||||
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b,
|
||||
0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
|
||||
0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5,
|
||||
0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
|
||||
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975,
|
||||
0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
|
||||
0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905,
|
||||
0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
|
||||
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8,
|
||||
0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
|
||||
0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8,
|
||||
0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
|
||||
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78,
|
||||
0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
|
||||
0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6,
|
||||
0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
|
||||
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69,
|
||||
0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
|
||||
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351};
|
||||
|
||||
const uint32_t kStrideExtensionTable0[256] = {
|
||||
0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1,
|
||||
0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76,
|
||||
0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526,
|
||||
0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478,
|
||||
0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b,
|
||||
0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229,
|
||||
0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a,
|
||||
0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664,
|
||||
0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34,
|
||||
0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3,
|
||||
0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69,
|
||||
0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37,
|
||||
0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924,
|
||||
0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0,
|
||||
0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3,
|
||||
0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad,
|
||||
0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b,
|
||||
0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc,
|
||||
0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac,
|
||||
0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2,
|
||||
0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1,
|
||||
0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7,
|
||||
0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4,
|
||||
0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa,
|
||||
0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa,
|
||||
0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d,
|
||||
0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb,
|
||||
0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5,
|
||||
0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6,
|
||||
0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572,
|
||||
0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061,
|
||||
0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f,
|
||||
0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5,
|
||||
0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262,
|
||||
0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32,
|
||||
0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c,
|
||||
0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f,
|
||||
0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d,
|
||||
0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e,
|
||||
0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970,
|
||||
0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120,
|
||||
0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7,
|
||||
0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433};
|
||||
|
||||
const uint32_t kStrideExtensionTable1[256] = {
|
||||
0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af,
|
||||
0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818,
|
||||
0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13,
|
||||
0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576,
|
||||
0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828,
|
||||
0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60,
|
||||
0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e,
|
||||
0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b,
|
||||
0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50,
|
||||
0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7,
|
||||
0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3,
|
||||
0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86,
|
||||
0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8,
|
||||
0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a,
|
||||
0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864,
|
||||
0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101,
|
||||
0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0,
|
||||
0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917,
|
||||
0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c,
|
||||
0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479,
|
||||
0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927,
|
||||
0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880,
|
||||
0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de,
|
||||
0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb,
|
||||
0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0,
|
||||
0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607,
|
||||
0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6,
|
||||
0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3,
|
||||
0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d,
|
||||
0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f,
|
||||
0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21,
|
||||
0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744,
|
||||
0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240,
|
||||
0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7,
|
||||
0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc,
|
||||
0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199,
|
||||
0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7,
|
||||
0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f,
|
||||
0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1,
|
||||
0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4,
|
||||
0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf,
|
||||
0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708,
|
||||
0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1};
|
||||
|
||||
const uint32_t kStrideExtensionTable2[256] = {
|
||||
0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4,
|
||||
0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418,
|
||||
0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37,
|
||||
0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0,
|
||||
0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9,
|
||||
0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f,
|
||||
0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276,
|
||||
0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81,
|
||||
0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae,
|
||||
0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42,
|
||||
0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328,
|
||||
0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf,
|
||||
0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6,
|
||||
0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c,
|
||||
0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605,
|
||||
0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2,
|
||||
0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1,
|
||||
0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d,
|
||||
0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972,
|
||||
0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185,
|
||||
0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c,
|
||||
0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0,
|
||||
0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9,
|
||||
0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e,
|
||||
0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361,
|
||||
0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d,
|
||||
0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce,
|
||||
0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339,
|
||||
0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20,
|
||||
0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa,
|
||||
0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3,
|
||||
0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614,
|
||||
0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e,
|
||||
0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092,
|
||||
0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd,
|
||||
0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a,
|
||||
0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53,
|
||||
0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5,
|
||||
0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc,
|
||||
0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b,
|
||||
0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124,
|
||||
0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8,
|
||||
0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d};
|
||||
|
||||
const uint32_t kStrideExtensionTable3[256] = {
|
||||
0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115,
|
||||
0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4,
|
||||
0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541,
|
||||
0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7,
|
||||
0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d,
|
||||
0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d,
|
||||
0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7,
|
||||
0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241,
|
||||
0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4,
|
||||
0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615,
|
||||
0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02,
|
||||
0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4,
|
||||
0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce,
|
||||
0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0,
|
||||
0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a,
|
||||
0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c,
|
||||
0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297,
|
||||
0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56,
|
||||
0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3,
|
||||
0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725,
|
||||
0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f,
|
||||
0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b,
|
||||
0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721,
|
||||
0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7,
|
||||
0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52,
|
||||
0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293,
|
||||
0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978,
|
||||
0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e,
|
||||
0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4,
|
||||
0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca,
|
||||
0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0,
|
||||
0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06,
|
||||
0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611,
|
||||
0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0,
|
||||
0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245,
|
||||
0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3,
|
||||
0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189,
|
||||
0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689,
|
||||
0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3,
|
||||
0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545,
|
||||
0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0,
|
||||
0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111,
|
||||
0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa};
|
||||
|
||||
constexpr const ptrdiff_t kPrefetchHorizon = 256;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
uint32_t ExtendPortable(uint32_t crc, const uint8_t* data, size_t size) {
|
||||
const uint8_t* p = data;
|
||||
const uint8_t* e = p + size;
|
||||
uint32_t l = crc ^ kCRC32Xor;
|
||||
|
||||
// Process one byte at a time.
|
||||
#define STEP1 \
|
||||
do { \
|
||||
int c = (l & 0xff) ^ *p++; \
|
||||
l = kByteExtensionTable[c] ^ (l >> 8); \
|
||||
} while (0)
|
||||
|
||||
// Process one of the 4 strides of 4-byte data.
|
||||
#define STEP4(s) \
|
||||
do { \
|
||||
crc##s = ReadUint32LE(p + s * 4) ^ kStrideExtensionTable3[crc##s & 0xff] ^ \
|
||||
kStrideExtensionTable2[(crc##s >> 8) & 0xff] ^ \
|
||||
kStrideExtensionTable1[(crc##s >> 16) & 0xff] ^ \
|
||||
kStrideExtensionTable0[crc##s >> 24]; \
|
||||
} while (0)
|
||||
|
||||
// Process a 16-byte swath of 4 strides, each of which has 4 bytes of data.
|
||||
#define STEP16 \
|
||||
do { \
|
||||
STEP4(0); \
|
||||
STEP4(1); \
|
||||
STEP4(2); \
|
||||
STEP4(3); \
|
||||
p += 16; \
|
||||
} while (0)
|
||||
|
||||
// Process 4 bytes that were already loaded into a word.
|
||||
#define STEP4W(w) \
|
||||
do { \
|
||||
w ^= l; \
|
||||
for (size_t i = 0; i < 4; ++i) { \
|
||||
w = (w >> 8) ^ kByteExtensionTable[w & 0xff]; \
|
||||
} \
|
||||
l = w; \
|
||||
} while (0)
|
||||
|
||||
// Point x at first 4-byte aligned byte in the buffer. This might be past the
|
||||
// end of the buffer.
|
||||
const uint8_t* x = RoundUp<4>(p);
|
||||
if (x <= e) {
|
||||
// Process bytes p is 4-byte aligned.
|
||||
while (p != x) {
|
||||
STEP1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((e - p) >= 16) {
|
||||
// Load a 16-byte swath into the stride partial results.
|
||||
uint32_t crc0 = ReadUint32LE(p + 0 * 4) ^ l;
|
||||
uint32_t crc1 = ReadUint32LE(p + 1 * 4);
|
||||
uint32_t crc2 = ReadUint32LE(p + 2 * 4);
|
||||
uint32_t crc3 = ReadUint32LE(p + 3 * 4);
|
||||
p += 16;
|
||||
|
||||
while ((e - p) > kPrefetchHorizon) {
|
||||
RequestPrefetch(p + kPrefetchHorizon);
|
||||
|
||||
// Process 64 bytes at a time.
|
||||
STEP16;
|
||||
STEP16;
|
||||
STEP16;
|
||||
STEP16;
|
||||
}
|
||||
|
||||
// Process one 16-byte swath at a time.
|
||||
while ((e - p) >= 16) {
|
||||
STEP16;
|
||||
}
|
||||
|
||||
// Advance one word at a time as far as possible.
|
||||
while ((e - p) >= 4) {
|
||||
STEP4(0);
|
||||
uint32_t tmp = crc0;
|
||||
crc0 = crc1;
|
||||
crc1 = crc2;
|
||||
crc2 = crc3;
|
||||
crc3 = tmp;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
// Combine the 4 partial stride results.
|
||||
l = 0;
|
||||
STEP4W(crc0);
|
||||
STEP4W(crc1);
|
||||
STEP4W(crc2);
|
||||
STEP4W(crc3);
|
||||
}
|
||||
|
||||
// Process the last few bytes.
|
||||
while (p != e) {
|
||||
STEP1;
|
||||
}
|
||||
#undef STEP4W
|
||||
#undef STEP16
|
||||
#undef STEP4
|
||||
#undef STEP1
|
||||
return l ^ kCRC32Xor;
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
20
src/crc32c/src/crc32c_portable_unittest.cc
Normal file
20
src/crc32c/src/crc32c_portable_unittest.cc
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2017 The CRC32C 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 "gtest/gtest.h"
|
||||
|
||||
#include "./crc32c_extend_unittests.h"
|
||||
#include "./crc32c_internal.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
struct PortableTestTraits {
|
||||
static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
|
||||
return ExtendPortable(crc, data, count);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(Portable, ExtendTest, PortableTestTraits);
|
||||
|
||||
} // namespace crc32c
|
46
src/crc32c/src/crc32c_prefetch.h
Normal file
46
src/crc32c/src/crc32c_prefetch.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_PREFETCH_H_
|
||||
#define CRC32C_CRC32C_PREFETCH_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_MM_PREFETCH
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else // !defined(_MSC_VER)
|
||||
#include <xmmintrin.h>
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
#endif // HAVE_MM_PREFETCH
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Ask the hardware to prefetch the data at the given address into the L1 cache.
|
||||
inline void RequestPrefetch(const uint8_t* address) {
|
||||
#if HAVE_BUILTIN_PREFETCH
|
||||
// Clang and GCC implement the __builtin_prefetch non-standard extension,
|
||||
// which maps to the best instruction on the target architecture.
|
||||
__builtin_prefetch(reinterpret_cast<const char*>(address), 0 /* Read only. */,
|
||||
0 /* No temporal locality. */);
|
||||
#elif HAVE_MM_PREFETCH
|
||||
// Visual Studio doesn't implement __builtin_prefetch, but exposes the
|
||||
// PREFETCHNTA instruction via the _mm_prefetch intrinsic.
|
||||
_mm_prefetch(reinterpret_cast<const char*>(address), _MM_HINT_NTA);
|
||||
#else
|
||||
// No prefetch support. Silence compiler warnings.
|
||||
(void)address;
|
||||
#endif // HAVE_BUILTIN_PREFETCH
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // CRC32C_CRC32C_ROUND_UP_H_
|
9
src/crc32c/src/crc32c_prefetch_unittest.cc
Normal file
9
src/crc32c/src/crc32c_prefetch_unittest.cc
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2017 The CRC32C 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 "./crc32c_prefetch.h"
|
||||
|
||||
// There is no easy way to test cache prefetching. We can only test that the
|
||||
// crc32c_prefetch.h header compiles on its own, so it doesn't have any unstated
|
||||
// dependencies.
|
53
src/crc32c/src/crc32c_read_le.h
Normal file
53
src/crc32c/src/crc32c_read_le.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_READ_LE_H_
|
||||
#define CRC32C_CRC32C_READ_LE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Reads a little-endian 32-bit integer from a 32-bit-aligned buffer.
|
||||
inline uint32_t ReadUint32LE(const uint8_t* buffer) {
|
||||
#if BYTE_ORDER_BIG_ENDIAN
|
||||
return ((static_cast<uint32_t>(static_cast<uint8_t>(buffer[0]))) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[1])) << 8) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[2])) << 16) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[3])) << 24));
|
||||
#else // !BYTE_ORDER_BIG_ENDIAN
|
||||
uint32_t result;
|
||||
// This should be optimized to a single instruction.
|
||||
std::memcpy(&result, buffer, sizeof(result));
|
||||
return result;
|
||||
#endif // BYTE_ORDER_BIG_ENDIAN
|
||||
}
|
||||
|
||||
// Reads a little-endian 64-bit integer from a 64-bit-aligned buffer.
|
||||
inline uint64_t ReadUint64LE(const uint8_t* buffer) {
|
||||
#if BYTE_ORDER_BIG_ENDIAN
|
||||
return ((static_cast<uint32_t>(static_cast<uint8_t>(buffer[0]))) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[1])) << 8) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[2])) << 16) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[3])) << 24) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[4])) << 32) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[5])) << 40) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[6])) << 48) |
|
||||
(static_cast<uint32_t>(static_cast<uint8_t>(buffer[7])) << 56));
|
||||
#else // !BYTE_ORDER_BIG_ENDIAN
|
||||
uint64_t result;
|
||||
// This should be optimized to a single instruction.
|
||||
std::memcpy(&result, buffer, sizeof(result));
|
||||
return result;
|
||||
#endif // BYTE_ORDER_BIG_ENDIAN
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // CRC32C_CRC32C_READ_LE_H_
|
32
src/crc32c/src/crc32c_read_le_unittest.cc
Normal file
32
src/crc32c/src/crc32c_read_le_unittest.cc
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2017 The CRC32C 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 "./crc32c_read_le.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "./crc32c_round_up.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
TEST(Crc32CReadLETest, ReadUint32LE) {
|
||||
// little-endian 0x12345678
|
||||
alignas(4) uint8_t bytes[] = {0x78, 0x56, 0x34, 0x12};
|
||||
|
||||
ASSERT_EQ(RoundUp<4>(bytes), bytes) << "Stack array is not aligned";
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x12345678), ReadUint32LE(bytes));
|
||||
}
|
||||
|
||||
TEST(Crc32CReadLETest, ReadUint64LE) {
|
||||
// little-endian 0x123456789ABCDEF0
|
||||
alignas(8) uint8_t bytes[] = {0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12};
|
||||
|
||||
ASSERT_EQ(RoundUp<8>(bytes), bytes) << "Stack array is not aligned";
|
||||
EXPECT_EQ(static_cast<uint64_t>(0x123456789ABCDEF0), ReadUint64LE(bytes));
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
34
src/crc32c/src/crc32c_round_up.h
Normal file
34
src/crc32c/src/crc32c_round_up.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_ROUND_UP_H_
|
||||
#define CRC32C_CRC32C_ROUND_UP_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// Returns the smallest number >= the given number that is evenly divided by N.
|
||||
//
|
||||
// N must be a power of two.
|
||||
template <int N>
|
||||
constexpr inline uintptr_t RoundUp(uintptr_t pointer) {
|
||||
static_assert((N & (N - 1)) == 0, "N must be a power of two");
|
||||
return (pointer + (N - 1)) & ~(N - 1);
|
||||
}
|
||||
|
||||
// Returns the smallest address >= the given address that is aligned to N bytes.
|
||||
//
|
||||
// N must be a power of two.
|
||||
template <int N>
|
||||
constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) {
|
||||
static_assert((N & (N - 1)) == 0, "N must be a power of two");
|
||||
return reinterpret_cast<uint8_t*>(
|
||||
RoundUp<N>(reinterpret_cast<uintptr_t>(pointer)));
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // CRC32C_CRC32C_ROUND_UP_H_
|
84
src/crc32c/src/crc32c_round_up_unittest.cc
Normal file
84
src/crc32c/src/crc32c_round_up_unittest.cc
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2017 The CRC32C 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 "./crc32c_round_up.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
TEST(CRC32CRoundUpTest, RoundUpUintptr) {
|
||||
uintptr_t zero = 0;
|
||||
|
||||
ASSERT_EQ(zero, RoundUp<1>(zero));
|
||||
ASSERT_EQ(1U, RoundUp<1>(1U));
|
||||
ASSERT_EQ(2U, RoundUp<1>(2U));
|
||||
ASSERT_EQ(3U, RoundUp<1>(3U));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(0), RoundUp<1>(~static_cast<uintptr_t>(0)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(1), RoundUp<1>(~static_cast<uintptr_t>(1)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(2), RoundUp<1>(~static_cast<uintptr_t>(2)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<1>(~static_cast<uintptr_t>(3)));
|
||||
|
||||
ASSERT_EQ(zero, RoundUp<2>(zero));
|
||||
ASSERT_EQ(2U, RoundUp<2>(1U));
|
||||
ASSERT_EQ(2U, RoundUp<2>(2U));
|
||||
ASSERT_EQ(4U, RoundUp<2>(3U));
|
||||
ASSERT_EQ(4U, RoundUp<2>(4U));
|
||||
ASSERT_EQ(6U, RoundUp<2>(5U));
|
||||
ASSERT_EQ(6U, RoundUp<2>(6U));
|
||||
ASSERT_EQ(8U, RoundUp<2>(7U));
|
||||
ASSERT_EQ(8U, RoundUp<2>(8U));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(1), RoundUp<2>(~static_cast<uintptr_t>(1)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(1), RoundUp<2>(~static_cast<uintptr_t>(2)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<2>(~static_cast<uintptr_t>(3)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<2>(~static_cast<uintptr_t>(4)));
|
||||
|
||||
ASSERT_EQ(zero, RoundUp<4>(zero));
|
||||
ASSERT_EQ(4U, RoundUp<4>(1U));
|
||||
ASSERT_EQ(4U, RoundUp<4>(2U));
|
||||
ASSERT_EQ(4U, RoundUp<4>(3U));
|
||||
ASSERT_EQ(4U, RoundUp<4>(4U));
|
||||
ASSERT_EQ(8U, RoundUp<4>(5U));
|
||||
ASSERT_EQ(8U, RoundUp<4>(6U));
|
||||
ASSERT_EQ(8U, RoundUp<4>(7U));
|
||||
ASSERT_EQ(8U, RoundUp<4>(8U));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<4>(~static_cast<uintptr_t>(3)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<4>(~static_cast<uintptr_t>(4)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<4>(~static_cast<uintptr_t>(5)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(3), RoundUp<4>(~static_cast<uintptr_t>(6)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(7), RoundUp<4>(~static_cast<uintptr_t>(7)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(7), RoundUp<4>(~static_cast<uintptr_t>(8)));
|
||||
ASSERT_EQ(~static_cast<uintptr_t>(7), RoundUp<4>(~static_cast<uintptr_t>(9)));
|
||||
}
|
||||
|
||||
TEST(CRC32CRoundUpTest, RoundUpPointer) {
|
||||
uintptr_t zero = 0, three = 3, four = 4, seven = 7, eight = 8;
|
||||
|
||||
const uint8_t* zero_ptr = reinterpret_cast<const uint8_t*>(zero);
|
||||
const uint8_t* three_ptr = reinterpret_cast<const uint8_t*>(three);
|
||||
const uint8_t* four_ptr = reinterpret_cast<const uint8_t*>(four);
|
||||
const uint8_t* seven_ptr = reinterpret_cast<const uint8_t*>(seven);
|
||||
const uint8_t* eight_ptr = reinterpret_cast<uint8_t*>(eight);
|
||||
|
||||
ASSERT_EQ(zero_ptr, RoundUp<1>(zero_ptr));
|
||||
ASSERT_EQ(zero_ptr, RoundUp<4>(zero_ptr));
|
||||
ASSERT_EQ(zero_ptr, RoundUp<8>(zero_ptr));
|
||||
|
||||
ASSERT_EQ(three_ptr, RoundUp<1>(three_ptr));
|
||||
ASSERT_EQ(four_ptr, RoundUp<4>(three_ptr));
|
||||
ASSERT_EQ(eight_ptr, RoundUp<8>(three_ptr));
|
||||
|
||||
ASSERT_EQ(four_ptr, RoundUp<1>(four_ptr));
|
||||
ASSERT_EQ(four_ptr, RoundUp<4>(four_ptr));
|
||||
ASSERT_EQ(eight_ptr, RoundUp<8>(four_ptr));
|
||||
|
||||
ASSERT_EQ(seven_ptr, RoundUp<1>(seven_ptr));
|
||||
ASSERT_EQ(eight_ptr, RoundUp<4>(seven_ptr));
|
||||
ASSERT_EQ(eight_ptr, RoundUp<8>(four_ptr));
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
258
src/crc32c/src/crc32c_sse42.cc
Normal file
258
src/crc32c/src/crc32c_sse42.cc
Normal file
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2008 The CRC32C 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 "./crc32c_sse42.h"
|
||||
|
||||
// In a separate source file to allow this accelerated CRC32C function to be
|
||||
// compiled with the appropriate compiler flags to enable SSE4.2 instructions.
|
||||
|
||||
// This implementation is loosely based on Intel Pub 323405 from April 2011,
|
||||
// "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction".
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "./crc32c_internal.h"
|
||||
#include "./crc32c_prefetch.h"
|
||||
#include "./crc32c_read_le.h"
|
||||
#include "./crc32c_round_up.h"
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#else // !defined(_MSC_VER)
|
||||
#include <nmmintrin.h>
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const ptrdiff_t kGroups = 3;
|
||||
constexpr const ptrdiff_t kBlock0Size = 16 * 1024 / kGroups / 64 * 64;
|
||||
constexpr const ptrdiff_t kBlock1Size = 4 * 1024 / kGroups / 8 * 8;
|
||||
constexpr const ptrdiff_t kBlock2Size = 1024 / kGroups / 8 * 8;
|
||||
|
||||
const uint32_t kBlock0SkipTable[8][16] = {
|
||||
{0x00000000, 0xff770459, 0xfb027e43, 0x04757a1a, 0xf3e88a77, 0x0c9f8e2e,
|
||||
0x08eaf434, 0xf79df06d, 0xe23d621f, 0x1d4a6646, 0x193f1c5c, 0xe6481805,
|
||||
0x11d5e868, 0xeea2ec31, 0xead7962b, 0x15a09272},
|
||||
{0x00000000, 0xc196b2cf, 0x86c1136f, 0x4757a1a0, 0x086e502f, 0xc9f8e2e0,
|
||||
0x8eaf4340, 0x4f39f18f, 0x10dca05e, 0xd14a1291, 0x961db331, 0x578b01fe,
|
||||
0x18b2f071, 0xd92442be, 0x9e73e31e, 0x5fe551d1},
|
||||
{0x00000000, 0x21b940bc, 0x43728178, 0x62cbc1c4, 0x86e502f0, 0xa75c424c,
|
||||
0xc5978388, 0xe42ec334, 0x08267311, 0x299f33ad, 0x4b54f269, 0x6aedb2d5,
|
||||
0x8ec371e1, 0xaf7a315d, 0xcdb1f099, 0xec08b025},
|
||||
{0x00000000, 0x104ce622, 0x2099cc44, 0x30d52a66, 0x41339888, 0x517f7eaa,
|
||||
0x61aa54cc, 0x71e6b2ee, 0x82673110, 0x922bd732, 0xa2fefd54, 0xb2b21b76,
|
||||
0xc354a998, 0xd3184fba, 0xe3cd65dc, 0xf38183fe},
|
||||
{0x00000000, 0x012214d1, 0x024429a2, 0x03663d73, 0x04885344, 0x05aa4795,
|
||||
0x06cc7ae6, 0x07ee6e37, 0x0910a688, 0x0832b259, 0x0b548f2a, 0x0a769bfb,
|
||||
0x0d98f5cc, 0x0cbae11d, 0x0fdcdc6e, 0x0efec8bf},
|
||||
{0x00000000, 0x12214d10, 0x24429a20, 0x3663d730, 0x48853440, 0x5aa47950,
|
||||
0x6cc7ae60, 0x7ee6e370, 0x910a6880, 0x832b2590, 0xb548f2a0, 0xa769bfb0,
|
||||
0xd98f5cc0, 0xcbae11d0, 0xfdcdc6e0, 0xefec8bf0},
|
||||
{0x00000000, 0x27f8a7f1, 0x4ff14fe2, 0x6809e813, 0x9fe29fc4, 0xb81a3835,
|
||||
0xd013d026, 0xf7eb77d7, 0x3a294979, 0x1dd1ee88, 0x75d8069b, 0x5220a16a,
|
||||
0xa5cbd6bd, 0x8233714c, 0xea3a995f, 0xcdc23eae},
|
||||
{0x00000000, 0x745292f2, 0xe8a525e4, 0x9cf7b716, 0xd4a63d39, 0xa0f4afcb,
|
||||
0x3c0318dd, 0x48518a2f, 0xaca00c83, 0xd8f29e71, 0x44052967, 0x3057bb95,
|
||||
0x780631ba, 0x0c54a348, 0x90a3145e, 0xe4f186ac},
|
||||
};
|
||||
const uint32_t kBlock1SkipTable[8][16] = {
|
||||
{0x00000000, 0x79113270, 0xf22264e0, 0x8b335690, 0xe1a8bf31, 0x98b98d41,
|
||||
0x138adbd1, 0x6a9be9a1, 0xc6bd0893, 0xbfac3ae3, 0x349f6c73, 0x4d8e5e03,
|
||||
0x2715b7a2, 0x5e0485d2, 0xd537d342, 0xac26e132},
|
||||
{0x00000000, 0x889667d7, 0x14c0b95f, 0x9c56de88, 0x298172be, 0xa1171569,
|
||||
0x3d41cbe1, 0xb5d7ac36, 0x5302e57c, 0xdb9482ab, 0x47c25c23, 0xcf543bf4,
|
||||
0x7a8397c2, 0xf215f015, 0x6e432e9d, 0xe6d5494a},
|
||||
{0x00000000, 0xa605caf8, 0x49e7e301, 0xefe229f9, 0x93cfc602, 0x35ca0cfa,
|
||||
0xda282503, 0x7c2deffb, 0x2273faf5, 0x8476300d, 0x6b9419f4, 0xcd91d30c,
|
||||
0xb1bc3cf7, 0x17b9f60f, 0xf85bdff6, 0x5e5e150e},
|
||||
{0x00000000, 0x44e7f5ea, 0x89cfebd4, 0xcd281e3e, 0x1673a159, 0x529454b3,
|
||||
0x9fbc4a8d, 0xdb5bbf67, 0x2ce742b2, 0x6800b758, 0xa528a966, 0xe1cf5c8c,
|
||||
0x3a94e3eb, 0x7e731601, 0xb35b083f, 0xf7bcfdd5},
|
||||
{0x00000000, 0x59ce8564, 0xb39d0ac8, 0xea538fac, 0x62d66361, 0x3b18e605,
|
||||
0xd14b69a9, 0x8885eccd, 0xc5acc6c2, 0x9c6243a6, 0x7631cc0a, 0x2fff496e,
|
||||
0xa77aa5a3, 0xfeb420c7, 0x14e7af6b, 0x4d292a0f},
|
||||
{0x00000000, 0x8eb5fb75, 0x1887801b, 0x96327b6e, 0x310f0036, 0xbfbafb43,
|
||||
0x2988802d, 0xa73d7b58, 0x621e006c, 0xecabfb19, 0x7a998077, 0xf42c7b02,
|
||||
0x5311005a, 0xdda4fb2f, 0x4b968041, 0xc5237b34},
|
||||
{0x00000000, 0xc43c00d8, 0x8d947741, 0x49a87799, 0x1ec49873, 0xdaf898ab,
|
||||
0x9350ef32, 0x576cefea, 0x3d8930e6, 0xf9b5303e, 0xb01d47a7, 0x7421477f,
|
||||
0x234da895, 0xe771a84d, 0xaed9dfd4, 0x6ae5df0c},
|
||||
{0x00000000, 0x7b1261cc, 0xf624c398, 0x8d36a254, 0xe9a5f1c1, 0x92b7900d,
|
||||
0x1f813259, 0x64935395, 0xd6a79573, 0xadb5f4bf, 0x208356eb, 0x5b913727,
|
||||
0x3f0264b2, 0x4410057e, 0xc926a72a, 0xb234c6e6},
|
||||
};
|
||||
const uint32_t kBlock2SkipTable[8][16] = {
|
||||
{0x00000000, 0x8f158014, 0x1bc776d9, 0x94d2f6cd, 0x378eedb2, 0xb89b6da6,
|
||||
0x2c499b6b, 0xa35c1b7f, 0x6f1ddb64, 0xe0085b70, 0x74daadbd, 0xfbcf2da9,
|
||||
0x589336d6, 0xd786b6c2, 0x4354400f, 0xcc41c01b},
|
||||
{0x00000000, 0xde3bb6c8, 0xb99b1b61, 0x67a0ada9, 0x76da4033, 0xa8e1f6fb,
|
||||
0xcf415b52, 0x117aed9a, 0xedb48066, 0x338f36ae, 0x542f9b07, 0x8a142dcf,
|
||||
0x9b6ec055, 0x4555769d, 0x22f5db34, 0xfcce6dfc},
|
||||
{0x00000000, 0xde85763d, 0xb8e69a8b, 0x6663ecb6, 0x742143e7, 0xaaa435da,
|
||||
0xccc7d96c, 0x1242af51, 0xe84287ce, 0x36c7f1f3, 0x50a41d45, 0x8e216b78,
|
||||
0x9c63c429, 0x42e6b214, 0x24855ea2, 0xfa00289f},
|
||||
{0x00000000, 0xd569796d, 0xaf3e842b, 0x7a57fd46, 0x5b917ea7, 0x8ef807ca,
|
||||
0xf4affa8c, 0x21c683e1, 0xb722fd4e, 0x624b8423, 0x181c7965, 0xcd750008,
|
||||
0xecb383e9, 0x39dafa84, 0x438d07c2, 0x96e47eaf},
|
||||
{0x00000000, 0x6ba98c6d, 0xd75318da, 0xbcfa94b7, 0xab4a4745, 0xc0e3cb28,
|
||||
0x7c195f9f, 0x17b0d3f2, 0x5378f87b, 0x38d17416, 0x842be0a1, 0xef826ccc,
|
||||
0xf832bf3e, 0x939b3353, 0x2f61a7e4, 0x44c82b89},
|
||||
{0x00000000, 0xa6f1f0f6, 0x480f971d, 0xeefe67eb, 0x901f2e3a, 0x36eedecc,
|
||||
0xd810b927, 0x7ee149d1, 0x25d22a85, 0x8323da73, 0x6dddbd98, 0xcb2c4d6e,
|
||||
0xb5cd04bf, 0x133cf449, 0xfdc293a2, 0x5b336354},
|
||||
{0x00000000, 0x4ba4550a, 0x9748aa14, 0xdcecff1e, 0x2b7d22d9, 0x60d977d3,
|
||||
0xbc3588cd, 0xf791ddc7, 0x56fa45b2, 0x1d5e10b8, 0xc1b2efa6, 0x8a16baac,
|
||||
0x7d87676b, 0x36233261, 0xeacfcd7f, 0xa16b9875},
|
||||
{0x00000000, 0xadf48b64, 0x5e056039, 0xf3f1eb5d, 0xbc0ac072, 0x11fe4b16,
|
||||
0xe20fa04b, 0x4ffb2b2f, 0x7df9f615, 0xd00d7d71, 0x23fc962c, 0x8e081d48,
|
||||
0xc1f33667, 0x6c07bd03, 0x9ff6565e, 0x3202dd3a},
|
||||
};
|
||||
|
||||
constexpr const ptrdiff_t kPrefetchHorizon = 256;
|
||||
|
||||
} // namespace
|
||||
|
||||
uint32_t ExtendSse42(uint32_t crc, const uint8_t* data, size_t size) {
|
||||
const uint8_t* p = data;
|
||||
const uint8_t* e = data + size;
|
||||
uint32_t l = crc ^ kCRC32Xor;
|
||||
|
||||
#define STEP1 \
|
||||
do { \
|
||||
l = _mm_crc32_u8(l, *p++); \
|
||||
} while (0)
|
||||
|
||||
#define STEP4(crc) \
|
||||
do { \
|
||||
crc = _mm_crc32_u32(crc, ReadUint32LE(p)); \
|
||||
p += 4; \
|
||||
} while (0)
|
||||
|
||||
#define STEP8(crc, data) \
|
||||
do { \
|
||||
crc = _mm_crc32_u64(crc, ReadUint64LE(data)); \
|
||||
data += 8; \
|
||||
} while (0)
|
||||
|
||||
#define STEP8BY3(crc0, crc1, crc2, p0, p1, p2) \
|
||||
do { \
|
||||
STEP8(crc0, p0); \
|
||||
STEP8(crc1, p1); \
|
||||
STEP8(crc2, p2); \
|
||||
} while (0)
|
||||
|
||||
#define STEP8X3(crc0, crc1, crc2, bs) \
|
||||
do { \
|
||||
crc0 = _mm_crc32_u64(crc0, ReadUint64LE(p)); \
|
||||
crc1 = _mm_crc32_u64(crc1, ReadUint64LE(p + bs)); \
|
||||
crc2 = _mm_crc32_u64(crc2, ReadUint64LE(p + 2 * bs)); \
|
||||
p += 8; \
|
||||
} while (0)
|
||||
|
||||
#define SKIP_BLOCK(crc, tab) \
|
||||
do { \
|
||||
crc = tab[0][crc & 0xf] ^ tab[1][(crc >> 4) & 0xf] ^ \
|
||||
tab[2][(crc >> 8) & 0xf] ^ tab[3][(crc >> 12) & 0xf] ^ \
|
||||
tab[4][(crc >> 16) & 0xf] ^ tab[5][(crc >> 20) & 0xf] ^ \
|
||||
tab[6][(crc >> 24) & 0xf] ^ tab[7][(crc >> 28) & 0xf]; \
|
||||
} while (0)
|
||||
|
||||
// Point x at first 8-byte aligned byte in the buffer. This might be past the
|
||||
// end of the buffer.
|
||||
const uint8_t* x = RoundUp<8>(p);
|
||||
if (x <= e) {
|
||||
// Process bytes p is 8-byte aligned.
|
||||
while (p != x) {
|
||||
STEP1;
|
||||
}
|
||||
}
|
||||
|
||||
// Proccess the data in predetermined block sizes with tables for quickly
|
||||
// combining the checksum. Experimentally it's better to use larger block
|
||||
// sizes where possible so use a hierarchy of decreasing block sizes.
|
||||
uint64_t l64 = l;
|
||||
while ((e - p) >= kGroups * kBlock0Size) {
|
||||
uint64_t l641 = 0;
|
||||
uint64_t l642 = 0;
|
||||
for (int i = 0; i < kBlock0Size; i += 8 * 8) {
|
||||
// Prefetch ahead to hide latency.
|
||||
RequestPrefetch(p + kPrefetchHorizon);
|
||||
RequestPrefetch(p + kBlock0Size + kPrefetchHorizon);
|
||||
RequestPrefetch(p + 2 * kBlock0Size + kPrefetchHorizon);
|
||||
|
||||
// Process 64 bytes at a time.
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
STEP8X3(l64, l641, l642, kBlock0Size);
|
||||
}
|
||||
|
||||
// Combine results.
|
||||
SKIP_BLOCK(l64, kBlock0SkipTable);
|
||||
l64 ^= l641;
|
||||
SKIP_BLOCK(l64, kBlock0SkipTable);
|
||||
l64 ^= l642;
|
||||
p += (kGroups - 1) * kBlock0Size;
|
||||
}
|
||||
while ((e - p) >= kGroups * kBlock1Size) {
|
||||
uint64_t l641 = 0;
|
||||
uint64_t l642 = 0;
|
||||
for (int i = 0; i < kBlock1Size; i += 8) {
|
||||
STEP8X3(l64, l641, l642, kBlock1Size);
|
||||
}
|
||||
SKIP_BLOCK(l64, kBlock1SkipTable);
|
||||
l64 ^= l641;
|
||||
SKIP_BLOCK(l64, kBlock1SkipTable);
|
||||
l64 ^= l642;
|
||||
p += (kGroups - 1) * kBlock1Size;
|
||||
}
|
||||
while ((e - p) >= kGroups * kBlock2Size) {
|
||||
uint64_t l641 = 0;
|
||||
uint64_t l642 = 0;
|
||||
for (int i = 0; i < kBlock2Size; i += 8) {
|
||||
STEP8X3(l64, l641, l642, kBlock2Size);
|
||||
}
|
||||
SKIP_BLOCK(l64, kBlock2SkipTable);
|
||||
l64 ^= l641;
|
||||
SKIP_BLOCK(l64, kBlock2SkipTable);
|
||||
l64 ^= l642;
|
||||
p += (kGroups - 1) * kBlock2Size;
|
||||
}
|
||||
|
||||
// Process bytes 16 at a time
|
||||
while ((e - p) >= 16) {
|
||||
STEP8(l64, p);
|
||||
STEP8(l64, p);
|
||||
}
|
||||
|
||||
l = static_cast<uint32_t>(l64);
|
||||
// Process the last few bytes.
|
||||
while (p != e) {
|
||||
STEP1;
|
||||
}
|
||||
#undef SKIP_BLOCK
|
||||
#undef STEP8X3
|
||||
#undef STEP8BY3
|
||||
#undef STEP8
|
||||
#undef STEP4
|
||||
#undef STEP1
|
||||
|
||||
return l ^ kCRC32Xor;
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
33
src/crc32c/src/crc32c_sse42.h
Normal file
33
src/crc32c/src/crc32c_sse42.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_SSE42_H_
|
||||
#define CRC32C_CRC32C_SSE42_H_
|
||||
|
||||
// X86-specific code.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
// The hardware-accelerated implementation is only enabled for 64-bit builds,
|
||||
// because a straightforward 32-bit implementation actually runs slower than the
|
||||
// portable version. Most X86 machines are 64-bit nowadays, so it doesn't make
|
||||
// much sense to spend time building an optimized hardware-accelerated
|
||||
// implementation.
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
// SSE4.2-accelerated implementation in crc32c_sse42.cc
|
||||
uint32_t ExtendSse42(uint32_t crc, const uint8_t* data, size_t count);
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
#endif // CRC32C_CRC32C_SSE42_H_
|
50
src/crc32c/src/crc32c_sse42_check.h
Normal file
50
src/crc32c/src/crc32c_sse42_check.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef CRC32C_CRC32C_SSE42_CHECK_H_
|
||||
#define CRC32C_CRC32C_SSE42_CHECK_H_
|
||||
|
||||
// X86-specific code checking the availability of SSE4.2 instructions.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
// If the compiler supports SSE4.2, it definitely supports X86.
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
inline bool CanUseSse42() {
|
||||
int cpu_info[4];
|
||||
__cpuid(cpu_info, 1);
|
||||
return (cpu_info[2] & (1 << 20)) != 0;
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#else // !defined(_MSC_VER)
|
||||
#include <cpuid.h>
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
inline bool CanUseSse42() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
return __get_cpuid(1, &eax, &ebx, &ecx, &edx) && ((ecx & (1 << 20)) != 0);
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
#endif // CRC32C_CRC32C_SSE42_CHECK_H_
|
24
src/crc32c/src/crc32c_sse42_unittest.cc
Normal file
24
src/crc32c/src/crc32c_sse42_unittest.cc
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 The CRC32C 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 "gtest/gtest.h"
|
||||
|
||||
#include "./crc32c_extend_unittests.h"
|
||||
#include "./crc32c_sse42.h"
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
struct Sse42TestTraits {
|
||||
static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
|
||||
return ExtendSse42(crc, data, count);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(Sse42, ExtendTest, Sse42TestTraits);
|
||||
|
||||
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
|
||||
|
||||
} // namespace crc32c
|
22
src/crc32c/src/crc32c_test_main.cc
Normal file
22
src/crc32c/src/crc32c_test_main.cc
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2017 The CRC32C Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifdef CRC32C_HAVE_CONFIG_H
|
||||
#include "crc32c/crc32c_config.h"
|
||||
#endif
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#if CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
#include "glog/logging.h"
|
||||
#endif // CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#if CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
google::InstallFailureSignalHandler();
|
||||
#endif // CRC32C_TESTS_BUILT_WITH_GLOG
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
129
src/crc32c/src/crc32c_unittest.cc
Normal file
129
src/crc32c/src/crc32c_unittest.cc
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2017 The CRC32C 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 "crc32c/crc32c.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "./crc32c_extend_unittests.h"
|
||||
|
||||
TEST(Crc32CTest, Crc32c) {
|
||||
// From rfc3720 section B.4.
|
||||
uint8_t buf[32];
|
||||
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x8a9136aa),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
std::memset(buf, 0xff, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x62a8ab43),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x46dd794e),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(31 - i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x113fdb5c),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
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,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
EXPECT_EQ(static_cast<uint32_t>(0xd9963a56),
|
||||
crc32c::Crc32c(data, sizeof(data)));
|
||||
}
|
||||
|
||||
namespace crc32c {
|
||||
|
||||
struct ApiTestTraits {
|
||||
static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
|
||||
return ::crc32c::Extend(crc, data, count);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(Api, ExtendTest, ApiTestTraits);
|
||||
|
||||
} // namespace crc32c
|
||||
|
||||
TEST(CRC32CTest, Crc32cCharPointer) {
|
||||
char buf[32];
|
||||
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x8a9136aa),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
std::memset(buf, 0xff, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x62a8ab43),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<char>(i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x46dd794e),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<char>(31 - i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x113fdb5c),
|
||||
crc32c::Crc32c(buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
TEST(CRC32CTest, Crc32cStdString) {
|
||||
std::string buf;
|
||||
buf.resize(32);
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<char>(0x00);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x8a9136aa), crc32c::Crc32c(buf));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = '\xff';
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x62a8ab43), crc32c::Crc32c(buf));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<char>(i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x46dd794e), crc32c::Crc32c(buf));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<char>(31 - i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x113fdb5c), crc32c::Crc32c(buf));
|
||||
}
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
#if __has_include(<string_view>)
|
||||
|
||||
TEST(CRC32CTest, Crc32cStdStringView) {
|
||||
uint8_t buf[32];
|
||||
std::string_view view(reinterpret_cast<const char*>(buf), sizeof(buf));
|
||||
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x8a9136aa), crc32c::Crc32c(view));
|
||||
|
||||
std::memset(buf, 0xff, sizeof(buf));
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x62a8ab43), crc32c::Crc32c(view));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x46dd794e), crc32c::Crc32c(view));
|
||||
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
buf[i] = static_cast<uint8_t>(31 - i);
|
||||
EXPECT_EQ(static_cast<uint32_t>(0x113fdb5c), crc32c::Crc32c(view));
|
||||
}
|
||||
|
||||
#endif // __has_include(<string_view>)
|
||||
#endif // __cplusplus > 201402L
|
||||
|
||||
#define TESTED_EXTEND Extend
|
||||
#include "./crc32c_extend_unittests.h"
|
||||
#undef TESTED_EXTEND
|
35
src/leveldb/.appveyor.yml
Normal file
35
src/leveldb/.appveyor.yml
Normal file
|
@ -0,0 +1,35 @@
|
|||
# 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 ..
|
18
src/leveldb/.clang-format
Normal file
18
src/leveldb/.clang-format
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Run manually to reformat a file:
|
||||
# clang-format -i --style=file <file>
|
||||
# find . -iname '*.cc' -o -iname '*.h' -o -iname '*.h.in' | xargs clang-format -i --style=file
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
|
||||
# Public headers are in a different location in the internal Google repository.
|
||||
# Order them so that when imported to the authoritative repository they will be
|
||||
# in correct alphabetical order.
|
||||
IncludeCategories:
|
||||
- Regex: '^(<|"(benchmarks|db|helpers)/)'
|
||||
Priority: 1
|
||||
- Regex: '^"(leveldb)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(issues|port|table|third_party|util)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 4
|
21
src/leveldb/.gitignore
vendored
21
src/leveldb/.gitignore
vendored
|
@ -1,13 +1,8 @@
|
|||
build_config.mk
|
||||
*.a
|
||||
*.o
|
||||
*.dylib*
|
||||
*.so
|
||||
*.so.*
|
||||
*_test
|
||||
db_bench
|
||||
leveldbutil
|
||||
Release
|
||||
Debug
|
||||
Benchmark
|
||||
vs2010.*
|
||||
# Editors.
|
||||
*.sw*
|
||||
.vscode
|
||||
.DS_Store
|
||||
|
||||
# Build directory.
|
||||
build/
|
||||
out/
|
||||
|
|
|
@ -1,13 +1,82 @@
|
|||
# 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: xcode10.3
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
- clang
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
sudo: false
|
||||
before_install:
|
||||
- echo $LANG
|
||||
- echo $LC_ALL
|
||||
|
||||
env:
|
||||
- BUILD_TYPE=Debug
|
||||
- BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
- sourceline: 'ppa:ubuntu-toolchain-r/test'
|
||||
packages:
|
||||
- clang-9
|
||||
- cmake
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libgoogle-perftools-dev
|
||||
- libkyotocabinet-dev
|
||||
- libsnappy-dev
|
||||
- libsqlite3-dev
|
||||
- ninja-build
|
||||
homebrew:
|
||||
packages:
|
||||
- cmake
|
||||
- crc32c
|
||||
- gcc@9
|
||||
- gperftools
|
||||
- kyoto-cabinet
|
||||
- llvm@9
|
||||
- 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++-9" CC="gcc-9"; 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++-9" CC="clang-9"; 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:
|
||||
- make -j 4 check
|
||||
- cd build && ctest --verbose && cd ..
|
||||
- "if [ -f build/db_bench ] ; then build/db_bench ; fi"
|
||||
- "if [ -f build/db_bench_sqlite3 ] ; then build/db_bench_sqlite3 ; fi"
|
||||
- "if [ -f build/db_bench_tree_db ] ; then build/db_bench_tree_db ; fi"
|
||||
- cd build && cmake --build . --target install
|
||||
|
|
465
src/leveldb/CMakeLists.txt
Normal file
465
src/leveldb/CMakeLists.txt
Normal file
|
@ -0,0 +1,465 @@
|
|||
# Copyright 2017 The LevelDB Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
# Keep the version below in sync with the one in db.h
|
||||
project(leveldb VERSION 1.22.0 LANGUAGES C CXX)
|
||||
|
||||
# This project can use C11, but will gracefully decay down to C89.
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED OFF)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
# This project requires C++11.
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if (WIN32)
|
||||
set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS)
|
||||
# TODO(cmumford): Make UNICODE configurable for Windows.
|
||||
add_definitions(-D_UNICODE -DUNICODE)
|
||||
else (WIN32)
|
||||
set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_POSIX)
|
||||
endif (WIN32)
|
||||
|
||||
option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON)
|
||||
option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON)
|
||||
option(LEVELDB_INSTALL "Install LevelDB's header and library" ON)
|
||||
|
||||
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(tcmalloc malloc "" HAVE_TCMALLOC)
|
||||
|
||||
include(CheckCXXSymbolExists)
|
||||
# Using check_cxx_symbol_exists() instead of check_c_symbol_exists() because
|
||||
# we're including the header from C++, and feature detection should use the same
|
||||
# compiler language that the project will use later. Principles aside, some
|
||||
# versions of do not expose fdatasync() in <unistd.h> in standard C mode
|
||||
# (-std=c11), but do expose the function in standard C++ mode (-std=c++11).
|
||||
check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC)
|
||||
check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC)
|
||||
check_cxx_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# Disable C++ exceptions.
|
||||
string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c-")
|
||||
add_definitions(-D_HAS_EXCEPTIONS=0)
|
||||
|
||||
# Disable RTTI.
|
||||
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# Enable strict prototype warnings for C code in clang and gcc.
|
||||
if(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
endif(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes")
|
||||
|
||||
# Disable C++ exceptions.
|
||||
string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
|
||||
# Disable RTTI.
|
||||
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
# Test whether -Wthread-safety is available. See
|
||||
# https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-Wthread-safety HAVE_CLANG_THREAD_SAFETY)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# Test whether C++17 __has_include is available.
|
||||
check_cxx_source_compiles("
|
||||
#if defined(__has_include) && __has_include(<string>)
|
||||
#include <string>
|
||||
#endif
|
||||
int main() { std::string str; return 0; }
|
||||
" HAVE_CXX17_HAS_INCLUDE)
|
||||
|
||||
set(LEVELDB_PUBLIC_INCLUDE_DIR "include/leveldb")
|
||||
set(LEVELDB_PORT_CONFIG_DIR "include/port")
|
||||
|
||||
configure_file(
|
||||
"port/port_config.h.in"
|
||||
"${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"."
|
||||
)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
# Only export LEVELDB_EXPORT symbols from the shared library.
|
||||
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"
|
||||
"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>
|
||||
"${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"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_sources(leveldb
|
||||
PRIVATE
|
||||
"util/env_windows.cc"
|
||||
"util/windows_logger.h"
|
||||
)
|
||||
else (WIN32)
|
||||
target_sources(leveldb
|
||||
PRIVATE
|
||||
"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
|
||||
"helpers/memenv/memenv.cc"
|
||||
"helpers/memenv/memenv.h"
|
||||
)
|
||||
|
||||
target_include_directories(leveldb
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
set_target_properties(leveldb
|
||||
PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
target_compile_definitions(leveldb
|
||||
PRIVATE
|
||||
# Used by include/export.h when building shared libraries.
|
||||
LEVELDB_COMPILE_LIBRARY
|
||||
# Used by port/port.h.
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
)
|
||||
if (NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
target_compile_definitions(leveldb
|
||||
PRIVATE
|
||||
LEVELDB_HAS_PORT_CONFIG_H=1
|
||||
)
|
||||
endif(NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(leveldb
|
||||
PUBLIC
|
||||
# Used by include/export.h.
|
||||
LEVELDB_SHARED_LIBRARY
|
||||
)
|
||||
endif(BUILD_SHARED_LIBS)
|
||||
|
||||
if(HAVE_CLANG_THREAD_SAFETY)
|
||||
target_compile_options(leveldb
|
||||
PUBLIC
|
||||
-Werror -Wthread-safety)
|
||||
endif(HAVE_CLANG_THREAD_SAFETY)
|
||||
|
||||
if(HAVE_CRC32C)
|
||||
target_link_libraries(leveldb crc32c)
|
||||
endif(HAVE_CRC32C)
|
||||
if(HAVE_SNAPPY)
|
||||
target_link_libraries(leveldb snappy)
|
||||
endif(HAVE_SNAPPY)
|
||||
if(HAVE_TCMALLOC)
|
||||
target_link_libraries(leveldb tcmalloc)
|
||||
endif(HAVE_TCMALLOC)
|
||||
|
||||
# Needed by port_stdcxx.h
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(leveldb Threads::Threads)
|
||||
|
||||
add_executable(leveldbutil
|
||||
"db/leveldbutil.cc"
|
||||
)
|
||||
target_link_libraries(leveldbutil leveldb)
|
||||
|
||||
if(LEVELDB_BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
function(leveldb_test test_file)
|
||||
get_filename_component(test_target_name "${test_file}" NAME_WE)
|
||||
|
||||
add_executable("${test_target_name}" "")
|
||||
target_sources("${test_target_name}"
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h"
|
||||
"util/testharness.cc"
|
||||
"util/testharness.h"
|
||||
"util/testutil.cc"
|
||||
"util/testutil.h"
|
||||
|
||||
"${test_file}"
|
||||
)
|
||||
target_link_libraries("${test_target_name}" leveldb)
|
||||
target_compile_definitions("${test_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
)
|
||||
if (NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
target_compile_definitions("${test_target_name}"
|
||||
PRIVATE
|
||||
LEVELDB_HAS_PORT_CONFIG_H=1
|
||||
)
|
||||
endif(NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
|
||||
add_test(NAME "${test_target_name}" COMMAND "${test_target_name}")
|
||||
endfunction(leveldb_test)
|
||||
|
||||
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)
|
||||
leveldb_test("util/env_windows_test.cc")
|
||||
else (WIN32)
|
||||
leveldb_test("util/env_posix_test.cc")
|
||||
endif (WIN32)
|
||||
endif(NOT BUILD_SHARED_LIBS)
|
||||
endif(LEVELDB_BUILD_TESTS)
|
||||
|
||||
if(LEVELDB_BUILD_BENCHMARKS)
|
||||
function(leveldb_benchmark bench_file)
|
||||
get_filename_component(bench_target_name "${bench_file}" NAME_WE)
|
||||
|
||||
add_executable("${bench_target_name}" "")
|
||||
target_sources("${bench_target_name}"
|
||||
PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h"
|
||||
"util/histogram.cc"
|
||||
"util/histogram.h"
|
||||
"util/testharness.cc"
|
||||
"util/testharness.h"
|
||||
"util/testutil.cc"
|
||||
"util/testutil.h"
|
||||
|
||||
"${bench_file}"
|
||||
)
|
||||
target_link_libraries("${bench_target_name}" leveldb)
|
||||
target_compile_definitions("${bench_target_name}"
|
||||
PRIVATE
|
||||
${LEVELDB_PLATFORM_NAME}=1
|
||||
)
|
||||
if (NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
target_compile_definitions("${bench_target_name}"
|
||||
PRIVATE
|
||||
LEVELDB_HAS_PORT_CONFIG_H=1
|
||||
)
|
||||
endif(NOT HAVE_CXX17_HAS_INCLUDE)
|
||||
endfunction(leveldb_benchmark)
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
leveldb_benchmark("benchmarks/db_bench.cc")
|
||||
endif(NOT BUILD_SHARED_LIBS)
|
||||
|
||||
check_library_exists(sqlite3 sqlite3_open "" HAVE_SQLITE3)
|
||||
if(HAVE_SQLITE3)
|
||||
leveldb_benchmark("benchmarks/db_bench_sqlite3.cc")
|
||||
target_link_libraries(db_bench_sqlite3 sqlite3)
|
||||
endif(HAVE_SQLITE3)
|
||||
|
||||
# check_library_exists is insufficient here because the library names have
|
||||
# different manglings when compiled with clang or gcc, at least when installed
|
||||
# with Homebrew on Mac.
|
||||
set(OLD_CMAKE_REQURED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES kyotocabinet)
|
||||
check_cxx_source_compiles("
|
||||
#include <kcpolydb.h>
|
||||
|
||||
int main() {
|
||||
kyotocabinet::TreeDB* db = new kyotocabinet::TreeDB();
|
||||
delete db;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_KYOTOCABINET)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQURED_LIBRARIES})
|
||||
if(HAVE_KYOTOCABINET)
|
||||
leveldb_benchmark("benchmarks/db_bench_tree_db.cc")
|
||||
target_link_libraries(db_bench_tree_db kyotocabinet)
|
||||
endif(HAVE_KYOTOCABINET)
|
||||
endif(LEVELDB_BUILD_BENCHMARKS)
|
||||
|
||||
if(LEVELDB_INSTALL)
|
||||
install(TARGETS leveldb
|
||||
EXPORT leveldbTargets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"${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)
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
install(
|
||||
EXPORT leveldbTargets
|
||||
NAMESPACE leveldb::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb"
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"cmake/leveldbConfig.cmake"
|
||||
"${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb"
|
||||
)
|
||||
endif(LEVELDB_INSTALL)
|
|
@ -31,6 +31,6 @@ the CLA.
|
|||
|
||||
## Writing Code ##
|
||||
|
||||
If your contribution contains code, please make sure that it follows
|
||||
[the style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml).
|
||||
If your contribution contains code, please make sure that it follows
|
||||
[the style guide](http://google.github.io/styleguide/cppguide.html).
|
||||
Otherwise we will have to ask you to make changes, and that's no fun for anyone.
|
||||
|
|
|
@ -1,424 +0,0 @@
|
|||
# Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#-----------------------------------------------
|
||||
# Uncomment exactly one of the lines labelled (A), (B), and (C) below
|
||||
# to switch between compilation modes.
|
||||
|
||||
# (A) Production use (optimized mode)
|
||||
OPT ?= -O2 -DNDEBUG
|
||||
# (B) Debug mode, w/ full line-level debugging symbols
|
||||
# OPT ?= -g2
|
||||
# (C) Profiling mode: opt, but w/debugging symbols
|
||||
# OPT ?= -O2 -g2 -DNDEBUG
|
||||
#-----------------------------------------------
|
||||
|
||||
# detect what platform we're building on
|
||||
$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \
|
||||
./build_detect_platform build_config.mk ./)
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include build_config.mk
|
||||
|
||||
TESTS = \
|
||||
db/autocompact_test \
|
||||
db/c_test \
|
||||
db/corruption_test \
|
||||
db/db_test \
|
||||
db/dbformat_test \
|
||||
db/fault_injection_test \
|
||||
db/filename_test \
|
||||
db/log_test \
|
||||
db/recovery_test \
|
||||
db/skiplist_test \
|
||||
db/version_edit_test \
|
||||
db/version_set_test \
|
||||
db/write_batch_test \
|
||||
helpers/memenv/memenv_test \
|
||||
issues/issue178_test \
|
||||
issues/issue200_test \
|
||||
table/filter_block_test \
|
||||
table/table_test \
|
||||
util/arena_test \
|
||||
util/bloom_test \
|
||||
util/cache_test \
|
||||
util/coding_test \
|
||||
util/crc32c_test \
|
||||
util/env_posix_test \
|
||||
util/env_test \
|
||||
util/hash_test
|
||||
|
||||
UTILS = \
|
||||
db/db_bench \
|
||||
db/leveldbutil
|
||||
|
||||
# Put the object files in a subdirectory, but the application at the top of the object dir.
|
||||
PROGNAMES := $(notdir $(TESTS) $(UTILS))
|
||||
|
||||
# On Linux may need libkyotocabinet-dev for dependency.
|
||||
BENCHMARKS = \
|
||||
doc/bench/db_bench_sqlite3 \
|
||||
doc/bench/db_bench_tree_db
|
||||
|
||||
CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
LIBS += $(PLATFORM_LIBS)
|
||||
|
||||
SIMULATOR_OUTDIR=out-ios-x86
|
||||
DEVICE_OUTDIR=out-ios-arm
|
||||
|
||||
ifeq ($(PLATFORM), IOS)
|
||||
# Note: iOS should probably be using libtool, not ar.
|
||||
AR=xcrun ar
|
||||
SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path)
|
||||
DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path)
|
||||
DEVICE_CFLAGS = -isysroot "$(DEVICESDK)" -arch armv6 -arch armv7 -arch armv7s -arch arm64
|
||||
SIMULATOR_CFLAGS = -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64
|
||||
STATIC_OUTDIR=out-ios-universal
|
||||
else
|
||||
STATIC_OUTDIR=out-static
|
||||
SHARED_OUTDIR=out-shared
|
||||
STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES))
|
||||
SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench)
|
||||
endif
|
||||
|
||||
STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o))
|
||||
STATIC_MEMENVOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
|
||||
|
||||
DEVICE_LIBOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(SOURCES:.cc=.o))
|
||||
DEVICE_MEMENVOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
|
||||
|
||||
SIMULATOR_LIBOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(SOURCES:.cc=.o))
|
||||
SIMULATOR_MEMENVOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
|
||||
|
||||
SHARED_LIBOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(SOURCES:.cc=.o))
|
||||
SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o))
|
||||
|
||||
TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o
|
||||
TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL)
|
||||
|
||||
STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS)))
|
||||
STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS)))
|
||||
STATIC_ALLOBJS := $(STATIC_LIBOBJECTS) $(STATIC_MEMENVOBJECTS) $(STATIC_TESTOBJS) $(STATIC_UTILOBJS) $(TESTHARNESS)
|
||||
DEVICE_ALLOBJS := $(DEVICE_LIBOBJECTS) $(DEVICE_MEMENVOBJECTS)
|
||||
SIMULATOR_ALLOBJS := $(SIMULATOR_LIBOBJECTS) $(SIMULATOR_MEMENVOBJECTS)
|
||||
|
||||
default: all
|
||||
|
||||
# Should we build shared libraries?
|
||||
ifneq ($(PLATFORM_SHARED_EXT),)
|
||||
|
||||
# Many leveldb test apps use non-exported API's. Only build a subset for testing.
|
||||
SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS)
|
||||
|
||||
ifneq ($(PLATFORM_SHARED_VERSIONED),true)
|
||||
SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
|
||||
SHARED_LIB2 = $(SHARED_LIB1)
|
||||
SHARED_LIB3 = $(SHARED_LIB1)
|
||||
SHARED_LIBS = $(SHARED_LIB1)
|
||||
SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
|
||||
else
|
||||
# Update db.h if you change these.
|
||||
SHARED_VERSION_MAJOR = 1
|
||||
SHARED_VERSION_MINOR = 20
|
||||
SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT)
|
||||
SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR)
|
||||
SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR)
|
||||
SHARED_LIBS = $(SHARED_OUTDIR)/$(SHARED_LIB1) $(SHARED_OUTDIR)/$(SHARED_LIB2) $(SHARED_OUTDIR)/$(SHARED_LIB3)
|
||||
$(SHARED_OUTDIR)/$(SHARED_LIB1): $(SHARED_OUTDIR)/$(SHARED_LIB3)
|
||||
ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB1)
|
||||
$(SHARED_OUTDIR)/$(SHARED_LIB2): $(SHARED_OUTDIR)/$(SHARED_LIB3)
|
||||
ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB2)
|
||||
SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a
|
||||
endif
|
||||
|
||||
$(SHARED_OUTDIR)/$(SHARED_LIB3): $(SHARED_LIBOBJECTS)
|
||||
$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED_LIB2) $(SHARED_LIBOBJECTS) -o $(SHARED_OUTDIR)/$(SHARED_LIB3) $(LIBS)
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
all: $(SHARED_LIBS) $(SHARED_PROGRAMS) $(STATIC_OUTDIR)/libleveldb.a $(STATIC_OUTDIR)/libmemenv.a $(STATIC_PROGRAMS)
|
||||
|
||||
check: $(STATIC_PROGRAMS)
|
||||
for t in $(notdir $(TESTS)); do echo "***** Running $$t"; $(STATIC_OUTDIR)/$$t || exit 1; done
|
||||
|
||||
clean:
|
||||
-rm -rf out-static out-shared out-ios-x86 out-ios-arm out-ios-universal
|
||||
-rm -f build_config.mk
|
||||
-rm -rf ios-x86 ios-arm
|
||||
|
||||
$(STATIC_OUTDIR):
|
||||
mkdir $@
|
||||
|
||||
$(STATIC_OUTDIR)/db: | $(STATIC_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(STATIC_OUTDIR)/helpers/memenv: | $(STATIC_OUTDIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(STATIC_OUTDIR)/port: | $(STATIC_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(STATIC_OUTDIR)/table: | $(STATIC_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(STATIC_OUTDIR)/util: | $(STATIC_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
.PHONY: STATIC_OBJDIRS
|
||||
STATIC_OBJDIRS: \
|
||||
$(STATIC_OUTDIR)/db \
|
||||
$(STATIC_OUTDIR)/port \
|
||||
$(STATIC_OUTDIR)/table \
|
||||
$(STATIC_OUTDIR)/util \
|
||||
$(STATIC_OUTDIR)/helpers/memenv
|
||||
|
||||
$(SHARED_OUTDIR):
|
||||
mkdir $@
|
||||
|
||||
$(SHARED_OUTDIR)/db: | $(SHARED_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SHARED_OUTDIR)/helpers/memenv: | $(SHARED_OUTDIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(SHARED_OUTDIR)/port: | $(SHARED_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SHARED_OUTDIR)/table: | $(SHARED_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SHARED_OUTDIR)/util: | $(SHARED_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
.PHONY: SHARED_OBJDIRS
|
||||
SHARED_OBJDIRS: \
|
||||
$(SHARED_OUTDIR)/db \
|
||||
$(SHARED_OUTDIR)/port \
|
||||
$(SHARED_OUTDIR)/table \
|
||||
$(SHARED_OUTDIR)/util \
|
||||
$(SHARED_OUTDIR)/helpers/memenv
|
||||
|
||||
$(DEVICE_OUTDIR):
|
||||
mkdir $@
|
||||
|
||||
$(DEVICE_OUTDIR)/db: | $(DEVICE_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(DEVICE_OUTDIR)/helpers/memenv: | $(DEVICE_OUTDIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(DEVICE_OUTDIR)/port: | $(DEVICE_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(DEVICE_OUTDIR)/table: | $(DEVICE_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(DEVICE_OUTDIR)/util: | $(DEVICE_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
.PHONY: DEVICE_OBJDIRS
|
||||
DEVICE_OBJDIRS: \
|
||||
$(DEVICE_OUTDIR)/db \
|
||||
$(DEVICE_OUTDIR)/port \
|
||||
$(DEVICE_OUTDIR)/table \
|
||||
$(DEVICE_OUTDIR)/util \
|
||||
$(DEVICE_OUTDIR)/helpers/memenv
|
||||
|
||||
$(SIMULATOR_OUTDIR):
|
||||
mkdir $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/db: | $(SIMULATOR_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/helpers/memenv: | $(SIMULATOR_OUTDIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/port: | $(SIMULATOR_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/table: | $(SIMULATOR_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/util: | $(SIMULATOR_OUTDIR)
|
||||
mkdir $@
|
||||
|
||||
.PHONY: SIMULATOR_OBJDIRS
|
||||
SIMULATOR_OBJDIRS: \
|
||||
$(SIMULATOR_OUTDIR)/db \
|
||||
$(SIMULATOR_OUTDIR)/port \
|
||||
$(SIMULATOR_OUTDIR)/table \
|
||||
$(SIMULATOR_OUTDIR)/util \
|
||||
$(SIMULATOR_OUTDIR)/helpers/memenv
|
||||
|
||||
$(STATIC_ALLOBJS): | STATIC_OBJDIRS
|
||||
$(DEVICE_ALLOBJS): | DEVICE_OBJDIRS
|
||||
$(SIMULATOR_ALLOBJS): | SIMULATOR_OBJDIRS
|
||||
$(SHARED_ALLOBJS): | SHARED_OBJDIRS
|
||||
|
||||
ifeq ($(PLATFORM), IOS)
|
||||
$(DEVICE_OUTDIR)/libleveldb.a: $(DEVICE_LIBOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(DEVICE_LIBOBJECTS)
|
||||
|
||||
$(SIMULATOR_OUTDIR)/libleveldb.a: $(SIMULATOR_LIBOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(SIMULATOR_LIBOBJECTS)
|
||||
|
||||
$(DEVICE_OUTDIR)/libmemenv.a: $(DEVICE_MEMENVOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(DEVICE_MEMENVOBJECTS)
|
||||
|
||||
$(SIMULATOR_OUTDIR)/libmemenv.a: $(SIMULATOR_MEMENVOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(SIMULATOR_MEMENVOBJECTS)
|
||||
|
||||
# For iOS, create universal object libraries to be used on both the simulator and
|
||||
# a device.
|
||||
$(STATIC_OUTDIR)/libleveldb.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a
|
||||
lipo -create $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a -output $@
|
||||
|
||||
$(STATIC_OUTDIR)/libmemenv.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a
|
||||
lipo -create $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a -output $@
|
||||
else
|
||||
$(STATIC_OUTDIR)/libleveldb.a:$(STATIC_LIBOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(STATIC_LIBOBJECTS)
|
||||
|
||||
$(STATIC_OUTDIR)/libmemenv.a:$(STATIC_MEMENVOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(STATIC_MEMENVOBJECTS)
|
||||
endif
|
||||
|
||||
$(SHARED_MEMENVLIB):$(SHARED_MEMENVOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(SHARED_MEMENVOBJECTS)
|
||||
|
||||
$(STATIC_OUTDIR)/db_bench:db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/db_bench_sqlite3:doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/db_bench_tree_db:doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/leveldbutil:db/leveldbutil.cc $(STATIC_LIBOBJECTS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/leveldbutil.cc $(STATIC_LIBOBJECTS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/arena_test:util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/autocompact_test:db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/bloom_test:util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/c_test:$(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/cache_test:util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/coding_test:util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/corruption_test:db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/crc32c_test:util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/env_posix_test:util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/fault_injection_test:db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/filename_test:db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/filter_block_test:table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/hash_test:util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/issue178_test:issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/issue200_test:issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/log_test:db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/recovery_test:db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/table_test:table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/skiplist_test:db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/version_edit_test:db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/version_set_test:db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS)
|
||||
$(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS)
|
||||
|
||||
$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL)
|
||||
$(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS)
|
||||
|
||||
.PHONY: run-shared
|
||||
run-shared: $(SHARED_OUTDIR)/db_bench
|
||||
LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench
|
||||
|
||||
$(SIMULATOR_OUTDIR)/%.o: %.cc
|
||||
xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
|
||||
|
||||
$(DEVICE_OUTDIR)/%.o: %.cc
|
||||
xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
|
||||
|
||||
$(SIMULATOR_OUTDIR)/%.o: %.c
|
||||
xcrun -sdk iphonesimulator $(CC) $(CFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@
|
||||
|
||||
$(DEVICE_OUTDIR)/%.o: %.c
|
||||
xcrun -sdk iphoneos $(CC) $(CFLAGS) $(DEVICE_CFLAGS) -c $< -o $@
|
||||
|
||||
$(STATIC_OUTDIR)/%.o: %.cc
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
$(STATIC_OUTDIR)/%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(SHARED_OUTDIR)/%.o: %.cc
|
||||
$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
|
||||
|
||||
$(SHARED_OUTDIR)/%.o: %.c
|
||||
$(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
|
||||
|
||||
$(STATIC_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc
|
||||
$(CXX) $(CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@
|
||||
|
||||
$(SHARED_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc
|
||||
$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@
|
|
@ -1,10 +1,12 @@
|
|||
**LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.**
|
||||
|
||||
[![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/g2j5j4rfkda6eyw5/branch/master?svg=true)](https://ci.appveyor.com/project/pwnall/leveldb)
|
||||
|
||||
Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
|
||||
|
||||
# Features
|
||||
|
||||
* Keys and values are arbitrary byte arrays.
|
||||
* Data is stored sorted by key.
|
||||
* Callers can provide a custom comparison function to override the sort order.
|
||||
|
@ -16,15 +18,55 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)
|
|||
* External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.
|
||||
|
||||
# Documentation
|
||||
|
||||
[LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code.
|
||||
|
||||
|
||||
# Limitations
|
||||
|
||||
* This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes.
|
||||
* Only a single process (possibly multi-threaded) can access a particular database at a time.
|
||||
* There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.
|
||||
|
||||
# Building
|
||||
|
||||
This project supports [CMake](https://cmake.org/) out of the box.
|
||||
|
||||
### Build for POSIX
|
||||
|
||||
Quick start:
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
|
||||
```
|
||||
|
||||
### Building for Windows
|
||||
|
||||
First generate the Visual Studio 2017 project/solution files:
|
||||
|
||||
```cmd
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "Visual Studio 15" ..
|
||||
```
|
||||
The default default will build for x86. For 64-bit run:
|
||||
|
||||
```cmd
|
||||
cmake -G "Visual Studio 15 Win64" ..
|
||||
```
|
||||
|
||||
To compile the Windows solution from the command-line:
|
||||
|
||||
```cmd
|
||||
devenv /build Debug leveldb.sln
|
||||
```
|
||||
|
||||
or open leveldb.sln in Visual Studio and build from within.
|
||||
|
||||
Please see the CMake documentation and `CMakeLists.txt` for more advanced usage.
|
||||
|
||||
# Contributing to the leveldb Project
|
||||
|
||||
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,
|
||||
|
@ -32,10 +74,10 @@ will be considered.
|
|||
|
||||
Contribution requirements:
|
||||
|
||||
1. **POSIX only**. We _generally_ will only accept changes that are both
|
||||
compiled, and tested on a POSIX platform - usually Linux. Very small
|
||||
changes will sometimes be accepted, but consider that more of an
|
||||
exception than the rule.
|
||||
1. **Tested platforms only**. We _generally_ will only accept changes for
|
||||
platforms that are compiled and tested. This means POSIX (for Linux and
|
||||
macOS) or Windows. Very small changes will sometimes be accepted, but
|
||||
consider that more of an exception than the rule.
|
||||
|
||||
2. **Stable API**. We strive very hard to maintain a stable API. Changes that
|
||||
require changes for projects using leveldb _might_ be rejected without
|
||||
|
@ -44,7 +86,16 @@ Contribution requirements:
|
|||
3. **Tests**: All changes must be accompanied by a new (or changed) test, or
|
||||
a sufficient explanation as to why a new (or changed) test is not required.
|
||||
|
||||
4. **Consistent Style**: This project conforms to the
|
||||
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
|
||||
To ensure your changes are properly formatted please run:
|
||||
|
||||
```
|
||||
clang-format -i --style=file <file>
|
||||
```
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Before any pull request will be accepted the author must first sign a
|
||||
Contributor License Agreement (CLA) at https://cla.developers.google.com/.
|
||||
|
||||
|
@ -138,37 +189,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.
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
# Building LevelDB On Windows
|
||||
|
||||
## Prereqs
|
||||
|
||||
Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b).
|
||||
|
||||
Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz)
|
||||
|
||||
1. Open the "Windows SDK 7.1 Command Prompt" :
|
||||
Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt"
|
||||
2. Change the directory to the leveldb project
|
||||
|
||||
## Building the Static lib
|
||||
|
||||
* 32 bit Version
|
||||
|
||||
setenv /x86
|
||||
msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5
|
||||
|
||||
* 64 bit Version
|
||||
|
||||
setenv /x64
|
||||
msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5
|
||||
|
||||
|
||||
## Building and Running the Benchmark app
|
||||
|
||||
* 32 bit Version
|
||||
|
||||
setenv /x86
|
||||
msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5
|
||||
Benchmark\leveldb.exe
|
||||
|
||||
* 64 bit Version
|
||||
|
||||
setenv /x64
|
||||
msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5
|
||||
x64\Benchmark\leveldb.exe
|
||||
|
|
@ -2,14 +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 <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/filter_policy.h"
|
||||
#include "leveldb/write_batch.h"
|
||||
#include "port/port.h"
|
||||
#include "util/crc32c.h"
|
||||
|
@ -35,7 +35,6 @@
|
|||
// seekrandom -- N random seeks
|
||||
// open -- cost of opening a DB
|
||||
// crc32c -- repeated crc32c of 4K of data
|
||||
// acquireload -- load N*1000 times
|
||||
// Meta operations:
|
||||
// compact -- Compact the entire DB
|
||||
// stats -- Print DB stats
|
||||
|
@ -57,9 +56,7 @@ static const char* FLAGS_benchmarks =
|
|||
"fill100K,"
|
||||
"crc32c,"
|
||||
"snappycomp,"
|
||||
"snappyuncomp,"
|
||||
"acquireload,"
|
||||
;
|
||||
"snappyuncomp,";
|
||||
|
||||
// Number of key/values to place in database
|
||||
static int FLAGS_num = 1000000;
|
||||
|
@ -112,12 +109,12 @@ static bool FLAGS_use_existing_db = false;
|
|||
static bool FLAGS_reuse_logs = false;
|
||||
|
||||
// Use the db with the following name.
|
||||
static const char* FLAGS_db = NULL;
|
||||
static const char* FLAGS_db = nullptr;
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
leveldb::Env* g_env = NULL;
|
||||
leveldb::Env* g_env = nullptr;
|
||||
|
||||
// Helper for quickly generating random data.
|
||||
class RandomGenerator {
|
||||
|
@ -158,7 +155,7 @@ static Slice TrimSpace(Slice s) {
|
|||
start++;
|
||||
}
|
||||
size_t limit = s.size();
|
||||
while (limit > start && isspace(s[limit-1])) {
|
||||
while (limit > start && isspace(s[limit - 1])) {
|
||||
limit--;
|
||||
}
|
||||
return Slice(s.data() + start, limit - start);
|
||||
|
@ -190,14 +187,12 @@ class Stats {
|
|||
|
||||
void Start() {
|
||||
next_report_ = 100;
|
||||
last_op_finish_ = start_;
|
||||
hist_.Clear();
|
||||
done_ = 0;
|
||||
bytes_ = 0;
|
||||
seconds_ = 0;
|
||||
start_ = g_env->NowMicros();
|
||||
finish_ = start_;
|
||||
message_.clear();
|
||||
start_ = finish_ = last_op_finish_ = g_env->NowMicros();
|
||||
}
|
||||
|
||||
void Merge(const Stats& other) {
|
||||
|
@ -217,9 +212,7 @@ class Stats {
|
|||
seconds_ = (finish_ - start_) * 1e-6;
|
||||
}
|
||||
|
||||
void AddMessage(Slice msg) {
|
||||
AppendWithSpace(&message_, msg);
|
||||
}
|
||||
void AddMessage(Slice msg) { AppendWithSpace(&message_, msg); }
|
||||
|
||||
void FinishedSingleOp() {
|
||||
if (FLAGS_histogram) {
|
||||
|
@ -235,21 +228,26 @@ class Stats {
|
|||
|
||||
done_++;
|
||||
if (done_ >= next_report_) {
|
||||
if (next_report_ < 1000) next_report_ += 100;
|
||||
else if (next_report_ < 5000) next_report_ += 500;
|
||||
else if (next_report_ < 10000) next_report_ += 1000;
|
||||
else if (next_report_ < 50000) next_report_ += 5000;
|
||||
else if (next_report_ < 100000) next_report_ += 10000;
|
||||
else if (next_report_ < 500000) next_report_ += 50000;
|
||||
else next_report_ += 100000;
|
||||
if (next_report_ < 1000)
|
||||
next_report_ += 100;
|
||||
else if (next_report_ < 5000)
|
||||
next_report_ += 500;
|
||||
else if (next_report_ < 10000)
|
||||
next_report_ += 1000;
|
||||
else if (next_report_ < 50000)
|
||||
next_report_ += 5000;
|
||||
else if (next_report_ < 100000)
|
||||
next_report_ += 10000;
|
||||
else if (next_report_ < 500000)
|
||||
next_report_ += 50000;
|
||||
else
|
||||
next_report_ += 100000;
|
||||
fprintf(stderr, "... finished %d ops%30s\r", done_, "");
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void AddBytes(int64_t n) {
|
||||
bytes_ += n;
|
||||
}
|
||||
void AddBytes(int64_t n) { bytes_ += n; }
|
||||
|
||||
void Report(const Slice& name) {
|
||||
// Pretend at least one op was done in case we are running a benchmark
|
||||
|
@ -268,11 +266,8 @@ class Stats {
|
|||
}
|
||||
AppendWithSpace(&extra, message_);
|
||||
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
|
||||
name.ToString().c_str(),
|
||||
seconds_ * 1e6 / done_,
|
||||
(extra.empty() ? "" : " "),
|
||||
extra.c_str());
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
|
||||
seconds_ * 1e6 / done_, (extra.empty() ? "" : " "), extra.c_str());
|
||||
if (FLAGS_histogram) {
|
||||
fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
|
||||
}
|
||||
|
@ -283,8 +278,8 @@ class Stats {
|
|||
// State shared by all concurrent executions of the same benchmark.
|
||||
struct SharedState {
|
||||
port::Mutex mu;
|
||||
port::CondVar cv;
|
||||
int total;
|
||||
port::CondVar cv GUARDED_BY(mu);
|
||||
int total GUARDED_BY(mu);
|
||||
|
||||
// Each thread goes through the following states:
|
||||
// (1) initializing
|
||||
|
@ -292,24 +287,22 @@ struct SharedState {
|
|||
// (3) running
|
||||
// (4) done
|
||||
|
||||
int num_initialized;
|
||||
int num_done;
|
||||
bool start;
|
||||
int num_initialized GUARDED_BY(mu);
|
||||
int num_done GUARDED_BY(mu);
|
||||
bool start GUARDED_BY(mu);
|
||||
|
||||
SharedState() : cv(&mu) { }
|
||||
SharedState(int total)
|
||||
: cv(&mu), total(total), num_initialized(0), num_done(0), start(false) {}
|
||||
};
|
||||
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
struct ThreadState {
|
||||
int tid; // 0..n-1 when running in n threads
|
||||
Random rand; // Has different seeds for different threads
|
||||
int tid; // 0..n-1 when running in n threads
|
||||
Random rand; // Has different seeds for different threads
|
||||
Stats stats;
|
||||
SharedState* shared;
|
||||
|
||||
ThreadState(int index)
|
||||
: tid(index),
|
||||
rand(1000 + index) {
|
||||
}
|
||||
ThreadState(int index) : tid(index), rand(1000 + index), shared(nullptr) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -335,20 +328,20 @@ class Benchmark {
|
|||
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));
|
||||
((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));
|
||||
(((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
|
||||
1048576.0));
|
||||
PrintWarnings();
|
||||
fprintf(stdout, "------------------------------------------------\n");
|
||||
}
|
||||
|
||||
void PrintWarnings() {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
fprintf(stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
|
||||
);
|
||||
fprintf(
|
||||
stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout,
|
||||
|
@ -366,22 +359,22 @@ class Benchmark {
|
|||
}
|
||||
|
||||
void PrintEnvironment() {
|
||||
fprintf(stderr, "LevelDB: version %d.%d\n",
|
||||
kMajorVersion, kMinorVersion);
|
||||
fprintf(stderr, "LevelDB: version %d.%d\n", kMajorVersion,
|
||||
kMinorVersion);
|
||||
|
||||
#if defined(__linux)
|
||||
time_t now = time(NULL);
|
||||
time_t now = time(nullptr);
|
||||
fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
|
||||
|
||||
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if (cpuinfo != NULL) {
|
||||
if (cpuinfo != nullptr) {
|
||||
char line[1000];
|
||||
int num_cpus = 0;
|
||||
std::string cpu_type;
|
||||
std::string cache_size;
|
||||
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
|
||||
while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
|
||||
const char* sep = strchr(line, ':');
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Slice key = TrimSpace(Slice(line, sep - 1 - line));
|
||||
|
@ -402,16 +395,16 @@ class Benchmark {
|
|||
|
||||
public:
|
||||
Benchmark()
|
||||
: cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL),
|
||||
filter_policy_(FLAGS_bloom_bits >= 0
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits)
|
||||
: NULL),
|
||||
db_(NULL),
|
||||
num_(FLAGS_num),
|
||||
value_size_(FLAGS_value_size),
|
||||
entries_per_batch_(1),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
heap_counter_(0) {
|
||||
: cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr),
|
||||
filter_policy_(FLAGS_bloom_bits >= 0
|
||||
? NewBloomFilterPolicy(FLAGS_bloom_bits)
|
||||
: nullptr),
|
||||
db_(nullptr),
|
||||
num_(FLAGS_num),
|
||||
value_size_(FLAGS_value_size),
|
||||
entries_per_batch_(1),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
heap_counter_(0) {
|
||||
std::vector<std::string> files;
|
||||
g_env->GetChildren(FLAGS_db, &files);
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
|
@ -435,12 +428,12 @@ class Benchmark {
|
|||
Open();
|
||||
|
||||
const char* benchmarks = FLAGS_benchmarks;
|
||||
while (benchmarks != NULL) {
|
||||
while (benchmarks != nullptr) {
|
||||
const char* sep = strchr(benchmarks, ',');
|
||||
Slice name;
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
name = benchmarks;
|
||||
benchmarks = NULL;
|
||||
benchmarks = nullptr;
|
||||
} else {
|
||||
name = Slice(benchmarks, sep - benchmarks);
|
||||
benchmarks = sep + 1;
|
||||
|
@ -453,7 +446,7 @@ class Benchmark {
|
|||
entries_per_batch_ = 1;
|
||||
write_options_ = WriteOptions();
|
||||
|
||||
void (Benchmark::*method)(ThreadState*) = NULL;
|
||||
void (Benchmark::*method)(ThreadState*) = nullptr;
|
||||
bool fresh_db = false;
|
||||
int num_threads = FLAGS_threads;
|
||||
|
||||
|
@ -510,8 +503,6 @@ class Benchmark {
|
|||
method = &Benchmark::Compact;
|
||||
} else if (name == Slice("crc32c")) {
|
||||
method = &Benchmark::Crc32c;
|
||||
} else if (name == Slice("acquireload")) {
|
||||
method = &Benchmark::AcquireLoad;
|
||||
} else if (name == Slice("snappycomp")) {
|
||||
method = &Benchmark::SnappyCompress;
|
||||
} else if (name == Slice("snappyuncomp")) {
|
||||
|
@ -523,7 +514,7 @@ class Benchmark {
|
|||
} else if (name == Slice("sstables")) {
|
||||
PrintStats("leveldb.sstables");
|
||||
} else {
|
||||
if (name != Slice()) { // No error message for empty name
|
||||
if (!name.empty()) { // No error message for empty name
|
||||
fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
@ -532,16 +523,16 @@ class Benchmark {
|
|||
if (FLAGS_use_existing_db) {
|
||||
fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",
|
||||
name.ToString().c_str());
|
||||
method = NULL;
|
||||
method = nullptr;
|
||||
} else {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
DestroyDB(FLAGS_db, Options());
|
||||
Open();
|
||||
}
|
||||
}
|
||||
|
||||
if (method != NULL) {
|
||||
if (method != nullptr) {
|
||||
RunBenchmark(num_threads, name, method);
|
||||
}
|
||||
}
|
||||
|
@ -585,11 +576,7 @@ class Benchmark {
|
|||
|
||||
void RunBenchmark(int n, Slice name,
|
||||
void (Benchmark::*method)(ThreadState*)) {
|
||||
SharedState shared;
|
||||
shared.total = n;
|
||||
shared.num_initialized = 0;
|
||||
shared.num_done = 0;
|
||||
shared.start = false;
|
||||
SharedState shared(n);
|
||||
|
||||
ThreadArg* arg = new ThreadArg[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
@ -643,22 +630,6 @@ class Benchmark {
|
|||
thread->stats.AddMessage(label);
|
||||
}
|
||||
|
||||
void AcquireLoad(ThreadState* thread) {
|
||||
int dummy;
|
||||
port::AtomicPointer ap(&dummy);
|
||||
int count = 0;
|
||||
void *ptr = NULL;
|
||||
thread->stats.AddMessage("(each op is 1000 loads)");
|
||||
while (count < 100000) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
ptr = ap.Acquire_Load();
|
||||
}
|
||||
count++;
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
if (ptr == NULL) exit(1); // Disable unused variable warning.
|
||||
}
|
||||
|
||||
void SnappyCompress(ThreadState* thread) {
|
||||
RandomGenerator gen;
|
||||
Slice input = gen.Generate(Options().block_size);
|
||||
|
@ -692,8 +663,8 @@ class Benchmark {
|
|||
int64_t bytes = 0;
|
||||
char* uncompressed = new char[input.size()];
|
||||
while (ok && bytes < 1024 * 1048576) { // Compress 1G
|
||||
ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
|
||||
uncompressed);
|
||||
ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),
|
||||
uncompressed);
|
||||
bytes += input.size();
|
||||
thread->stats.FinishedSingleOp();
|
||||
}
|
||||
|
@ -707,7 +678,7 @@ class Benchmark {
|
|||
}
|
||||
|
||||
void Open() {
|
||||
assert(db_ == NULL);
|
||||
assert(db_ == nullptr);
|
||||
Options options;
|
||||
options.env = g_env;
|
||||
options.create_if_missing = !FLAGS_use_existing_db;
|
||||
|
@ -733,13 +704,9 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
|
||||
void WriteSeq(ThreadState* thread) {
|
||||
DoWrite(thread, true);
|
||||
}
|
||||
void WriteSeq(ThreadState* thread) { DoWrite(thread, true); }
|
||||
|
||||
void WriteRandom(ThreadState* thread) {
|
||||
DoWrite(thread, false);
|
||||
}
|
||||
void WriteRandom(ThreadState* thread) { DoWrite(thread, false); }
|
||||
|
||||
void DoWrite(ThreadState* thread, bool seq) {
|
||||
if (num_ != FLAGS_num) {
|
||||
|
@ -755,7 +722,7 @@ class Benchmark {
|
|||
for (int i = 0; i < num_; i += entries_per_batch_) {
|
||||
batch.Clear();
|
||||
for (int j = 0; j < entries_per_batch_; j++) {
|
||||
const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
|
||||
const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num);
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%016d", k);
|
||||
batch.Put(key, gen.Generate(value_size_));
|
||||
|
@ -865,7 +832,7 @@ class Benchmark {
|
|||
for (int i = 0; i < num_; i += entries_per_batch_) {
|
||||
batch.Clear();
|
||||
for (int j = 0; j < entries_per_batch_; j++) {
|
||||
const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);
|
||||
const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num);
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%016d", k);
|
||||
batch.Delete(key);
|
||||
|
@ -879,13 +846,9 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
|
||||
void DeleteSeq(ThreadState* thread) {
|
||||
DoDelete(thread, true);
|
||||
}
|
||||
void DeleteSeq(ThreadState* thread) { DoDelete(thread, true); }
|
||||
|
||||
void DeleteRandom(ThreadState* thread) {
|
||||
DoDelete(thread, false);
|
||||
}
|
||||
void DeleteRandom(ThreadState* thread) { DoDelete(thread, false); }
|
||||
|
||||
void ReadWhileWriting(ThreadState* thread) {
|
||||
if (thread->tid > 0) {
|
||||
|
@ -917,9 +880,7 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
|
||||
void Compact(ThreadState* thread) {
|
||||
db_->CompactRange(NULL, NULL);
|
||||
}
|
||||
void Compact(ThreadState* thread) { db_->CompactRange(nullptr, nullptr); }
|
||||
|
||||
void PrintStats(const char* key) {
|
||||
std::string stats;
|
||||
|
@ -1008,10 +969,10 @@ int main(int argc, char** argv) {
|
|||
leveldb::g_env = leveldb::Env::Default();
|
||||
|
||||
// Choose a location for the test database if none given with --db=<path>
|
||||
if (FLAGS_db == NULL) {
|
||||
leveldb::g_env->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
if (FLAGS_db == nullptr) {
|
||||
leveldb::g_env->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
}
|
||||
|
||||
leveldb::Benchmark benchmark;
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "util/histogram.h"
|
||||
#include "util/random.h"
|
||||
#include "util/testutil.h"
|
||||
|
@ -38,8 +39,7 @@ static const char* FLAGS_benchmarks =
|
|||
"fillrand100K,"
|
||||
"fillseq100K,"
|
||||
"readseq,"
|
||||
"readrand100K,"
|
||||
;
|
||||
"readrand100K,";
|
||||
|
||||
// Number of key/values to place in database
|
||||
static int FLAGS_num = 1000000;
|
||||
|
@ -76,10 +76,9 @@ static bool FLAGS_transaction = true;
|
|||
static bool FLAGS_WAL_enabled = true;
|
||||
|
||||
// Use the db with the following name.
|
||||
static const char* FLAGS_db = NULL;
|
||||
static const char* FLAGS_db = nullptr;
|
||||
|
||||
inline
|
||||
static void ExecErrorCheck(int status, char *err_msg) {
|
||||
inline static void ExecErrorCheck(int status, char* err_msg) {
|
||||
if (status != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
|
@ -87,27 +86,25 @@ static void ExecErrorCheck(int status, char *err_msg) {
|
|||
}
|
||||
}
|
||||
|
||||
inline
|
||||
static void StepErrorCheck(int status) {
|
||||
inline static void StepErrorCheck(int status) {
|
||||
if (status != SQLITE_DONE) {
|
||||
fprintf(stderr, "SQL step error: status = %d\n", status);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
static void ErrorCheck(int status) {
|
||||
inline static void ErrorCheck(int status) {
|
||||
if (status != SQLITE_OK) {
|
||||
fprintf(stderr, "sqlite3 error: status = %d\n", status);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
static void WalCheckpoint(sqlite3* db_) {
|
||||
inline static void WalCheckpoint(sqlite3* db_) {
|
||||
// Flush all writes to disk
|
||||
if (FLAGS_WAL_enabled) {
|
||||
sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL);
|
||||
sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +149,7 @@ static Slice TrimSpace(Slice s) {
|
|||
start++;
|
||||
}
|
||||
int limit = s.size();
|
||||
while (limit > start && isspace(s[limit-1])) {
|
||||
while (limit > start && isspace(s[limit - 1])) {
|
||||
limit--;
|
||||
}
|
||||
return Slice(s.data() + start, limit - start);
|
||||
|
@ -176,7 +173,7 @@ class Benchmark {
|
|||
|
||||
// State kept for progress messages
|
||||
int done_;
|
||||
int next_report_; // When to report next
|
||||
int next_report_; // When to report next
|
||||
|
||||
void PrintHeader() {
|
||||
const int kKeySize = 16;
|
||||
|
@ -185,17 +182,17 @@ class Benchmark {
|
|||
fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size);
|
||||
fprintf(stdout, "Entries: %d\n", num_);
|
||||
fprintf(stdout, "RawSize: %.1f MB (estimated)\n",
|
||||
((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
|
||||
/ 1048576.0));
|
||||
((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_) /
|
||||
1048576.0));
|
||||
PrintWarnings();
|
||||
fprintf(stdout, "------------------------------------------------\n");
|
||||
}
|
||||
|
||||
void PrintWarnings() {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
fprintf(stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
|
||||
);
|
||||
fprintf(
|
||||
stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout,
|
||||
|
@ -207,18 +204,18 @@ class Benchmark {
|
|||
fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION);
|
||||
|
||||
#if defined(__linux)
|
||||
time_t now = time(NULL);
|
||||
time_t now = time(nullptr);
|
||||
fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
|
||||
|
||||
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if (cpuinfo != NULL) {
|
||||
if (cpuinfo != nullptr) {
|
||||
char line[1000];
|
||||
int num_cpus = 0;
|
||||
std::string cpu_type;
|
||||
std::string cache_size;
|
||||
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
|
||||
while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
|
||||
const char* sep = strchr(line, ':');
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Slice key = TrimSpace(Slice(line, sep - 1 - line));
|
||||
|
@ -261,13 +258,20 @@ class Benchmark {
|
|||
|
||||
done_++;
|
||||
if (done_ >= next_report_) {
|
||||
if (next_report_ < 1000) next_report_ += 100;
|
||||
else if (next_report_ < 5000) next_report_ += 500;
|
||||
else if (next_report_ < 10000) next_report_ += 1000;
|
||||
else if (next_report_ < 50000) next_report_ += 5000;
|
||||
else if (next_report_ < 100000) next_report_ += 10000;
|
||||
else if (next_report_ < 500000) next_report_ += 50000;
|
||||
else next_report_ += 100000;
|
||||
if (next_report_ < 1000)
|
||||
next_report_ += 100;
|
||||
else if (next_report_ < 5000)
|
||||
next_report_ += 500;
|
||||
else if (next_report_ < 10000)
|
||||
next_report_ += 1000;
|
||||
else if (next_report_ < 50000)
|
||||
next_report_ += 5000;
|
||||
else if (next_report_ < 100000)
|
||||
next_report_ += 10000;
|
||||
else if (next_report_ < 500000)
|
||||
next_report_ += 50000;
|
||||
else
|
||||
next_report_ += 100000;
|
||||
fprintf(stderr, "... finished %d ops%30s\r", done_, "");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
@ -285,16 +289,14 @@ class Benchmark {
|
|||
snprintf(rate, sizeof(rate), "%6.1f MB/s",
|
||||
(bytes_ / 1048576.0) / (finish - start_));
|
||||
if (!message_.empty()) {
|
||||
message_ = std::string(rate) + " " + message_;
|
||||
message_ = std::string(rate) + " " + message_;
|
||||
} else {
|
||||
message_ = rate;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
|
||||
name.ToString().c_str(),
|
||||
(finish - start_) * 1e6 / done_,
|
||||
(message_.empty() ? "" : " "),
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
|
||||
(finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "),
|
||||
message_.c_str());
|
||||
if (FLAGS_histogram) {
|
||||
fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
|
||||
|
@ -303,22 +305,16 @@ class Benchmark {
|
|||
}
|
||||
|
||||
public:
|
||||
enum Order {
|
||||
SEQUENTIAL,
|
||||
RANDOM
|
||||
};
|
||||
enum DBState {
|
||||
FRESH,
|
||||
EXISTING
|
||||
};
|
||||
enum Order { SEQUENTIAL, RANDOM };
|
||||
enum DBState { FRESH, EXISTING };
|
||||
|
||||
Benchmark()
|
||||
: db_(NULL),
|
||||
db_num_(0),
|
||||
num_(FLAGS_num),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
bytes_(0),
|
||||
rand_(301) {
|
||||
: db_(nullptr),
|
||||
db_num_(0),
|
||||
num_(FLAGS_num),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
bytes_(0),
|
||||
rand_(301) {
|
||||
std::vector<std::string> files;
|
||||
std::string test_dir;
|
||||
Env::Default()->GetTestDirectory(&test_dir);
|
||||
|
@ -345,12 +341,12 @@ class Benchmark {
|
|||
Open();
|
||||
|
||||
const char* benchmarks = FLAGS_benchmarks;
|
||||
while (benchmarks != NULL) {
|
||||
while (benchmarks != nullptr) {
|
||||
const char* sep = strchr(benchmarks, ',');
|
||||
Slice name;
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
name = benchmarks;
|
||||
benchmarks = NULL;
|
||||
benchmarks = nullptr;
|
||||
} else {
|
||||
name = Slice(benchmarks, sep - benchmarks);
|
||||
benchmarks = sep + 1;
|
||||
|
@ -415,20 +411,18 @@ class Benchmark {
|
|||
}
|
||||
|
||||
void Open() {
|
||||
assert(db_ == NULL);
|
||||
assert(db_ == nullptr);
|
||||
|
||||
int status;
|
||||
char file_name[100];
|
||||
char* err_msg = NULL;
|
||||
char* err_msg = nullptr;
|
||||
db_num_++;
|
||||
|
||||
// Open database
|
||||
std::string tmp_dir;
|
||||
Env::Default()->GetTestDirectory(&tmp_dir);
|
||||
snprintf(file_name, sizeof(file_name),
|
||||
"%s/dbbench_sqlite3-%d.db",
|
||||
tmp_dir.c_str(),
|
||||
db_num_);
|
||||
snprintf(file_name, sizeof(file_name), "%s/dbbench_sqlite3-%d.db",
|
||||
tmp_dir.c_str(), db_num_);
|
||||
status = sqlite3_open(file_name, &db_);
|
||||
if (status) {
|
||||
fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_));
|
||||
|
@ -439,7 +433,7 @@ class Benchmark {
|
|||
char cache_size[100];
|
||||
snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d",
|
||||
FLAGS_num_pages);
|
||||
status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg);
|
||||
status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
|
||||
// FLAGS_page_size is defaulted to 1024
|
||||
|
@ -447,7 +441,7 @@ class Benchmark {
|
|||
char page_size[100];
|
||||
snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d",
|
||||
FLAGS_page_size);
|
||||
status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg);
|
||||
status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
}
|
||||
|
||||
|
@ -457,26 +451,28 @@ class Benchmark {
|
|||
|
||||
// LevelDB's default cache size is a combined 4 MB
|
||||
std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096";
|
||||
status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg);
|
||||
status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg);
|
||||
status =
|
||||
sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
}
|
||||
|
||||
// Change locking mode to exclusive and create tables/index for database
|
||||
std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE";
|
||||
std::string create_stmt =
|
||||
"CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
|
||||
std::string stmt_array[] = { locking_stmt, create_stmt };
|
||||
"CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))";
|
||||
std::string stmt_array[] = {locking_stmt, create_stmt};
|
||||
int stmt_array_length = sizeof(stmt_array) / sizeof(std::string);
|
||||
for (int i = 0; i < stmt_array_length; i++) {
|
||||
status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg);
|
||||
status =
|
||||
sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(bool write_sync, Order order, DBState state,
|
||||
int num_entries, int value_size, int entries_per_batch) {
|
||||
void Write(bool write_sync, Order order, DBState state, int num_entries,
|
||||
int value_size, int entries_per_batch) {
|
||||
// Create new database if state == FRESH
|
||||
if (state == FRESH) {
|
||||
if (FLAGS_use_existing_db) {
|
||||
|
@ -484,7 +480,7 @@ class Benchmark {
|
|||
return;
|
||||
}
|
||||
sqlite3_close(db_);
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
Open();
|
||||
Start();
|
||||
}
|
||||
|
@ -495,7 +491,7 @@ class Benchmark {
|
|||
message_ = msg;
|
||||
}
|
||||
|
||||
char* err_msg = NULL;
|
||||
char* err_msg = nullptr;
|
||||
int status;
|
||||
|
||||
sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt;
|
||||
|
@ -504,20 +500,20 @@ class Benchmark {
|
|||
std::string end_trans_str = "END TRANSACTION;";
|
||||
|
||||
// Check for synchronous flag in options
|
||||
std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" :
|
||||
"PRAGMA synchronous = OFF";
|
||||
status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg);
|
||||
std::string sync_stmt =
|
||||
(write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF";
|
||||
status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg);
|
||||
ExecErrorCheck(status, err_msg);
|
||||
|
||||
// Preparing sqlite3 statements
|
||||
status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1,
|
||||
&replace_stmt, NULL);
|
||||
status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, &replace_stmt,
|
||||
nullptr);
|
||||
ErrorCheck(status);
|
||||
status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
|
||||
&begin_trans_stmt, NULL);
|
||||
&begin_trans_stmt, nullptr);
|
||||
ErrorCheck(status);
|
||||
status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1,
|
||||
&end_trans_stmt, NULL);
|
||||
status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
|
||||
nullptr);
|
||||
ErrorCheck(status);
|
||||
|
||||
bool transaction = (entries_per_batch > 1);
|
||||
|
@ -535,16 +531,16 @@ class Benchmark {
|
|||
const char* value = gen_.Generate(value_size).data();
|
||||
|
||||
// Create values for key-value pair
|
||||
const int k = (order == SEQUENTIAL) ? i + j :
|
||||
(rand_.Next() % num_entries);
|
||||
const int k =
|
||||
(order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries);
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%016d", k);
|
||||
|
||||
// Bind KV values into replace_stmt
|
||||
status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC);
|
||||
ErrorCheck(status);
|
||||
status = sqlite3_bind_blob(replace_stmt, 2, value,
|
||||
value_size, SQLITE_STATIC);
|
||||
status = sqlite3_bind_blob(replace_stmt, 2, value, value_size,
|
||||
SQLITE_STATIC);
|
||||
ErrorCheck(status);
|
||||
|
||||
// Execute replace_stmt
|
||||
|
@ -588,12 +584,12 @@ class Benchmark {
|
|||
|
||||
// Preparing sqlite3 statements
|
||||
status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1,
|
||||
&begin_trans_stmt, NULL);
|
||||
&begin_trans_stmt, nullptr);
|
||||
ErrorCheck(status);
|
||||
status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1,
|
||||
&end_trans_stmt, NULL);
|
||||
status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt,
|
||||
nullptr);
|
||||
ErrorCheck(status);
|
||||
status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL);
|
||||
status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr);
|
||||
ErrorCheck(status);
|
||||
|
||||
bool transaction = (entries_per_batch > 1);
|
||||
|
@ -618,7 +614,8 @@ class Benchmark {
|
|||
ErrorCheck(status);
|
||||
|
||||
// Execute read statement
|
||||
while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {}
|
||||
while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {
|
||||
}
|
||||
StepErrorCheck(status);
|
||||
|
||||
// Reset SQLite statement for another use
|
||||
|
@ -648,10 +645,10 @@ class Benchmark {
|
|||
|
||||
void ReadSequential() {
|
||||
int status;
|
||||
sqlite3_stmt *pStmt;
|
||||
sqlite3_stmt* pStmt;
|
||||
std::string read_str = "SELECT * FROM test ORDER BY key";
|
||||
|
||||
status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL);
|
||||
status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr);
|
||||
ErrorCheck(status);
|
||||
for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) {
|
||||
bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2);
|
||||
|
@ -661,7 +658,6 @@ class Benchmark {
|
|||
status = sqlite3_finalize(pStmt);
|
||||
ErrorCheck(status);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
@ -706,10 +702,10 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
// Choose a location for the test database if none given with --db=<path>
|
||||
if (FLAGS_db == NULL) {
|
||||
leveldb::Env::Default()->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
if (FLAGS_db == nullptr) {
|
||||
leveldb::Env::Default()->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
}
|
||||
|
||||
leveldb::Benchmark benchmark;
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <kcpolydb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <kcpolydb.h>
|
||||
|
||||
#include "util/histogram.h"
|
||||
#include "util/random.h"
|
||||
#include "util/testutil.h"
|
||||
|
@ -34,8 +35,7 @@ static const char* FLAGS_benchmarks =
|
|||
"fillrand100K,"
|
||||
"fillseq100K,"
|
||||
"readseq100K,"
|
||||
"readrand100K,"
|
||||
;
|
||||
"readrand100K,";
|
||||
|
||||
// Number of key/values to place in database
|
||||
static int FLAGS_num = 1000000;
|
||||
|
@ -69,11 +69,9 @@ static bool FLAGS_use_existing_db = false;
|
|||
static bool FLAGS_compression = true;
|
||||
|
||||
// Use the db with the following name.
|
||||
static const char* FLAGS_db = NULL;
|
||||
static const char* FLAGS_db = nullptr;
|
||||
|
||||
inline
|
||||
static void DBSynchronize(kyotocabinet::TreeDB* db_)
|
||||
{
|
||||
inline static void DBSynchronize(kyotocabinet::TreeDB* db_) {
|
||||
// Synchronize will flush writes to disk
|
||||
if (!db_->synchronize()) {
|
||||
fprintf(stderr, "synchronize error: %s\n", db_->error().name());
|
||||
|
@ -121,7 +119,7 @@ static Slice TrimSpace(Slice s) {
|
|||
start++;
|
||||
}
|
||||
int limit = s.size();
|
||||
while (limit > start && isspace(s[limit-1])) {
|
||||
while (limit > start && isspace(s[limit - 1])) {
|
||||
limit--;
|
||||
}
|
||||
return Slice(s.data() + start, limit - start);
|
||||
|
@ -146,7 +144,7 @@ class Benchmark {
|
|||
|
||||
// State kept for progress messages
|
||||
int done_;
|
||||
int next_report_; // When to report next
|
||||
int next_report_; // When to report next
|
||||
|
||||
void PrintHeader() {
|
||||
const int kKeySize = 16;
|
||||
|
@ -157,20 +155,20 @@ class Benchmark {
|
|||
static_cast<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));
|
||||
((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));
|
||||
(((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) /
|
||||
1048576.0));
|
||||
PrintWarnings();
|
||||
fprintf(stdout, "------------------------------------------------\n");
|
||||
}
|
||||
|
||||
void PrintWarnings() {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
fprintf(stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
|
||||
);
|
||||
fprintf(
|
||||
stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout,
|
||||
|
@ -183,18 +181,18 @@ class Benchmark {
|
|||
kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
|
||||
|
||||
#if defined(__linux)
|
||||
time_t now = time(NULL);
|
||||
time_t now = time(nullptr);
|
||||
fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline
|
||||
|
||||
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
|
||||
if (cpuinfo != NULL) {
|
||||
if (cpuinfo != nullptr) {
|
||||
char line[1000];
|
||||
int num_cpus = 0;
|
||||
std::string cpu_type;
|
||||
std::string cache_size;
|
||||
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
|
||||
while (fgets(line, sizeof(line), cpuinfo) != nullptr) {
|
||||
const char* sep = strchr(line, ':');
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Slice key = TrimSpace(Slice(line, sep - 1 - line));
|
||||
|
@ -237,13 +235,20 @@ class Benchmark {
|
|||
|
||||
done_++;
|
||||
if (done_ >= next_report_) {
|
||||
if (next_report_ < 1000) next_report_ += 100;
|
||||
else if (next_report_ < 5000) next_report_ += 500;
|
||||
else if (next_report_ < 10000) next_report_ += 1000;
|
||||
else if (next_report_ < 50000) next_report_ += 5000;
|
||||
else if (next_report_ < 100000) next_report_ += 10000;
|
||||
else if (next_report_ < 500000) next_report_ += 50000;
|
||||
else next_report_ += 100000;
|
||||
if (next_report_ < 1000)
|
||||
next_report_ += 100;
|
||||
else if (next_report_ < 5000)
|
||||
next_report_ += 500;
|
||||
else if (next_report_ < 10000)
|
||||
next_report_ += 1000;
|
||||
else if (next_report_ < 50000)
|
||||
next_report_ += 5000;
|
||||
else if (next_report_ < 100000)
|
||||
next_report_ += 10000;
|
||||
else if (next_report_ < 500000)
|
||||
next_report_ += 50000;
|
||||
else
|
||||
next_report_ += 100000;
|
||||
fprintf(stderr, "... finished %d ops%30s\r", done_, "");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
@ -261,16 +266,14 @@ class Benchmark {
|
|||
snprintf(rate, sizeof(rate), "%6.1f MB/s",
|
||||
(bytes_ / 1048576.0) / (finish - start_));
|
||||
if (!message_.empty()) {
|
||||
message_ = std::string(rate) + " " + message_;
|
||||
message_ = std::string(rate) + " " + message_;
|
||||
} else {
|
||||
message_ = rate;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
|
||||
name.ToString().c_str(),
|
||||
(finish - start_) * 1e6 / done_,
|
||||
(message_.empty() ? "" : " "),
|
||||
fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(),
|
||||
(finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "),
|
||||
message_.c_str());
|
||||
if (FLAGS_histogram) {
|
||||
fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
|
||||
|
@ -279,21 +282,15 @@ class Benchmark {
|
|||
}
|
||||
|
||||
public:
|
||||
enum Order {
|
||||
SEQUENTIAL,
|
||||
RANDOM
|
||||
};
|
||||
enum DBState {
|
||||
FRESH,
|
||||
EXISTING
|
||||
};
|
||||
enum Order { SEQUENTIAL, RANDOM };
|
||||
enum DBState { FRESH, EXISTING };
|
||||
|
||||
Benchmark()
|
||||
: db_(NULL),
|
||||
num_(FLAGS_num),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
bytes_(0),
|
||||
rand_(301) {
|
||||
: db_(nullptr),
|
||||
num_(FLAGS_num),
|
||||
reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
|
||||
bytes_(0),
|
||||
rand_(301) {
|
||||
std::vector<std::string> files;
|
||||
std::string test_dir;
|
||||
Env::Default()->GetTestDirectory(&test_dir);
|
||||
|
@ -321,12 +318,12 @@ class Benchmark {
|
|||
Open(false);
|
||||
|
||||
const char* benchmarks = FLAGS_benchmarks;
|
||||
while (benchmarks != NULL) {
|
||||
while (benchmarks != nullptr) {
|
||||
const char* sep = strchr(benchmarks, ',');
|
||||
Slice name;
|
||||
if (sep == NULL) {
|
||||
if (sep == nullptr) {
|
||||
name = benchmarks;
|
||||
benchmarks = NULL;
|
||||
benchmarks = nullptr;
|
||||
} else {
|
||||
name = Slice(benchmarks, sep - benchmarks);
|
||||
benchmarks = sep + 1;
|
||||
|
@ -386,8 +383,8 @@ class Benchmark {
|
|||
}
|
||||
|
||||
private:
|
||||
void Open(bool sync) {
|
||||
assert(db_ == NULL);
|
||||
void Open(bool sync) {
|
||||
assert(db_ == nullptr);
|
||||
|
||||
// Initialize db_
|
||||
db_ = new kyotocabinet::TreeDB();
|
||||
|
@ -395,16 +392,14 @@ class Benchmark {
|
|||
db_num_++;
|
||||
std::string test_dir;
|
||||
Env::Default()->GetTestDirectory(&test_dir);
|
||||
snprintf(file_name, sizeof(file_name),
|
||||
"%s/dbbench_polyDB-%d.kct",
|
||||
test_dir.c_str(),
|
||||
db_num_);
|
||||
snprintf(file_name, sizeof(file_name), "%s/dbbench_polyDB-%d.kct",
|
||||
test_dir.c_str(), db_num_);
|
||||
|
||||
// Create tuning options and open the database
|
||||
int open_options = kyotocabinet::PolyDB::OWRITER |
|
||||
kyotocabinet::PolyDB::OCREATE;
|
||||
int tune_options = kyotocabinet::TreeDB::TSMALL |
|
||||
kyotocabinet::TreeDB::TLINEAR;
|
||||
int open_options =
|
||||
kyotocabinet::PolyDB::OWRITER | kyotocabinet::PolyDB::OCREATE;
|
||||
int tune_options =
|
||||
kyotocabinet::TreeDB::TSMALL | kyotocabinet::TreeDB::TLINEAR;
|
||||
if (FLAGS_compression) {
|
||||
tune_options |= kyotocabinet::TreeDB::TCOMPRESS;
|
||||
db_->tune_compressor(&comp_);
|
||||
|
@ -412,7 +407,7 @@ class Benchmark {
|
|||
db_->tune_options(tune_options);
|
||||
db_->tune_page_cache(FLAGS_cache_size);
|
||||
db_->tune_page(FLAGS_page_size);
|
||||
db_->tune_map(256LL<<20);
|
||||
db_->tune_map(256LL << 20);
|
||||
if (sync) {
|
||||
open_options |= kyotocabinet::PolyDB::OAUTOSYNC;
|
||||
}
|
||||
|
@ -421,8 +416,8 @@ class Benchmark {
|
|||
}
|
||||
}
|
||||
|
||||
void Write(bool sync, Order order, DBState state,
|
||||
int num_entries, int value_size, int entries_per_batch) {
|
||||
void Write(bool sync, Order order, DBState state, int num_entries,
|
||||
int value_size, int entries_per_batch) {
|
||||
// Create new database if state == FRESH
|
||||
if (state == FRESH) {
|
||||
if (FLAGS_use_existing_db) {
|
||||
|
@ -430,7 +425,7 @@ class Benchmark {
|
|||
return;
|
||||
}
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
Open(sync);
|
||||
Start(); // Do not count time taken to destroy/open
|
||||
}
|
||||
|
@ -442,8 +437,7 @@ class Benchmark {
|
|||
}
|
||||
|
||||
// Write to database
|
||||
for (int i = 0; i < num_entries; i++)
|
||||
{
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries);
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%016d", k);
|
||||
|
@ -516,10 +510,10 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
// Choose a location for the test database if none given with --db=<path>
|
||||
if (FLAGS_db == NULL) {
|
||||
leveldb::Env::Default()->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
if (FLAGS_db == nullptr) {
|
||||
leveldb::Env::Default()->GetTestDirectory(&default_db_path);
|
||||
default_db_path += "/dbbench";
|
||||
FLAGS_db = default_db_path.c_str();
|
||||
}
|
||||
|
||||
leveldb::Benchmark benchmark;
|
|
@ -1,259 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Detects OS we're compiling on and outputs a file specified by the first
|
||||
# argument, which in turn gets read while processing Makefile.
|
||||
#
|
||||
# The output will set the following variables:
|
||||
# CC C Compiler path
|
||||
# CXX C++ Compiler path
|
||||
# PLATFORM_LDFLAGS Linker flags
|
||||
# PLATFORM_LIBS Libraries flags
|
||||
# PLATFORM_SHARED_EXT Extension for shared libraries
|
||||
# PLATFORM_SHARED_LDFLAGS Flags for building shared library
|
||||
# This flag is embedded just before the name
|
||||
# of the shared library without intervening spaces
|
||||
# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library
|
||||
# PLATFORM_CCFLAGS C compiler flags
|
||||
# PLATFORM_CXXFLAGS C++ compiler flags. Will contain:
|
||||
# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned
|
||||
# shared libraries, empty otherwise.
|
||||
#
|
||||
# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following:
|
||||
#
|
||||
# -DLEVELDB_ATOMIC_PRESENT if <atomic> is present
|
||||
# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms
|
||||
# -DSNAPPY if the Snappy library is present
|
||||
#
|
||||
|
||||
OUTPUT=$1
|
||||
PREFIX=$2
|
||||
if test -z "$OUTPUT" || test -z "$PREFIX"; then
|
||||
echo "usage: $0 <output-filename> <directory_prefix>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
rm -f $OUTPUT
|
||||
touch $OUTPUT
|
||||
|
||||
if test -z "$CC"; then
|
||||
CC=cc
|
||||
fi
|
||||
|
||||
if test -z "$CXX"; then
|
||||
CXX=g++
|
||||
fi
|
||||
|
||||
if test -z "$TMPDIR"; then
|
||||
TMPDIR=/tmp
|
||||
fi
|
||||
|
||||
# Detect OS
|
||||
if test -z "$TARGET_OS"; then
|
||||
TARGET_OS=`uname -s`
|
||||
fi
|
||||
|
||||
COMMON_FLAGS=
|
||||
CROSS_COMPILE=
|
||||
PLATFORM_CCFLAGS=
|
||||
PLATFORM_CXXFLAGS=
|
||||
PLATFORM_LDFLAGS=
|
||||
PLATFORM_LIBS=
|
||||
PLATFORM_SHARED_EXT="so"
|
||||
PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl,"
|
||||
PLATFORM_SHARED_CFLAGS="-fPIC"
|
||||
PLATFORM_SHARED_VERSIONED=true
|
||||
PLATFORM_SSEFLAGS=
|
||||
|
||||
MEMCMP_FLAG=
|
||||
if [ "$CXX" = "g++" ]; then
|
||||
# Use libc's memcmp instead of GCC's memcmp. This results in ~40%
|
||||
# performance improvement on readrandom under gcc 4.4.3 on Linux/x86.
|
||||
MEMCMP_FLAG="-fno-builtin-memcmp"
|
||||
fi
|
||||
|
||||
case "$TARGET_OS" in
|
||||
CYGWIN_*)
|
||||
PLATFORM=OS_LINUX
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN"
|
||||
PLATFORM_LDFLAGS="-lpthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
Darwin)
|
||||
PLATFORM=OS_MACOSX
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX"
|
||||
PLATFORM_SHARED_EXT=dylib
|
||||
[ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd`
|
||||
PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
Linux)
|
||||
PLATFORM=OS_LINUX
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX"
|
||||
PLATFORM_LDFLAGS="-pthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
SunOS)
|
||||
PLATFORM=OS_SOLARIS
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS"
|
||||
PLATFORM_LIBS="-lpthread -lrt"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
FreeBSD)
|
||||
PLATFORM=OS_FREEBSD
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD"
|
||||
PLATFORM_LIBS="-lpthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
PLATFORM=OS_KFREEBSD
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD"
|
||||
PLATFORM_LIBS="-lpthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
;;
|
||||
NetBSD)
|
||||
PLATFORM=OS_NETBSD
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD"
|
||||
PLATFORM_LIBS="-lpthread -lgcc_s"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
OpenBSD)
|
||||
PLATFORM=OS_OPENBSD
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD"
|
||||
PLATFORM_LDFLAGS="-pthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
DragonFly)
|
||||
PLATFORM=OS_DRAGONFLYBSD
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD"
|
||||
PLATFORM_LIBS="-lpthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
;;
|
||||
OS_ANDROID_CROSSCOMPILE)
|
||||
PLATFORM=OS_ANDROID
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
|
||||
PLATFORM_LDFLAGS="" # All pthread features are in the Android C library
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
CROSS_COMPILE=true
|
||||
;;
|
||||
HP-UX)
|
||||
PLATFORM=OS_HPUX
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX"
|
||||
PLATFORM_LDFLAGS="-pthread"
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
# man ld: +h internal_name
|
||||
PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl,"
|
||||
;;
|
||||
IOS)
|
||||
PLATFORM=IOS
|
||||
COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX"
|
||||
[ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd`
|
||||
PORT_FILE=port/port_posix.cc
|
||||
PORT_SSE_FILE=port/port_posix_sse.cc
|
||||
PLATFORM_SHARED_EXT=
|
||||
PLATFORM_SHARED_LDFLAGS=
|
||||
PLATFORM_SHARED_CFLAGS=
|
||||
PLATFORM_SHARED_VERSIONED=
|
||||
;;
|
||||
OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS)
|
||||
PLATFORM=OS_WINDOWS
|
||||
COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1"
|
||||
PLATFORM_SOURCES="util/env_win.cc"
|
||||
PLATFORM_LIBS="-lshlwapi"
|
||||
PORT_FILE=port/port_win.cc
|
||||
CROSS_COMPILE=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown platform!" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
# We want to make a list of all cc files within util, db, table, and helpers
|
||||
# except for the test and benchmark files. By default, find will output a list
|
||||
# of all files matching either rule, so we need to append -print to make the
|
||||
# prune take effect.
|
||||
DIRS="$PREFIX/db $PREFIX/util $PREFIX/table"
|
||||
|
||||
set -f # temporarily disable globbing so that our patterns aren't expanded
|
||||
PRUNE_TEST="-name *test*.cc -prune"
|
||||
PRUNE_BENCH="-name *_bench.cc -prune"
|
||||
PRUNE_TOOL="-name leveldbutil.cc -prune"
|
||||
PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "`
|
||||
|
||||
set +f # re-enable globbing
|
||||
|
||||
# The sources consist of the portable files, plus the platform-specific port
|
||||
# file.
|
||||
echo "SOURCES=$PORTABLE_FILES $PORT_FILE $PORT_SSE_FILE" >> $OUTPUT
|
||||
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" ]; then
|
||||
# Cross-compiling; do not try any compilation tests.
|
||||
true
|
||||
else
|
||||
CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$"
|
||||
|
||||
# If -std=c++0x works, use <atomic> as fallback for when memory barriers
|
||||
# are not available.
|
||||
$CXX $CXXFLAGS -std=c++0x -x c++ - -o $CXXOUTPUT 2>/dev/null <<EOF
|
||||
#include <atomic>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT"
|
||||
PLATFORM_CXXFLAGS="-std=c++0x"
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX"
|
||||
fi
|
||||
|
||||
# Test whether tcmalloc is available
|
||||
$CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LIBS="$PLATFORM_LIBS -ltcmalloc"
|
||||
fi
|
||||
|
||||
rm -f $CXXOUTPUT 2>/dev/null
|
||||
|
||||
# Test if gcc SSE 4.2 is supported
|
||||
$CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -msse4.2 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_SSEFLAGS="-msse4.2"
|
||||
fi
|
||||
|
||||
rm -f $CXXOUTPUT 2>/dev/null
|
||||
fi
|
||||
|
||||
# Use the SSE 4.2 CRC32C intrinsics iff runtime checks indicate compiler supports them.
|
||||
if [ -n "$PLATFORM_SSEFLAGS" ]; then
|
||||
PLATFORM_SSEFLAGS="$PLATFORM_SSEFLAGS -DLEVELDB_PLATFORM_POSIX_SSE"
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
echo "CC=$CC" >> $OUTPUT
|
||||
echo "CXX=$CXX" >> $OUTPUT
|
||||
echo "PLATFORM=$PLATFORM" >> $OUTPUT
|
||||
echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT
|
||||
echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_SSEFLAGS=$PLATFORM_SSEFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT
|
||||
echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT
|
1
src/leveldb/cmake/leveldbConfig.cmake
Normal file
1
src/leveldb/cmake/leveldbConfig.cmake
Normal file
|
@ -0,0 +1 @@
|
|||
include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake")
|
|
@ -2,9 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/db.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
|
@ -12,11 +12,6 @@ namespace leveldb {
|
|||
|
||||
class AutoCompactTest {
|
||||
public:
|
||||
std::string dbname_;
|
||||
Cache* tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
AutoCompactTest() {
|
||||
dbname_ = test::TmpDir() + "/autocompact_test";
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
|
@ -47,6 +42,12 @@ class AutoCompactTest {
|
|||
}
|
||||
|
||||
void DoReads(int n);
|
||||
|
||||
private:
|
||||
std::string dbname_;
|
||||
Cache* tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
};
|
||||
|
||||
static const int kValueSize = 200 * 1024;
|
||||
|
@ -81,17 +82,16 @@ void AutoCompactTest::DoReads(int n) {
|
|||
ASSERT_LT(read, 100) << "Taking too long to compact";
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst();
|
||||
iter->Valid() && iter->key().ToString() < limit_key;
|
||||
iter->Next()) {
|
||||
iter->Valid() && iter->key().ToString() < limit_key; iter->Next()) {
|
||||
// Drop data
|
||||
}
|
||||
delete iter;
|
||||
// Wait a little bit to allow any triggered compactions to complete.
|
||||
Env::Default()->SleepForMicroseconds(1000000);
|
||||
uint64_t size = Size(Key(0), Key(n));
|
||||
fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n",
|
||||
read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0);
|
||||
if (size <= initial_size/10) {
|
||||
fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", read + 1,
|
||||
size / 1048576.0, Size(Key(n), Key(kCount)) / 1048576.0);
|
||||
if (size <= initial_size / 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -100,19 +100,13 @@ void AutoCompactTest::DoReads(int n) {
|
|||
// is pretty much unchanged.
|
||||
const int64_t final_other_size = Size(Key(n), Key(kCount));
|
||||
ASSERT_LE(final_other_size, initial_other_size + 1048576);
|
||||
ASSERT_GE(final_other_size, initial_other_size/5 - 1048576);
|
||||
ASSERT_GE(final_other_size, initial_other_size / 5 - 1048576);
|
||||
}
|
||||
|
||||
TEST(AutoCompactTest, ReadAll) {
|
||||
DoReads(kCount);
|
||||
}
|
||||
TEST(AutoCompactTest, ReadAll) { DoReads(kCount); }
|
||||
|
||||
TEST(AutoCompactTest, ReadHalf) {
|
||||
DoReads(kCount/2);
|
||||
}
|
||||
TEST(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); }
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#include "db/builder.h"
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "leveldb/db.h"
|
||||
|
@ -14,12 +14,8 @@
|
|||
|
||||
namespace leveldb {
|
||||
|
||||
Status BuildTable(const std::string& dbname,
|
||||
Env* env,
|
||||
const Options& options,
|
||||
TableCache* table_cache,
|
||||
Iterator* iter,
|
||||
FileMetaData* meta) {
|
||||
Status BuildTable(const std::string& dbname, Env* env, const Options& options,
|
||||
TableCache* table_cache, Iterator* iter, FileMetaData* meta) {
|
||||
Status s;
|
||||
meta->file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
|
@ -41,14 +37,10 @@ Status BuildTable(const std::string& dbname,
|
|||
}
|
||||
|
||||
// Finish and check for builder errors
|
||||
s = builder->Finish();
|
||||
if (s.ok()) {
|
||||
s = builder->Finish();
|
||||
if (s.ok()) {
|
||||
meta->file_size = builder->FileSize();
|
||||
assert(meta->file_size > 0);
|
||||
}
|
||||
} else {
|
||||
builder->Abandon();
|
||||
meta->file_size = builder->FileSize();
|
||||
assert(meta->file_size > 0);
|
||||
}
|
||||
delete builder;
|
||||
|
||||
|
@ -60,12 +52,11 @@ Status BuildTable(const std::string& dbname,
|
|||
s = file->Close();
|
||||
}
|
||||
delete file;
|
||||
file = NULL;
|
||||
file = nullptr;
|
||||
|
||||
if (s.ok()) {
|
||||
// Verify that the table is usable
|
||||
Iterator* it = table_cache->NewIterator(ReadOptions(),
|
||||
meta->number,
|
||||
Iterator* it = table_cache->NewIterator(ReadOptions(), meta->number,
|
||||
meta->file_size);
|
||||
s = it->status();
|
||||
delete it;
|
||||
|
|
|
@ -22,12 +22,8 @@ class VersionEdit;
|
|||
// *meta will be filled with metadata about the generated table.
|
||||
// If no data is present in *iter, meta->file_size will be set to
|
||||
// zero, and no Table file will be produced.
|
||||
extern Status BuildTable(const std::string& dbname,
|
||||
Env* env,
|
||||
const Options& options,
|
||||
TableCache* table_cache,
|
||||
Iterator* iter,
|
||||
FileMetaData* meta);
|
||||
Status BuildTable(const std::string& dbname, Env* env, const Options& options,
|
||||
TableCache* table_cache, Iterator* iter, FileMetaData* meta);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
|
||||
#include "leveldb/c.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/db.h"
|
||||
|
@ -45,69 +44,72 @@ using leveldb::WriteOptions;
|
|||
|
||||
extern "C" {
|
||||
|
||||
struct leveldb_t { DB* rep; };
|
||||
struct leveldb_iterator_t { Iterator* rep; };
|
||||
struct leveldb_writebatch_t { WriteBatch rep; };
|
||||
struct leveldb_snapshot_t { const Snapshot* rep; };
|
||||
struct leveldb_readoptions_t { ReadOptions rep; };
|
||||
struct leveldb_writeoptions_t { WriteOptions rep; };
|
||||
struct leveldb_options_t { Options rep; };
|
||||
struct leveldb_cache_t { Cache* rep; };
|
||||
struct leveldb_seqfile_t { SequentialFile* rep; };
|
||||
struct leveldb_randomfile_t { RandomAccessFile* rep; };
|
||||
struct leveldb_writablefile_t { WritableFile* rep; };
|
||||
struct leveldb_logger_t { Logger* rep; };
|
||||
struct leveldb_filelock_t { FileLock* rep; };
|
||||
struct leveldb_t {
|
||||
DB* rep;
|
||||
};
|
||||
struct leveldb_iterator_t {
|
||||
Iterator* rep;
|
||||
};
|
||||
struct leveldb_writebatch_t {
|
||||
WriteBatch rep;
|
||||
};
|
||||
struct leveldb_snapshot_t {
|
||||
const Snapshot* rep;
|
||||
};
|
||||
struct leveldb_readoptions_t {
|
||||
ReadOptions rep;
|
||||
};
|
||||
struct leveldb_writeoptions_t {
|
||||
WriteOptions rep;
|
||||
};
|
||||
struct leveldb_options_t {
|
||||
Options rep;
|
||||
};
|
||||
struct leveldb_cache_t {
|
||||
Cache* rep;
|
||||
};
|
||||
struct leveldb_seqfile_t {
|
||||
SequentialFile* rep;
|
||||
};
|
||||
struct leveldb_randomfile_t {
|
||||
RandomAccessFile* rep;
|
||||
};
|
||||
struct leveldb_writablefile_t {
|
||||
WritableFile* rep;
|
||||
};
|
||||
struct leveldb_logger_t {
|
||||
Logger* rep;
|
||||
};
|
||||
struct leveldb_filelock_t {
|
||||
FileLock* rep;
|
||||
};
|
||||
|
||||
struct leveldb_comparator_t : public Comparator {
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
int (*compare_)(
|
||||
void*,
|
||||
const char* a, size_t alen,
|
||||
const char* b, size_t blen);
|
||||
const char* (*name_)(void*);
|
||||
~leveldb_comparator_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual ~leveldb_comparator_t() {
|
||||
(*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*);
|
||||
int (*compare_)(void*, const char* a, size_t alen, const char* b,
|
||||
size_t blen);
|
||||
const char* (*name_)(void*);
|
||||
};
|
||||
|
||||
struct leveldb_filterpolicy_t : public FilterPolicy {
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
char* (*create_)(
|
||||
void*,
|
||||
const char* const* key_array, const size_t* key_length_array,
|
||||
int num_keys,
|
||||
size_t* filter_length);
|
||||
unsigned char (*key_match_)(
|
||||
void*,
|
||||
const char* key, size_t length,
|
||||
const char* filter, size_t filter_length);
|
||||
~leveldb_filterpolicy_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual ~leveldb_filterpolicy_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual const char* Name() const {
|
||||
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++) {
|
||||
|
@ -120,10 +122,19 @@ struct leveldb_filterpolicy_t : public FilterPolicy {
|
|||
free(filter);
|
||||
}
|
||||
|
||||
virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
||||
return (*key_match_)(state_, key.data(), key.size(),
|
||||
filter.data(), filter.size());
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return (*key_match_)(state_, key.data(), key.size(), filter.data(),
|
||||
filter.size());
|
||||
}
|
||||
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
char* (*create_)(void*, const char* const* key_array,
|
||||
const size_t* key_length_array, int num_keys,
|
||||
size_t* filter_length);
|
||||
uint8_t (*key_match_)(void*, const char* key, size_t length,
|
||||
const char* filter, size_t filter_length);
|
||||
};
|
||||
|
||||
struct leveldb_env_t {
|
||||
|
@ -132,10 +143,10 @@ struct leveldb_env_t {
|
|||
};
|
||||
|
||||
static bool SaveError(char** errptr, const Status& s) {
|
||||
assert(errptr != NULL);
|
||||
assert(errptr != nullptr);
|
||||
if (s.ok()) {
|
||||
return false;
|
||||
} else if (*errptr == NULL) {
|
||||
} else if (*errptr == nullptr) {
|
||||
*errptr = strdup(s.ToString().c_str());
|
||||
} else {
|
||||
// TODO(sanjay): Merge with existing error?
|
||||
|
@ -151,13 +162,11 @@ static char* CopyString(const std::string& str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
leveldb_t* leveldb_open(
|
||||
const leveldb_options_t* options,
|
||||
const char* name,
|
||||
char** errptr) {
|
||||
leveldb_t* leveldb_open(const leveldb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
DB* db;
|
||||
if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
leveldb_t* result = new leveldb_t;
|
||||
result->rep = db;
|
||||
|
@ -169,40 +178,27 @@ void leveldb_close(leveldb_t* db) {
|
|||
delete db;
|
||||
}
|
||||
|
||||
void leveldb_put(
|
||||
leveldb_t* db,
|
||||
const leveldb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
void leveldb_put(leveldb_t* db, const leveldb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr,
|
||||
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void leveldb_delete(
|
||||
leveldb_t* db,
|
||||
const leveldb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
char** errptr) {
|
||||
void leveldb_delete(leveldb_t* db, const leveldb_writeoptions_t* options,
|
||||
const char* key, size_t keylen, char** errptr) {
|
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen)));
|
||||
}
|
||||
|
||||
|
||||
void leveldb_write(
|
||||
leveldb_t* db,
|
||||
const leveldb_writeoptions_t* options,
|
||||
leveldb_writebatch_t* batch,
|
||||
char** errptr) {
|
||||
void leveldb_write(leveldb_t* db, const leveldb_writeoptions_t* options,
|
||||
leveldb_writebatch_t* batch, char** errptr) {
|
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
char* leveldb_get(
|
||||
leveldb_t* db,
|
||||
const leveldb_readoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = NULL;
|
||||
char* leveldb_get(leveldb_t* db, const leveldb_readoptions_t* options,
|
||||
const char* key, size_t keylen, size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
|
@ -218,45 +214,40 @@ char* leveldb_get(
|
|||
}
|
||||
|
||||
leveldb_iterator_t* leveldb_create_iterator(
|
||||
leveldb_t* db,
|
||||
const leveldb_readoptions_t* options) {
|
||||
leveldb_t* db, const leveldb_readoptions_t* options) {
|
||||
leveldb_iterator_t* result = new leveldb_iterator_t;
|
||||
result->rep = db->rep->NewIterator(options->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
const leveldb_snapshot_t* leveldb_create_snapshot(
|
||||
leveldb_t* db) {
|
||||
const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db) {
|
||||
leveldb_snapshot_t* result = new leveldb_snapshot_t;
|
||||
result->rep = db->rep->GetSnapshot();
|
||||
return result;
|
||||
}
|
||||
|
||||
void leveldb_release_snapshot(
|
||||
leveldb_t* db,
|
||||
const leveldb_snapshot_t* snapshot) {
|
||||
void leveldb_release_snapshot(leveldb_t* db,
|
||||
const leveldb_snapshot_t* snapshot) {
|
||||
db->rep->ReleaseSnapshot(snapshot->rep);
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
char* leveldb_property_value(
|
||||
leveldb_t* db,
|
||||
const char* propname) {
|
||||
char* leveldb_property_value(leveldb_t* db, const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
// We use strdup() since we expect human readable output.
|
||||
return strdup(tmp.c_str());
|
||||
} else {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void leveldb_approximate_sizes(
|
||||
leveldb_t* db,
|
||||
int num_ranges,
|
||||
const char* const* range_start_key, const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes) {
|
||||
void leveldb_approximate_sizes(leveldb_t* db, int num_ranges,
|
||||
const char* const* range_start_key,
|
||||
const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len,
|
||||
uint64_t* sizes) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
|
@ -266,28 +257,23 @@ void leveldb_approximate_sizes(
|
|||
delete[] ranges;
|
||||
}
|
||||
|
||||
void leveldb_compact_range(
|
||||
leveldb_t* db,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len) {
|
||||
void leveldb_compact_range(leveldb_t* db, const char* start_key,
|
||||
size_t start_key_len, const char* limit_key,
|
||||
size_t limit_key_len) {
|
||||
Slice a, b;
|
||||
db->rep->CompactRange(
|
||||
// Pass NULL Slice if corresponding "const char*" is NULL
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : NULL),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL));
|
||||
// Pass null Slice if corresponding "const char*" is null
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void leveldb_destroy_db(
|
||||
const leveldb_options_t* options,
|
||||
const char* name,
|
||||
char** errptr) {
|
||||
void leveldb_destroy_db(const leveldb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, DestroyDB(name, options->rep));
|
||||
}
|
||||
|
||||
void leveldb_repair_db(
|
||||
const leveldb_options_t* options,
|
||||
const char* name,
|
||||
char** errptr) {
|
||||
void leveldb_repair_db(const leveldb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
SaveError(errptr, RepairDB(name, options->rep));
|
||||
}
|
||||
|
||||
|
@ -296,7 +282,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();
|
||||
}
|
||||
|
||||
|
@ -312,13 +298,9 @@ void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) {
|
|||
iter->rep->Seek(Slice(k, klen));
|
||||
}
|
||||
|
||||
void leveldb_iter_next(leveldb_iterator_t* iter) {
|
||||
iter->rep->Next();
|
||||
}
|
||||
void leveldb_iter_next(leveldb_iterator_t* iter) { iter->rep->Next(); }
|
||||
|
||||
void leveldb_iter_prev(leveldb_iterator_t* iter) {
|
||||
iter->rep->Prev();
|
||||
}
|
||||
void leveldb_iter_prev(leveldb_iterator_t* iter) { iter->rep->Prev(); }
|
||||
|
||||
const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) {
|
||||
Slice s = iter->rep->key();
|
||||
|
@ -340,41 +322,34 @@ leveldb_writebatch_t* leveldb_writebatch_create() {
|
|||
return new leveldb_writebatch_t;
|
||||
}
|
||||
|
||||
void leveldb_writebatch_destroy(leveldb_writebatch_t* b) {
|
||||
delete b;
|
||||
}
|
||||
void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { delete b; }
|
||||
|
||||
void leveldb_writebatch_clear(leveldb_writebatch_t* b) {
|
||||
b->rep.Clear();
|
||||
}
|
||||
void leveldb_writebatch_clear(leveldb_writebatch_t* b) { b->rep.Clear(); }
|
||||
|
||||
void leveldb_writebatch_put(
|
||||
leveldb_writebatch_t* b,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
void leveldb_writebatch_put(leveldb_writebatch_t* b, const char* key,
|
||||
size_t klen, const char* val, size_t vlen) {
|
||||
b->rep.Put(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void leveldb_writebatch_delete(
|
||||
leveldb_writebatch_t* b,
|
||||
const char* key, size_t klen) {
|
||||
void leveldb_writebatch_delete(leveldb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void leveldb_writebatch_iterate(
|
||||
leveldb_writebatch_t* b,
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
void leveldb_writebatch_iterate(const leveldb_writebatch_t* b, void* state,
|
||||
void (*put)(void*, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k,
|
||||
size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
@ -385,47 +360,43 @@ void leveldb_writebatch_iterate(
|
|||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
leveldb_options_t* leveldb_options_create() {
|
||||
return new leveldb_options_t;
|
||||
void leveldb_writebatch_append(leveldb_writebatch_t* destination,
|
||||
const leveldb_writebatch_t* source) {
|
||||
destination->rep.Append(source->rep);
|
||||
}
|
||||
|
||||
void leveldb_options_destroy(leveldb_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
leveldb_options_t* leveldb_options_create() { return new leveldb_options_t; }
|
||||
|
||||
void leveldb_options_set_comparator(
|
||||
leveldb_options_t* opt,
|
||||
leveldb_comparator_t* cmp) {
|
||||
void leveldb_options_destroy(leveldb_options_t* options) { delete options; }
|
||||
|
||||
void leveldb_options_set_comparator(leveldb_options_t* opt,
|
||||
leveldb_comparator_t* cmp) {
|
||||
opt->rep.comparator = cmp;
|
||||
}
|
||||
|
||||
void leveldb_options_set_filter_policy(
|
||||
leveldb_options_t* opt,
|
||||
leveldb_filterpolicy_t* policy) {
|
||||
void leveldb_options_set_filter_policy(leveldb_options_t* opt,
|
||||
leveldb_filterpolicy_t* policy) {
|
||||
opt->rep.filter_policy = policy;
|
||||
}
|
||||
|
||||
void leveldb_options_set_create_if_missing(
|
||||
leveldb_options_t* opt, unsigned char v) {
|
||||
void leveldb_options_set_create_if_missing(leveldb_options_t* opt, 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;
|
||||
}
|
||||
|
||||
void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) {
|
||||
opt->rep.env = (env ? env->rep : NULL);
|
||||
opt->rep.env = (env ? env->rep : nullptr);
|
||||
}
|
||||
|
||||
void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) {
|
||||
opt->rep.info_log = (l ? l->rep : NULL);
|
||||
opt->rep.info_log = (l ? l->rep : nullptr);
|
||||
}
|
||||
|
||||
void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) {
|
||||
|
@ -448,17 +419,18 @@ void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) {
|
|||
opt->rep.block_restart_interval = n;
|
||||
}
|
||||
|
||||
void leveldb_options_set_max_file_size(leveldb_options_t* opt, size_t s) {
|
||||
opt->rep.max_file_size = s;
|
||||
}
|
||||
|
||||
void leveldb_options_set_compression(leveldb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
leveldb_comparator_t* leveldb_comparator_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
int (*compare)(
|
||||
void*,
|
||||
const char* a, size_t alen,
|
||||
const char* b, size_t blen),
|
||||
void* state, void (*destructor)(void*),
|
||||
int (*compare)(void*, const char* a, size_t alen, const char* b,
|
||||
size_t blen),
|
||||
const char* (*name)(void*)) {
|
||||
leveldb_comparator_t* result = new leveldb_comparator_t;
|
||||
result->state_ = state;
|
||||
|
@ -468,22 +440,15 @@ leveldb_comparator_t* leveldb_comparator_create(
|
|||
return result;
|
||||
}
|
||||
|
||||
void leveldb_comparator_destroy(leveldb_comparator_t* cmp) {
|
||||
delete cmp;
|
||||
}
|
||||
void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { delete cmp; }
|
||||
|
||||
leveldb_filterpolicy_t* leveldb_filterpolicy_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
char* (*create_filter)(
|
||||
void*,
|
||||
const char* const* key_array, const size_t* key_length_array,
|
||||
int num_keys,
|
||||
size_t* filter_length),
|
||||
unsigned char (*key_may_match)(
|
||||
void*,
|
||||
const char* key, size_t length,
|
||||
const char* filter, size_t filter_length),
|
||||
void* state, void (*destructor)(void*),
|
||||
char* (*create_filter)(void*, const char* const* key_array,
|
||||
const size_t* key_length_array, int num_keys,
|
||||
size_t* filter_length),
|
||||
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;
|
||||
|
@ -503,7 +468,8 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) {
|
|||
// they delegate to a NewBloomFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
struct Wrapper : public leveldb_filterpolicy_t {
|
||||
const FilterPolicy* rep_;
|
||||
static void DoNothing(void*) {}
|
||||
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
||||
|
@ -512,11 +478,12 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) {
|
|||
bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
||||
return rep_->KeyMayMatch(key, filter);
|
||||
}
|
||||
static void DoNothing(void*) { }
|
||||
|
||||
const FilterPolicy* rep_;
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = NewBloomFilterPolicy(bits_per_key);
|
||||
wrapper->state_ = NULL;
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
return wrapper;
|
||||
}
|
||||
|
@ -525,37 +492,29 @@ leveldb_readoptions_t* leveldb_readoptions_create() {
|
|||
return new leveldb_readoptions_t;
|
||||
}
|
||||
|
||||
void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { delete opt; }
|
||||
|
||||
void leveldb_readoptions_set_verify_checksums(
|
||||
leveldb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
void leveldb_readoptions_set_verify_checksums(leveldb_readoptions_t* opt,
|
||||
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;
|
||||
}
|
||||
|
||||
void leveldb_readoptions_set_snapshot(
|
||||
leveldb_readoptions_t* opt,
|
||||
const leveldb_snapshot_t* snap) {
|
||||
opt->rep.snapshot = (snap ? snap->rep : NULL);
|
||||
void leveldb_readoptions_set_snapshot(leveldb_readoptions_t* opt,
|
||||
const leveldb_snapshot_t* snap) {
|
||||
opt->rep.snapshot = (snap ? snap->rep : nullptr);
|
||||
}
|
||||
|
||||
leveldb_writeoptions_t* leveldb_writeoptions_create() {
|
||||
return new leveldb_writeoptions_t;
|
||||
}
|
||||
|
||||
void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { delete opt; }
|
||||
|
||||
void leveldb_writeoptions_set_sync(
|
||||
leveldb_writeoptions_t* opt, unsigned char v) {
|
||||
void leveldb_writeoptions_set_sync(leveldb_writeoptions_t* opt, uint8_t v) {
|
||||
opt->rep.sync = v;
|
||||
}
|
||||
|
||||
|
@ -582,16 +541,22 @@ void leveldb_env_destroy(leveldb_env_t* env) {
|
|||
delete env;
|
||||
}
|
||||
|
||||
void leveldb_free(void* ptr) {
|
||||
free(ptr);
|
||||
char* leveldb_env_get_test_directory(leveldb_env_t* env) {
|
||||
std::string result;
|
||||
if (!env->rep->GetTestDirectory(&result).ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* buffer = static_cast<char*>(malloc(result.size() + 1));
|
||||
memcpy(buffer, result.data(), result.size());
|
||||
buffer[result.size()] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int leveldb_major_version() {
|
||||
return kMajorVersion;
|
||||
}
|
||||
void leveldb_free(void* ptr) { free(ptr); }
|
||||
|
||||
int leveldb_minor_version() {
|
||||
return kMinorVersion;
|
||||
}
|
||||
int leveldb_major_version() { return kMajorVersion; }
|
||||
|
||||
int leveldb_minor_version() { return kMinorVersion; }
|
||||
|
||||
} // end extern "C"
|
||||
|
|
|
@ -8,24 +8,14 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const char* phase = "";
|
||||
static char dbname[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
phase = name;
|
||||
}
|
||||
|
||||
static const char* GetTempDir(void) {
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
ret = "/tmp";
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CheckNoError(err) \
|
||||
if ((err) != NULL) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
|
||||
|
@ -130,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";
|
||||
|
@ -145,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;
|
||||
|
@ -162,21 +150,19 @@ int main(int argc, char** argv) {
|
|||
leveldb_options_t* options;
|
||||
leveldb_readoptions_t* roptions;
|
||||
leveldb_writeoptions_t* woptions;
|
||||
char* dbname;
|
||||
char* err = NULL;
|
||||
int run = -1;
|
||||
|
||||
CheckCondition(leveldb_major_version() >= 1);
|
||||
CheckCondition(leveldb_minor_version() >= 1);
|
||||
|
||||
snprintf(dbname, sizeof(dbname),
|
||||
"%s/leveldb_c_test-%d",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
StartPhase("create_objects");
|
||||
cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
|
||||
env = leveldb_create_default_env();
|
||||
cache = leveldb_cache_create_lru(100000);
|
||||
dbname = leveldb_env_get_test_directory(env);
|
||||
CheckCondition(dbname != NULL);
|
||||
|
||||
options = leveldb_options_create();
|
||||
leveldb_options_set_comparator(options, cmp);
|
||||
|
@ -189,6 +175,7 @@ int main(int argc, char** argv) {
|
|||
leveldb_options_set_max_open_files(options, 10);
|
||||
leveldb_options_set_block_size(options, 1024);
|
||||
leveldb_options_set_block_restart_interval(options, 8);
|
||||
leveldb_options_set_max_file_size(options, 3 << 20);
|
||||
leveldb_options_set_compression(options, leveldb_no_compression);
|
||||
|
||||
roptions = leveldb_readoptions_create();
|
||||
|
@ -239,12 +226,18 @@ int main(int argc, char** argv) {
|
|||
leveldb_writebatch_clear(wb);
|
||||
leveldb_writebatch_put(wb, "bar", 3, "b", 1);
|
||||
leveldb_writebatch_put(wb, "box", 3, "c", 1);
|
||||
leveldb_writebatch_delete(wb, "bar", 3);
|
||||
|
||||
leveldb_writebatch_t* wb2 = leveldb_writebatch_create();
|
||||
leveldb_writebatch_delete(wb2, "bar", 3);
|
||||
leveldb_writebatch_append(wb, wb2);
|
||||
leveldb_writebatch_destroy(wb2);
|
||||
|
||||
leveldb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "box", "c");
|
||||
|
||||
int pos = 0;
|
||||
leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
|
||||
CheckCondition(pos == 3);
|
||||
|
@ -381,6 +374,7 @@ int main(int argc, char** argv) {
|
|||
leveldb_options_destroy(options);
|
||||
leveldb_readoptions_destroy(roptions);
|
||||
leveldb_writeoptions_destroy(woptions);
|
||||
leveldb_free(dbname);
|
||||
leveldb_cache_destroy(cache);
|
||||
leveldb_comparator_destroy(cmp);
|
||||
leveldb_env_destroy(env);
|
||||
|
|
|
@ -2,20 +2,16 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/db.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/table.h"
|
||||
#include "leveldb/write_batch.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/table.h"
|
||||
#include "leveldb/write_batch.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
@ -26,44 +22,35 @@ static const int kValueSize = 1000;
|
|||
|
||||
class CorruptionTest {
|
||||
public:
|
||||
test::ErrorEnv env_;
|
||||
std::string dbname_;
|
||||
Cache* tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
CorruptionTest() {
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
CorruptionTest()
|
||||
: db_(nullptr),
|
||||
dbname_("/memenv/corruption_test"),
|
||||
tiny_cache_(NewLRUCache(100)) {
|
||||
options_.env = &env_;
|
||||
options_.block_cache = tiny_cache_;
|
||||
dbname_ = test::TmpDir() + "/corruption_test";
|
||||
DestroyDB(dbname_, options_);
|
||||
|
||||
db_ = NULL;
|
||||
options_.create_if_missing = true;
|
||||
Reopen();
|
||||
options_.create_if_missing = false;
|
||||
}
|
||||
|
||||
~CorruptionTest() {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
delete tiny_cache_;
|
||||
delete db_;
|
||||
delete tiny_cache_;
|
||||
}
|
||||
|
||||
Status TryReopen() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void Reopen() {
|
||||
ASSERT_OK(TryReopen());
|
||||
}
|
||||
void Reopen() { ASSERT_OK(TryReopen()); }
|
||||
|
||||
void RepairDB() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
|
||||
}
|
||||
|
||||
|
@ -71,7 +58,7 @@ class CorruptionTest {
|
|||
std::string key_space, value_space;
|
||||
WriteBatch batch;
|
||||
for (int i = 0; i < n; i++) {
|
||||
//if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
||||
// if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
||||
Slice key = Key(i, &key_space);
|
||||
batch.Clear();
|
||||
batch.Put(key, Value(i, &value_space));
|
||||
|
@ -100,8 +87,7 @@ class CorruptionTest {
|
|||
// Ignore boundary keys.
|
||||
continue;
|
||||
}
|
||||
if (!ConsumeDecimalNumber(&in, &key) ||
|
||||
!in.empty() ||
|
||||
if (!ConsumeDecimalNumber(&in, &key) || !in.empty() ||
|
||||
key < next_expected) {
|
||||
bad_keys++;
|
||||
continue;
|
||||
|
@ -126,14 +112,13 @@ class CorruptionTest {
|
|||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_.GetChildren(dbname_, &filenames));
|
||||
ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames));
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
std::string fname;
|
||||
int picked_number = -1;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type) &&
|
||||
type == filetype &&
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == filetype &&
|
||||
int(number) > picked_number) { // Pick latest file
|
||||
fname = dbname_ + "/" + filenames[i];
|
||||
picked_number = number;
|
||||
|
@ -141,35 +126,32 @@ class CorruptionTest {
|
|||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
|
||||
struct stat sbuf;
|
||||
if (stat(fname.c_str(), &sbuf) != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
ASSERT_TRUE(false) << fname << ": " << msg;
|
||||
}
|
||||
uint64_t file_size;
|
||||
ASSERT_OK(env_.target()->GetFileSize(fname, &file_size));
|
||||
|
||||
if (offset < 0) {
|
||||
// Relative to end of file; make it absolute
|
||||
if (-offset > sbuf.st_size) {
|
||||
if (-offset > file_size) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = sbuf.st_size + offset;
|
||||
offset = file_size + offset;
|
||||
}
|
||||
}
|
||||
if (offset > sbuf.st_size) {
|
||||
offset = sbuf.st_size;
|
||||
if (offset > file_size) {
|
||||
offset = file_size;
|
||||
}
|
||||
if (offset + bytes_to_corrupt > sbuf.st_size) {
|
||||
bytes_to_corrupt = sbuf.st_size - offset;
|
||||
if (offset + bytes_to_corrupt > file_size) {
|
||||
bytes_to_corrupt = file_size - offset;
|
||||
}
|
||||
|
||||
// Do it
|
||||
std::string contents;
|
||||
Status s = ReadFileToString(Env::Default(), fname, &contents);
|
||||
Status s = ReadFileToString(env_.target(), fname, &contents);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
for (int i = 0; i < bytes_to_corrupt; i++) {
|
||||
contents[i + offset] ^= 0x80;
|
||||
}
|
||||
s = WriteStringToFile(Env::Default(), contents, fname);
|
||||
s = WriteStringToFile(env_.target(), contents, fname);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
}
|
||||
|
||||
|
@ -197,12 +179,20 @@ class CorruptionTest {
|
|||
Random r(k);
|
||||
return test::RandomString(&r, kValueSize, storage);
|
||||
}
|
||||
|
||||
test::ErrorEnv env_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
private:
|
||||
std::string dbname_;
|
||||
Cache* tiny_cache_;
|
||||
};
|
||||
|
||||
TEST(CorruptionTest, Recovery) {
|
||||
Build(100);
|
||||
Check(100, 100);
|
||||
Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
|
||||
Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
|
||||
Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
|
||||
Reopen();
|
||||
|
||||
|
@ -237,8 +227,8 @@ TEST(CorruptionTest, TableFile) {
|
|||
Build(100);
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_CompactMemTable();
|
||||
dbi->TEST_CompactRange(0, NULL, NULL);
|
||||
dbi->TEST_CompactRange(1, NULL, NULL);
|
||||
dbi->TEST_CompactRange(0, nullptr, nullptr);
|
||||
dbi->TEST_CompactRange(1, nullptr, nullptr);
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
Check(90, 99);
|
||||
|
@ -251,8 +241,8 @@ TEST(CorruptionTest, TableFileRepair) {
|
|||
Build(100);
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_CompactMemTable();
|
||||
dbi->TEST_CompactRange(0, NULL, NULL);
|
||||
dbi->TEST_CompactRange(1, NULL, NULL);
|
||||
dbi->TEST_CompactRange(0, nullptr, nullptr);
|
||||
dbi->TEST_CompactRange(1, nullptr, nullptr);
|
||||
|
||||
Corrupt(kTableFile, 100, 1);
|
||||
RepairDB();
|
||||
|
@ -302,7 +292,7 @@ TEST(CorruptionTest, CorruptedDescriptor) {
|
|||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_CompactMemTable();
|
||||
dbi->TEST_CompactRange(0, NULL, NULL);
|
||||
dbi->TEST_CompactRange(0, nullptr, nullptr);
|
||||
|
||||
Corrupt(kDescriptorFile, 0, 1000);
|
||||
Status s = TryReopen();
|
||||
|
@ -343,7 +333,7 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) {
|
|||
Corrupt(kTableFile, 100, 1);
|
||||
env_.SleepForMicroseconds(100000);
|
||||
}
|
||||
dbi->CompactRange(NULL, NULL);
|
||||
dbi->CompactRange(nullptr, nullptr);
|
||||
|
||||
// Write must fail because of corrupted table
|
||||
std::string tmp1, tmp2;
|
||||
|
@ -369,6 +359,4 @@ TEST(CorruptionTest, UnrelatedKeys) {
|
|||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,8 +5,11 @@
|
|||
#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_
|
||||
#define STORAGE_LEVELDB_DB_DB_IMPL_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/snapshot.h"
|
||||
|
@ -26,21 +29,25 @@ class VersionSet;
|
|||
class DBImpl : public DB {
|
||||
public:
|
||||
DBImpl(const Options& options, const std::string& dbname);
|
||||
virtual ~DBImpl();
|
||||
|
||||
DBImpl(const DBImpl&) = delete;
|
||||
DBImpl& operator=(const DBImpl&) = delete;
|
||||
|
||||
~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
|
||||
|
||||
|
@ -69,6 +76,31 @@ class DBImpl : public DB {
|
|||
struct CompactionState;
|
||||
struct Writer;
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompaction {
|
||||
int level;
|
||||
bool done;
|
||||
const InternalKey* begin; // null means beginning of key range
|
||||
const InternalKey* end; // null means end of key range
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
};
|
||||
|
||||
// Per level compaction stats. stats_[level] stores the stats for
|
||||
// compactions that produced data for the specified "level".
|
||||
struct CompactionStats {
|
||||
CompactionStats() : micros(0), bytes_read(0), bytes_written(0) {}
|
||||
|
||||
void Add(const CompactionStats& c) {
|
||||
this->micros += c.micros;
|
||||
this->bytes_read += c.bytes_read;
|
||||
this->bytes_written += c.bytes_written;
|
||||
}
|
||||
|
||||
int64_t micros;
|
||||
int64_t bytes_read;
|
||||
int64_t bytes_written;
|
||||
};
|
||||
|
||||
Iterator* NewInternalIterator(const ReadOptions&,
|
||||
SequenceNumber* latest_snapshot,
|
||||
uint32_t* seed);
|
||||
|
@ -84,7 +116,7 @@ class DBImpl : public DB {
|
|||
void MaybeIgnoreError(Status* s) const;
|
||||
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void DeleteObsoleteFiles();
|
||||
void DeleteObsoleteFiles() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Compact the in-memory write buffer to disk. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
|
@ -100,14 +132,15 @@ class DBImpl : public DB {
|
|||
|
||||
Status MakeRoomForWrite(bool force /* compact even if there is room? */)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
WriteBatch* BuildBatchGroup(Writer** last_writer);
|
||||
WriteBatch* BuildBatchGroup(Writer** last_writer)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
void RecordBackgroundError(const Status& s);
|
||||
|
||||
void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
static void BGWork(void* db);
|
||||
void BackgroundCall();
|
||||
void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
void CleanupCompaction(CompactionState* compact)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
Status DoCompactionWork(CompactionState* compact)
|
||||
|
@ -118,93 +151,66 @@ class DBImpl : public DB {
|
|||
Status InstallCompactionResults(CompactionState* compact)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
const Comparator* user_comparator() const {
|
||||
return internal_comparator_.user_comparator();
|
||||
}
|
||||
|
||||
// Constant after construction
|
||||
Env* const env_;
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
const InternalFilterPolicy internal_filter_policy_;
|
||||
const Options options_; // options_.comparator == &internal_comparator_
|
||||
bool owns_info_log_;
|
||||
bool owns_cache_;
|
||||
const bool owns_info_log_;
|
||||
const bool owns_cache_;
|
||||
const std::string dbname_;
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
TableCache* table_cache_;
|
||||
TableCache* const table_cache_;
|
||||
|
||||
// Lock over the persistent DB state. Non-NULL iff successfully acquired.
|
||||
// Lock over the persistent DB state. Non-null iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
|
||||
// State below is protected by mutex_
|
||||
port::Mutex mutex_;
|
||||
port::AtomicPointer shutting_down_;
|
||||
port::CondVar bg_cv_; // Signalled when background work finishes
|
||||
std::atomic<bool> shutting_down_;
|
||||
port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_);
|
||||
MemTable* mem_;
|
||||
MemTable* imm_; // Memtable being compacted
|
||||
port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_
|
||||
MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted
|
||||
std::atomic<bool> has_imm_; // So bg thread can detect non-null imm_
|
||||
WritableFile* logfile_;
|
||||
uint64_t logfile_number_;
|
||||
uint64_t logfile_number_ GUARDED_BY(mutex_);
|
||||
log::Writer* log_;
|
||||
uint32_t seed_; // For sampling.
|
||||
uint32_t seed_ GUARDED_BY(mutex_); // For sampling.
|
||||
|
||||
// Queue of writers.
|
||||
std::deque<Writer*> writers_;
|
||||
WriteBatch* tmp_batch_;
|
||||
std::deque<Writer*> writers_ GUARDED_BY(mutex_);
|
||||
WriteBatch* tmp_batch_ GUARDED_BY(mutex_);
|
||||
|
||||
SnapshotList snapshots_;
|
||||
SnapshotList snapshots_ GUARDED_BY(mutex_);
|
||||
|
||||
// Set of table files to protect from deletion because they are
|
||||
// part of ongoing compactions.
|
||||
std::set<uint64_t> pending_outputs_;
|
||||
std::set<uint64_t> pending_outputs_ GUARDED_BY(mutex_);
|
||||
|
||||
// Has a background compaction been scheduled or is running?
|
||||
bool bg_compaction_scheduled_;
|
||||
bool background_compaction_scheduled_ GUARDED_BY(mutex_);
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompaction {
|
||||
int level;
|
||||
bool done;
|
||||
const InternalKey* begin; // NULL means beginning of key range
|
||||
const InternalKey* end; // NULL means end of key range
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
};
|
||||
ManualCompaction* manual_compaction_;
|
||||
ManualCompaction* manual_compaction_ GUARDED_BY(mutex_);
|
||||
|
||||
VersionSet* versions_;
|
||||
VersionSet* const versions_ GUARDED_BY(mutex_);
|
||||
|
||||
// Have we encountered a background error in paranoid mode?
|
||||
Status bg_error_;
|
||||
Status bg_error_ GUARDED_BY(mutex_);
|
||||
|
||||
// Per level compaction stats. stats_[level] stores the stats for
|
||||
// compactions that produced data for the specified "level".
|
||||
struct CompactionStats {
|
||||
int64_t micros;
|
||||
int64_t bytes_read;
|
||||
int64_t bytes_written;
|
||||
|
||||
CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { }
|
||||
|
||||
void Add(const CompactionStats& c) {
|
||||
this->micros += c.micros;
|
||||
this->bytes_read += c.bytes_read;
|
||||
this->bytes_written += c.bytes_written;
|
||||
}
|
||||
};
|
||||
CompactionStats stats_[config::kNumLevels];
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
|
||||
const Comparator* user_comparator() const {
|
||||
return internal_comparator_.user_comparator();
|
||||
}
|
||||
CompactionStats stats_[config::kNumLevels] GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
// Sanitize db options. The caller should delete result.info_log if
|
||||
// it is not equal to src.info_log.
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const Options& src);
|
||||
Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const Options& src);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
#include "db/db_iter.h"
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/iterator.h"
|
||||
#include "port/port.h"
|
||||
|
@ -36,17 +36,14 @@ namespace {
|
|||
// combines multiple entries for the same userkey found in the DB
|
||||
// representation into a single entry while accounting for sequence
|
||||
// numbers, deletion markers, overwrites, etc.
|
||||
class DBIter: public Iterator {
|
||||
class DBIter : public Iterator {
|
||||
public:
|
||||
// Which direction is the iterator currently moving?
|
||||
// (1) When moving forward, the internal iterator is positioned at
|
||||
// the exact entry that yields this->key(), this->value()
|
||||
// (2) When moving backwards, the internal iterator is positioned
|
||||
// just before all entries whose user key == this->key().
|
||||
enum Direction {
|
||||
kForward,
|
||||
kReverse
|
||||
};
|
||||
enum Direction { kForward, kReverse };
|
||||
|
||||
DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s,
|
||||
uint32_t seed)
|
||||
|
@ -57,21 +54,22 @@ class DBIter: public Iterator {
|
|||
direction_(kForward),
|
||||
valid_(false),
|
||||
rnd_(seed),
|
||||
bytes_counter_(RandomPeriod()) {
|
||||
}
|
||||
virtual ~DBIter() {
|
||||
delete iter_;
|
||||
}
|
||||
virtual bool Valid() const { return valid_; }
|
||||
virtual Slice key() const {
|
||||
bytes_until_read_sampling_(RandomCompactionPeriod()) {}
|
||||
|
||||
DBIter(const DBIter&) = delete;
|
||||
DBIter& operator=(const DBIter&) = delete;
|
||||
|
||||
~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 {
|
||||
|
@ -79,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);
|
||||
|
@ -103,38 +101,35 @@ class DBIter: public Iterator {
|
|||
}
|
||||
}
|
||||
|
||||
// Pick next gap with average value of config::kReadBytesPeriod.
|
||||
ssize_t RandomPeriod() {
|
||||
return rnd_.Uniform(2*config::kReadBytesPeriod);
|
||||
// Picks the number of bytes that can be read until a compaction is scheduled.
|
||||
size_t RandomCompactionPeriod() {
|
||||
return rnd_.Uniform(2 * config::kReadBytesPeriod);
|
||||
}
|
||||
|
||||
DBImpl* db_;
|
||||
const Comparator* const user_comparator_;
|
||||
Iterator* const iter_;
|
||||
SequenceNumber const sequence_;
|
||||
|
||||
Status status_;
|
||||
std::string saved_key_; // == current key when direction_==kReverse
|
||||
std::string saved_value_; // == current raw value when direction_==kReverse
|
||||
std::string saved_key_; // == current key when direction_==kReverse
|
||||
std::string saved_value_; // == current raw value when direction_==kReverse
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
|
||||
Random rnd_;
|
||||
ssize_t bytes_counter_;
|
||||
|
||||
// No copying allowed
|
||||
DBIter(const DBIter&);
|
||||
void operator=(const DBIter&);
|
||||
size_t bytes_until_read_sampling_;
|
||||
};
|
||||
|
||||
inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
Slice k = iter_->key();
|
||||
ssize_t n = k.size() + iter_->value().size();
|
||||
bytes_counter_ -= n;
|
||||
while (bytes_counter_ < 0) {
|
||||
bytes_counter_ += RandomPeriod();
|
||||
|
||||
size_t bytes_read = k.size() + iter_->value().size();
|
||||
while (bytes_until_read_sampling_ < bytes_read) {
|
||||
bytes_until_read_sampling_ += RandomCompactionPeriod();
|
||||
db_->RecordReadSample(k);
|
||||
}
|
||||
assert(bytes_until_read_sampling_ >= bytes_read);
|
||||
bytes_until_read_sampling_ -= bytes_read;
|
||||
|
||||
if (!ParseInternalKey(k, ikey)) {
|
||||
status_ = Status::Corruption("corrupted internal key in DBIter");
|
||||
return false;
|
||||
|
@ -165,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_);
|
||||
|
@ -218,8 +222,8 @@ void DBIter::Prev() {
|
|||
ClearSavedValue();
|
||||
return;
|
||||
}
|
||||
if (user_comparator_->Compare(ExtractUserKey(iter_->key()),
|
||||
saved_key_) < 0) {
|
||||
if (user_comparator_->Compare(ExtractUserKey(iter_->key()), saved_key_) <
|
||||
0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -275,8 +279,8 @@ void DBIter::Seek(const Slice& target) {
|
|||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
saved_key_.clear();
|
||||
AppendInternalKey(
|
||||
&saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek));
|
||||
AppendInternalKey(&saved_key_,
|
||||
ParsedInternalKey(target, sequence_, kValueTypeForSeek));
|
||||
iter_->Seek(saved_key_);
|
||||
if (iter_->Valid()) {
|
||||
FindNextUserEntry(false, &saved_key_ /* temporary storage */);
|
||||
|
@ -305,12 +309,9 @@ void DBIter::SeekToLast() {
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
Iterator* NewDBIterator(
|
||||
DBImpl* db,
|
||||
const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter,
|
||||
SequenceNumber sequence,
|
||||
uint32_t seed) {
|
||||
Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter, SequenceNumber sequence,
|
||||
uint32_t seed) {
|
||||
return new DBIter(db, user_key_comparator, internal_iter, sequence, seed);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
#define STORAGE_LEVELDB_DB_DB_ITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "leveldb/db.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "leveldb/db.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
|
@ -16,12 +17,9 @@ class DBImpl;
|
|||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified "sequence" number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(
|
||||
DBImpl* db,
|
||||
const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter,
|
||||
SequenceNumber sequence,
|
||||
uint32_t seed);
|
||||
Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter, SequenceNumber sequence,
|
||||
uint32_t seed);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,8 +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 <stdio.h>
|
||||
#include "db/dbformat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
|
@ -21,26 +25,20 @@ void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
|
|||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString() const {
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "' @ %llu : %d",
|
||||
(unsigned long long) sequence,
|
||||
int(type));
|
||||
std::string result = "'";
|
||||
result += EscapeString(user_key.ToString());
|
||||
result += buf;
|
||||
return result;
|
||||
std::ostringstream ss;
|
||||
ss << '\'' << EscapeString(user_key.ToString()) << "' @ " << sequence << " : "
|
||||
<< static_cast<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 {
|
||||
|
@ -65,9 +63,8 @@ int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {
|
|||
return r;
|
||||
}
|
||||
|
||||
void InternalKeyComparator::FindShortestSeparator(
|
||||
std::string* start,
|
||||
const Slice& limit) const {
|
||||
void InternalKeyComparator::FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {
|
||||
// Attempt to shorten the user portion of the key
|
||||
Slice user_start = ExtractUserKey(*start);
|
||||
Slice user_limit = ExtractUserKey(limit);
|
||||
|
@ -77,7 +74,8 @@ void InternalKeyComparator::FindShortestSeparator(
|
|||
user_comparator_->Compare(user_start, tmp) < 0) {
|
||||
// User key has become shorter physically, but larger logically.
|
||||
// Tack on the earliest possible number to the shortened user key.
|
||||
PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
|
||||
PutFixed64(&tmp,
|
||||
PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek));
|
||||
assert(this->Compare(*start, tmp) < 0);
|
||||
assert(this->Compare(tmp, limit) < 0);
|
||||
start->swap(tmp);
|
||||
|
@ -92,15 +90,14 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
|
|||
user_comparator_->Compare(user_key, tmp) < 0) {
|
||||
// User key has become shorter physically, but larger logically.
|
||||
// Tack on the earliest possible number to the shortened user key.
|
||||
PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
|
||||
PutFixed64(&tmp,
|
||||
PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek));
|
||||
assert(this->Compare(*key, tmp) < 0);
|
||||
key->swap(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
const char* InternalFilterPolicy::Name() const {
|
||||
return user_policy_->Name();
|
||||
}
|
||||
const char* InternalFilterPolicy::Name() const { return user_policy_->Name(); }
|
||||
|
||||
void InternalFilterPolicy::CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const {
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#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"
|
||||
#include "leveldb/filter_policy.h"
|
||||
|
@ -48,10 +51,7 @@ class InternalKey;
|
|||
// Value types encoded as the last component of internal keys.
|
||||
// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk
|
||||
// data structures.
|
||||
enum ValueType {
|
||||
kTypeDeletion = 0x0,
|
||||
kTypeValue = 0x1
|
||||
};
|
||||
enum ValueType { kTypeDeletion = 0x0, kTypeValue = 0x1 };
|
||||
// kValueTypeForSeek defines the ValueType that should be passed when
|
||||
// constructing a ParsedInternalKey object for seeking to a particular
|
||||
// sequence number (since we sort sequence numbers in decreasing order
|
||||
|
@ -64,17 +64,16 @@ typedef uint64_t SequenceNumber;
|
|||
|
||||
// We leave eight bits empty at the bottom so a type and sequence#
|
||||
// can be packed together into 64-bits.
|
||||
static const SequenceNumber kMaxSequenceNumber =
|
||||
((0x1ull << 56) - 1);
|
||||
static const SequenceNumber kMaxSequenceNumber = ((0x1ull << 56) - 1);
|
||||
|
||||
struct ParsedInternalKey {
|
||||
Slice user_key;
|
||||
SequenceNumber sequence;
|
||||
ValueType type;
|
||||
|
||||
ParsedInternalKey() { } // Intentionally left uninitialized (for speed)
|
||||
ParsedInternalKey() {} // Intentionally left uninitialized (for speed)
|
||||
ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
|
||||
: user_key(u), sequence(seq), type(t) { }
|
||||
: user_key(u), sequence(seq), type(t) {}
|
||||
std::string DebugString() const;
|
||||
};
|
||||
|
||||
|
@ -84,15 +83,13 @@ inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) {
|
|||
}
|
||||
|
||||
// Append the serialization of "key" to *result.
|
||||
extern void AppendInternalKey(std::string* result,
|
||||
const ParsedInternalKey& key);
|
||||
void AppendInternalKey(std::string* result, const ParsedInternalKey& key);
|
||||
|
||||
// Attempt to parse an internal key from "internal_key". On success,
|
||||
// stores the parsed data in "*result", and returns true.
|
||||
//
|
||||
// On error, returns false, leaves "*result" in an undefined state.
|
||||
extern bool ParseInternalKey(const Slice& internal_key,
|
||||
ParsedInternalKey* result);
|
||||
bool ParseInternalKey(const Slice& internal_key, ParsedInternalKey* result);
|
||||
|
||||
// Returns the user key portion of an internal key.
|
||||
inline Slice ExtractUserKey(const Slice& internal_key) {
|
||||
|
@ -100,27 +97,19 @@ inline Slice ExtractUserKey(const Slice& internal_key) {
|
|||
return Slice(internal_key.data(), internal_key.size() - 8);
|
||||
}
|
||||
|
||||
inline ValueType ExtractValueType(const Slice& internal_key) {
|
||||
assert(internal_key.size() >= 8);
|
||||
const size_t n = internal_key.size();
|
||||
uint64_t num = DecodeFixed64(internal_key.data() + n - 8);
|
||||
unsigned char c = num & 0xff;
|
||||
return static_cast<ValueType>(c);
|
||||
}
|
||||
|
||||
// A comparator for internal keys that uses a specified comparator for
|
||||
// the user key portion and breaks ties by decreasing sequence number.
|
||||
class InternalKeyComparator : public Comparator {
|
||||
private:
|
||||
const Comparator* user_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;
|
||||
explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) {}
|
||||
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_; }
|
||||
|
||||
|
@ -131,11 +120,12 @@ class InternalKeyComparator : public Comparator {
|
|||
class InternalFilterPolicy : public FilterPolicy {
|
||||
private:
|
||||
const FilterPolicy* const user_policy_;
|
||||
|
||||
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;
|
||||
explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) {}
|
||||
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
|
||||
|
@ -144,13 +134,18 @@ class InternalFilterPolicy : public FilterPolicy {
|
|||
class InternalKey {
|
||||
private:
|
||||
std::string rep_;
|
||||
|
||||
public:
|
||||
InternalKey() { } // Leave rep_ as empty to indicate it is invalid
|
||||
InternalKey() {} // Leave rep_ as empty to indicate it is invalid
|
||||
InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
|
||||
}
|
||||
|
||||
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_;
|
||||
|
@ -168,8 +163,8 @@ class InternalKey {
|
|||
std::string DebugString() const;
|
||||
};
|
||||
|
||||
inline int InternalKeyComparator::Compare(
|
||||
const InternalKey& a, const InternalKey& b) const {
|
||||
inline int InternalKeyComparator::Compare(const InternalKey& a,
|
||||
const InternalKey& b) const {
|
||||
return Compare(a.Encode(), b.Encode());
|
||||
}
|
||||
|
||||
|
@ -178,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()
|
||||
|
@ -192,6 +187,9 @@ class LookupKey {
|
|||
// the specified sequence number.
|
||||
LookupKey(const Slice& user_key, SequenceNumber sequence);
|
||||
|
||||
LookupKey(const LookupKey&) = delete;
|
||||
LookupKey& operator=(const LookupKey&) = delete;
|
||||
|
||||
~LookupKey();
|
||||
|
||||
// Return a key suitable for lookup in a MemTable.
|
||||
|
@ -214,11 +212,7 @@ class LookupKey {
|
|||
const char* start_;
|
||||
const char* kstart_;
|
||||
const char* end_;
|
||||
char space_[200]; // Avoid allocation for short keys
|
||||
|
||||
// No copying allowed
|
||||
LookupKey(const LookupKey&);
|
||||
void operator=(const LookupKey&);
|
||||
char space_[200]; // Avoid allocation for short keys
|
||||
};
|
||||
|
||||
inline LookupKey::~LookupKey() {
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
namespace leveldb {
|
||||
|
||||
static std::string IKey(const std::string& user_key,
|
||||
uint64_t seq,
|
||||
static std::string IKey(const std::string& user_key, uint64_t seq,
|
||||
ValueType vt) {
|
||||
std::string encoded;
|
||||
AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt));
|
||||
|
@ -28,9 +27,7 @@ static std::string ShortSuccessor(const std::string& s) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static void TestKey(const std::string& key,
|
||||
uint64_t seq,
|
||||
ValueType vt) {
|
||||
static void TestKey(const std::string& key, uint64_t seq, ValueType vt) {
|
||||
std::string encoded = IKey(key, seq, vt);
|
||||
|
||||
Slice in(encoded);
|
||||
|
@ -44,16 +41,22 @@ static void TestKey(const std::string& key,
|
|||
ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded));
|
||||
}
|
||||
|
||||
class FormatTest { };
|
||||
class FormatTest {};
|
||||
|
||||
TEST(FormatTest, InternalKey_EncodeDecode) {
|
||||
const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" };
|
||||
const uint64_t seq[] = {
|
||||
1, 2, 3,
|
||||
(1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1,
|
||||
(1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1,
|
||||
(1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1
|
||||
};
|
||||
const char* keys[] = {"", "k", "hello", "longggggggggggggggggggggg"};
|
||||
const uint64_t seq[] = {1,
|
||||
2,
|
||||
3,
|
||||
(1ull << 8) - 1,
|
||||
1ull << 8,
|
||||
(1ull << 8) + 1,
|
||||
(1ull << 16) - 1,
|
||||
1ull << 16,
|
||||
(1ull << 16) + 1,
|
||||
(1ull << 32) - 1,
|
||||
1ull << 32,
|
||||
(1ull << 32) + 1};
|
||||
for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) {
|
||||
for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) {
|
||||
TestKey(keys[k], seq[s], kTypeValue);
|
||||
|
@ -62,40 +65,44 @@ 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),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 99, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 101, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 100, kTypeValue)));
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foo", 100, kTypeDeletion)));
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 99, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 101, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeDeletion)));
|
||||
|
||||
// When user keys are misordered
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("bar", 99, kTypeValue)));
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("bar", 99, kTypeValue)));
|
||||
|
||||
// When user keys are different, but correctly ordered
|
||||
ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("hello", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("hello", 200, kTypeValue)));
|
||||
|
||||
// When start user key is prefix of limit user key
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
IKey("foobar", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue), IKey("foobar", 200, kTypeValue)));
|
||||
|
||||
// When limit user key is prefix of start user key
|
||||
ASSERT_EQ(IKey("foobar", 100, kTypeValue),
|
||||
Shorten(IKey("foobar", 100, kTypeValue),
|
||||
IKey("foo", 200, kTypeValue)));
|
||||
ASSERT_EQ(
|
||||
IKey("foobar", 100, kTypeValue),
|
||||
Shorten(IKey("foobar", 100, kTypeValue), IKey("foo", 200, kTypeValue)));
|
||||
}
|
||||
|
||||
TEST(FormatTest, InternalKeyShortestSuccessor) {
|
||||
|
@ -105,8 +112,20 @@ TEST(FormatTest, InternalKeyShortestSuccessor) {
|
|||
ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
|
||||
}
|
||||
|
||||
TEST(FormatTest, ParsedInternalKeyDebugString) {
|
||||
ParsedInternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue);
|
||||
|
||||
ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString());
|
||||
}
|
||||
|
||||
TEST(FormatTest, InternalKeyDebugString) {
|
||||
InternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue);
|
||||
ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString());
|
||||
|
||||
InternalKey invalid_key;
|
||||
ASSERT_EQ("(bad)", invalid_key.DebugString());
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/dumpfile.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
|
@ -35,8 +38,7 @@ bool GuessType(const std::string& fname, FileType* type) {
|
|||
// Notified when log reader encounters corruption.
|
||||
class CorruptionReporter : public log::Reader::Reporter {
|
||||
public:
|
||||
WritableFile* dst_;
|
||||
virtual void Corruption(size_t bytes, const Status& status) {
|
||||
void Corruption(size_t bytes, const Status& status) override {
|
||||
std::string r = "corruption: ";
|
||||
AppendNumberTo(&r, bytes);
|
||||
r += " bytes; ";
|
||||
|
@ -44,6 +46,8 @@ class CorruptionReporter : public log::Reader::Reporter {
|
|||
r.push_back('\n');
|
||||
dst_->Append(r);
|
||||
}
|
||||
|
||||
WritableFile* dst_;
|
||||
};
|
||||
|
||||
// Print contents of a log file. (*func)() is called on every record.
|
||||
|
@ -70,8 +74,7 @@ Status PrintLogContents(Env* env, const std::string& fname,
|
|||
// Called on every item found in a WriteBatch.
|
||||
class WriteBatchItemPrinter : public WriteBatch::Handler {
|
||||
public:
|
||||
WritableFile* dst_;
|
||||
virtual void Put(const Slice& key, const Slice& value) {
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
std::string r = " put '";
|
||||
AppendEscapedStringTo(&r, key);
|
||||
r += "' '";
|
||||
|
@ -79,14 +82,15 @@ 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";
|
||||
dst_->Append(r);
|
||||
}
|
||||
};
|
||||
|
||||
WritableFile* dst_;
|
||||
};
|
||||
|
||||
// Called on every log record (each one of which is a WriteBatch)
|
||||
// found in a kLogFile.
|
||||
|
@ -142,8 +146,8 @@ Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) {
|
|||
|
||||
Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) {
|
||||
uint64_t file_size;
|
||||
RandomAccessFile* file = NULL;
|
||||
Table* table = NULL;
|
||||
RandomAccessFile* file = nullptr;
|
||||
Table* table = nullptr;
|
||||
Status s = env->GetFileSize(fname, &file_size);
|
||||
if (s.ok()) {
|
||||
s = env->NewRandomAccessFile(fname, &file);
|
||||
|
@ -213,9 +217,12 @@ Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) {
|
|||
return Status::InvalidArgument(fname + ": unknown file type");
|
||||
}
|
||||
switch (ftype) {
|
||||
case kLogFile: return DumpLog(env, fname, dst);
|
||||
case kDescriptorFile: return DumpDescriptor(env, fname, dst);
|
||||
case kTableFile: return DumpTable(env, fname, dst);
|
||||
case kLogFile:
|
||||
return DumpLog(env, fname, dst);
|
||||
case kDescriptorFile:
|
||||
return DumpDescriptor(env, fname, dst);
|
||||
case kTableFile:
|
||||
return DumpTable(env, fname, dst);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,18 +6,20 @@
|
|||
// the last "sync". It then checks for data loss errors by purposely dropping
|
||||
// file data (or entire files) not protected by a "sync".
|
||||
|
||||
#include "leveldb/db.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/table.h"
|
||||
#include "leveldb/write_batch.h"
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/testharness.h"
|
||||
|
@ -34,7 +36,7 @@ class FaultInjectionTestEnv;
|
|||
namespace {
|
||||
|
||||
// Assume a filename, and not a directory name like "/foo/bar/"
|
||||
static std::string GetDirName(const std::string filename) {
|
||||
static std::string GetDirName(const std::string& filename) {
|
||||
size_t found = filename.find_last_of("/\\");
|
||||
if (found == std::string::npos) {
|
||||
return "";
|
||||
|
@ -54,8 +56,7 @@ Status Truncate(const std::string& filename, uint64_t length) {
|
|||
|
||||
SequentialFile* orig_file;
|
||||
Status s = env->NewSequentialFile(filename, &orig_file);
|
||||
if (!s.ok())
|
||||
return s;
|
||||
if (!s.ok()) return s;
|
||||
|
||||
char* scratch = new char[length];
|
||||
leveldb::Slice result;
|
||||
|
@ -83,15 +84,15 @@ Status Truncate(const std::string& filename, uint64_t length) {
|
|||
|
||||
struct FileState {
|
||||
std::string filename_;
|
||||
ssize_t pos_;
|
||||
ssize_t pos_at_last_sync_;
|
||||
ssize_t pos_at_last_flush_;
|
||||
int64_t pos_;
|
||||
int64_t pos_at_last_sync_;
|
||||
int64_t pos_at_last_flush_;
|
||||
|
||||
FileState(const std::string& filename)
|
||||
: filename_(filename),
|
||||
pos_(-1),
|
||||
pos_at_last_sync_(-1),
|
||||
pos_at_last_flush_(-1) { }
|
||||
pos_at_last_flush_(-1) {}
|
||||
|
||||
FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
|
||||
|
||||
|
@ -106,14 +107,14 @@ struct FileState {
|
|||
// is written to or sync'ed.
|
||||
class TestWritableFile : public WritableFile {
|
||||
public:
|
||||
TestWritableFile(const FileState& state,
|
||||
WritableFile* f,
|
||||
TestWritableFile(const FileState& state, WritableFile* f,
|
||||
FaultInjectionTestEnv* env);
|
||||
virtual ~TestWritableFile();
|
||||
virtual Status Append(const Slice& data);
|
||||
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;
|
||||
std::string GetName() const override { return ""; }
|
||||
|
||||
private:
|
||||
FileState state_;
|
||||
|
@ -126,14 +127,15 @@ class TestWritableFile : public WritableFile {
|
|||
|
||||
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()
|
||||
: EnvWrapper(Env::Default()), filesystem_active_(true) {}
|
||||
~FaultInjectionTestEnv() override = default;
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
WritableFile** result) override;
|
||||
Status NewAppendableFile(const std::string& fname,
|
||||
WritableFile** result) override;
|
||||
Status DeleteFile(const std::string& f) override;
|
||||
Status RenameFile(const std::string& s, const std::string& t) override;
|
||||
|
||||
void WritableFileClosed(const FileState& state);
|
||||
Status DropUnsyncedFileData();
|
||||
|
@ -146,24 +148,26 @@ class FaultInjectionTestEnv : public EnvWrapper {
|
|||
// system reset. Setting to inactive will freeze our saved filesystem state so
|
||||
// that it will stop being recorded. It can then be reset back to the state at
|
||||
// the time of the reset.
|
||||
bool IsFilesystemActive() const { return filesystem_active_; }
|
||||
void SetFilesystemActive(bool active) { filesystem_active_ = active; }
|
||||
bool IsFilesystemActive() LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock l(&mutex_);
|
||||
return filesystem_active_;
|
||||
}
|
||||
void SetFilesystemActive(bool active) LOCKS_EXCLUDED(mutex_) {
|
||||
MutexLock l(&mutex_);
|
||||
filesystem_active_ = active;
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
std::map<std::string, FileState> db_file_state_;
|
||||
std::set<std::string> new_files_since_last_dir_sync_;
|
||||
bool filesystem_active_; // Record flushes, syncs, writes
|
||||
std::map<std::string, FileState> db_file_state_ GUARDED_BY(mutex_);
|
||||
std::set<std::string> new_files_since_last_dir_sync_ GUARDED_BY(mutex_);
|
||||
bool filesystem_active_ GUARDED_BY(mutex_); // Record flushes, syncs, writes
|
||||
};
|
||||
|
||||
TestWritableFile::TestWritableFile(const FileState& state,
|
||||
WritableFile* f,
|
||||
TestWritableFile::TestWritableFile(const FileState& state, WritableFile* f,
|
||||
FaultInjectionTestEnv* env)
|
||||
: state_(state),
|
||||
target_(f),
|
||||
writable_file_opened_(true),
|
||||
env_(env) {
|
||||
assert(f != NULL);
|
||||
: state_(state), target_(f), writable_file_opened_(true), env_(env) {
|
||||
assert(f != nullptr);
|
||||
}
|
||||
|
||||
TestWritableFile::~TestWritableFile() {
|
||||
|
@ -265,10 +269,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();
|
||||
}
|
||||
|
@ -328,7 +333,6 @@ void FaultInjectionTestEnv::ResetState() {
|
|||
// Since we are not destroying the database, the existing files
|
||||
// should keep their recorded synced/flushed state. Therefore
|
||||
// we do not reset db_file_state_ and new_files_since_last_dir_sync_.
|
||||
MutexLock l(&mutex_);
|
||||
SetFilesystemActive(true);
|
||||
}
|
||||
|
||||
|
@ -338,12 +342,14 @@ Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
|
|||
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 delete_status = DeleteFile(new_file);
|
||||
if (!delete_status.ok() && status.ok()) {
|
||||
status = std::move(delete_status);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return status;
|
||||
}
|
||||
|
||||
void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
|
||||
|
@ -352,7 +358,7 @@ void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
|
|||
}
|
||||
|
||||
Status FileState::DropUnsyncedData() const {
|
||||
ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
|
||||
int64_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
|
||||
return Truncate(filename_, sync_pos);
|
||||
}
|
||||
|
||||
|
@ -370,7 +376,7 @@ class FaultInjectionTest {
|
|||
FaultInjectionTest()
|
||||
: env_(new FaultInjectionTestEnv),
|
||||
tiny_cache_(NewLRUCache(100)),
|
||||
db_(NULL) {
|
||||
db_(nullptr) {
|
||||
dbname_ = test::TmpDir() + "/fault_test";
|
||||
DestroyDB(dbname_, Options()); // Destroy any db from earlier run
|
||||
options_.reuse_logs = true;
|
||||
|
@ -387,9 +393,7 @@ class FaultInjectionTest {
|
|||
delete env_;
|
||||
}
|
||||
|
||||
void ReuseLogs(bool reuse) {
|
||||
options_.reuse_logs = reuse;
|
||||
}
|
||||
void ReuseLogs(bool reuse) { options_.reuse_logs = reuse; }
|
||||
|
||||
void Build(int start_idx, int num_vals) {
|
||||
std::string key_space, value_space;
|
||||
|
@ -449,19 +453,18 @@ class FaultInjectionTest {
|
|||
|
||||
Status OpenDB() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
env_->ResetState();
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void CloseDB() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
void DeleteAllData() {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
WriteOptions options;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
|
||||
}
|
||||
|
@ -485,23 +488,22 @@ class FaultInjectionTest {
|
|||
void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) {
|
||||
DeleteAllData();
|
||||
Build(0, num_pre_sync);
|
||||
db_->CompactRange(NULL, NULL);
|
||||
db_->CompactRange(nullptr, nullptr);
|
||||
Build(num_pre_sync, num_post_sync);
|
||||
}
|
||||
|
||||
void PartialCompactTestReopenWithFault(ResetMethod reset_method,
|
||||
int num_pre_sync,
|
||||
int num_post_sync) {
|
||||
int num_pre_sync, int num_post_sync) {
|
||||
env_->SetFilesystemActive(false);
|
||||
CloseDB();
|
||||
ResetDBState(reset_method);
|
||||
ASSERT_OK(OpenDB());
|
||||
ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR));
|
||||
ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR));
|
||||
ASSERT_OK(Verify(num_pre_sync, num_post_sync,
|
||||
FaultInjectionTest::VAL_EXPECT_ERROR));
|
||||
}
|
||||
|
||||
void NoWriteTestPreFault() {
|
||||
}
|
||||
void NoWriteTestPreFault() {}
|
||||
|
||||
void NoWriteTestReopenWithFault(ResetMethod reset_method) {
|
||||
CloseDB();
|
||||
|
@ -517,8 +519,7 @@ class FaultInjectionTest {
|
|||
int num_post_sync = rnd.Uniform(kMaxNumValues);
|
||||
|
||||
PartialCompactTestPreFault(num_pre_sync, num_post_sync);
|
||||
PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA,
|
||||
num_pre_sync,
|
||||
PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, num_pre_sync,
|
||||
num_post_sync);
|
||||
|
||||
NoWriteTestPreFault();
|
||||
|
@ -528,8 +529,7 @@ class FaultInjectionTest {
|
|||
// No new files created so we expect all values since no files will be
|
||||
// dropped.
|
||||
PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES,
|
||||
num_pre_sync + num_post_sync,
|
||||
0);
|
||||
num_pre_sync + num_post_sync, 0);
|
||||
|
||||
NoWriteTestPreFault();
|
||||
NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES);
|
||||
|
@ -549,6 +549,4 @@ TEST(FaultInjectionTest, FaultTestWithLogReuse) {
|
|||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/filename.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "db/filename.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "util/logging.h"
|
||||
|
@ -12,31 +14,30 @@
|
|||
namespace leveldb {
|
||||
|
||||
// A utility routine: write "data" to the named file and Sync() it.
|
||||
extern Status WriteStringToFileSync(Env* env, const Slice& data,
|
||||
const std::string& fname);
|
||||
Status WriteStringToFileSync(Env* env, const Slice& data,
|
||||
const std::string& fname);
|
||||
|
||||
static std::string MakeFileName(const std::string& name, uint64_t number,
|
||||
static std::string MakeFileName(const std::string& dbname, uint64_t number,
|
||||
const char* suffix) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "/%06llu.%s",
|
||||
static_cast<unsigned long long>(number),
|
||||
suffix);
|
||||
return name + buf;
|
||||
static_cast<unsigned long long>(number), suffix);
|
||||
return dbname + buf;
|
||||
}
|
||||
|
||||
std::string LogFileName(const std::string& name, uint64_t number) {
|
||||
std::string LogFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(name, number, "log");
|
||||
return MakeFileName(dbname, number, "log");
|
||||
}
|
||||
|
||||
std::string TableFileName(const std::string& name, uint64_t number) {
|
||||
std::string TableFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(name, number, "ldb");
|
||||
return MakeFileName(dbname, number, "ldb");
|
||||
}
|
||||
|
||||
std::string SSTTableFileName(const std::string& name, uint64_t number) {
|
||||
std::string SSTTableFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(name, number, "sst");
|
||||
return MakeFileName(dbname, number, "sst");
|
||||
}
|
||||
|
||||
std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
|
||||
|
@ -51,9 +52,7 @@ std::string CurrentFileName(const std::string& dbname) {
|
|||
return dbname + "/CURRENT";
|
||||
}
|
||||
|
||||
std::string LockFileName(const std::string& dbname) {
|
||||
return dbname + "/LOCK";
|
||||
}
|
||||
std::string LockFileName(const std::string& dbname) { return dbname + "/LOCK"; }
|
||||
|
||||
std::string TempFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
|
@ -69,7 +68,6 @@ std::string OldInfoLogFileName(const std::string& dbname) {
|
|||
return dbname + "/LOG.old";
|
||||
}
|
||||
|
||||
|
||||
// Owned filenames have the form:
|
||||
// dbname/CURRENT
|
||||
// dbname/LOCK
|
||||
|
@ -77,10 +75,9 @@ std::string OldInfoLogFileName(const std::string& dbname) {
|
|||
// dbname/LOG.old
|
||||
// dbname/MANIFEST-[0-9]+
|
||||
// dbname/[0-9]+.(log|sst|ldb)
|
||||
bool ParseFileName(const std::string& fname,
|
||||
uint64_t* number,
|
||||
bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
FileType* type) {
|
||||
Slice rest(fname);
|
||||
Slice rest(filename);
|
||||
if (rest == "CURRENT") {
|
||||
*number = 0;
|
||||
*type = kCurrentFile;
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#define STORAGE_LEVELDB_DB_FILENAME_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
#include "leveldb/status.h"
|
||||
#include "port/port.h"
|
||||
|
@ -30,55 +32,52 @@ enum FileType {
|
|||
// Return the name of the log file with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
extern std::string LogFileName(const std::string& dbname, uint64_t number);
|
||||
std::string LogFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the name of the sstable with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
extern std::string TableFileName(const std::string& dbname, uint64_t number);
|
||||
std::string TableFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the legacy file name for an sstable with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
extern std::string SSTTableFileName(const std::string& dbname, uint64_t number);
|
||||
std::string SSTTableFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the name of the descriptor file for the db named by
|
||||
// "dbname" and the specified incarnation number. The result will be
|
||||
// prefixed with "dbname".
|
||||
extern std::string DescriptorFileName(const std::string& dbname,
|
||||
uint64_t number);
|
||||
std::string DescriptorFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the name of the current file. This file contains the name
|
||||
// of the current manifest file. The result will be prefixed with
|
||||
// "dbname".
|
||||
extern std::string CurrentFileName(const std::string& dbname);
|
||||
std::string CurrentFileName(const std::string& dbname);
|
||||
|
||||
// Return the name of the lock file for the db named by
|
||||
// "dbname". The result will be prefixed with "dbname".
|
||||
extern std::string LockFileName(const std::string& dbname);
|
||||
std::string LockFileName(const std::string& dbname);
|
||||
|
||||
// Return the name of a temporary file owned by the db named "dbname".
|
||||
// The result will be prefixed with "dbname".
|
||||
extern std::string TempFileName(const std::string& dbname, uint64_t number);
|
||||
std::string TempFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the name of the info log file for "dbname".
|
||||
extern std::string InfoLogFileName(const std::string& dbname);
|
||||
std::string InfoLogFileName(const std::string& dbname);
|
||||
|
||||
// Return the name of the old info log file for "dbname".
|
||||
extern std::string OldInfoLogFileName(const std::string& dbname);
|
||||
std::string OldInfoLogFileName(const std::string& dbname);
|
||||
|
||||
// If filename is a leveldb file, store the type of the file in *type.
|
||||
// The number encoded in the filename is stored in *number. If the
|
||||
// filename was successfully parsed, returns true. Else return false.
|
||||
extern bool ParseFileName(const std::string& filename,
|
||||
uint64_t* number,
|
||||
FileType* type);
|
||||
bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
FileType* type);
|
||||
|
||||
// Make the CURRENT file point to the descriptor file with the
|
||||
// specified number.
|
||||
extern Status SetCurrentFile(Env* env, const std::string& dbname,
|
||||
uint64_t descriptor_number);
|
||||
|
||||
Status SetCurrentFile(Env* env, const std::string& dbname,
|
||||
uint64_t descriptor_number);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace leveldb {
|
||||
|
||||
class FileNameTest { };
|
||||
class FileNameTest {};
|
||||
|
||||
TEST(FileNameTest, Parse) {
|
||||
Slice db;
|
||||
|
@ -24,17 +24,17 @@ TEST(FileNameTest, Parse) {
|
|||
uint64_t number;
|
||||
FileType type;
|
||||
} cases[] = {
|
||||
{ "100.log", 100, kLogFile },
|
||||
{ "0.log", 0, kLogFile },
|
||||
{ "0.sst", 0, kTableFile },
|
||||
{ "0.ldb", 0, kTableFile },
|
||||
{ "CURRENT", 0, kCurrentFile },
|
||||
{ "LOCK", 0, kDBLockFile },
|
||||
{ "MANIFEST-2", 2, kDescriptorFile },
|
||||
{ "MANIFEST-7", 7, kDescriptorFile },
|
||||
{ "LOG", 0, kInfoLogFile },
|
||||
{ "LOG.old", 0, kInfoLogFile },
|
||||
{ "18446744073709551615.log", 18446744073709551615ull, kLogFile },
|
||||
{"100.log", 100, kLogFile},
|
||||
{"0.log", 0, kLogFile},
|
||||
{"0.sst", 0, kTableFile},
|
||||
{"0.ldb", 0, kTableFile},
|
||||
{"CURRENT", 0, kCurrentFile},
|
||||
{"LOCK", 0, kDBLockFile},
|
||||
{"MANIFEST-2", 2, kDescriptorFile},
|
||||
{"MANIFEST-7", 7, kDescriptorFile},
|
||||
{"LOG", 0, kInfoLogFile},
|
||||
{"LOG.old", 0, kInfoLogFile},
|
||||
{"18446744073709551615.log", 18446744073709551615ull, kLogFile},
|
||||
};
|
||||
for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
std::string f = cases[i].fname;
|
||||
|
@ -44,30 +44,28 @@ TEST(FileNameTest, Parse) {
|
|||
}
|
||||
|
||||
// Errors
|
||||
static const char* errors[] = {
|
||||
"",
|
||||
"foo",
|
||||
"foo-dx-100.log",
|
||||
".log",
|
||||
"",
|
||||
"manifest",
|
||||
"CURREN",
|
||||
"CURRENTX",
|
||||
"MANIFES",
|
||||
"MANIFEST",
|
||||
"MANIFEST-",
|
||||
"XMANIFEST-3",
|
||||
"MANIFEST-3x",
|
||||
"LOC",
|
||||
"LOCKx",
|
||||
"LO",
|
||||
"LOGx",
|
||||
"18446744073709551616.log",
|
||||
"184467440737095516150.log",
|
||||
"100",
|
||||
"100.",
|
||||
"100.lop"
|
||||
};
|
||||
static const char* errors[] = {"",
|
||||
"foo",
|
||||
"foo-dx-100.log",
|
||||
".log",
|
||||
"",
|
||||
"manifest",
|
||||
"CURREN",
|
||||
"CURRENTX",
|
||||
"MANIFES",
|
||||
"MANIFEST",
|
||||
"MANIFEST-",
|
||||
"XMANIFEST-3",
|
||||
"MANIFEST-3x",
|
||||
"LOC",
|
||||
"LOCKx",
|
||||
"LO",
|
||||
"LOGx",
|
||||
"18446744073709551616.log",
|
||||
"184467440737095516150.log",
|
||||
"100",
|
||||
"100.",
|
||||
"100.lop"};
|
||||
for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
|
||||
std::string f = errors[i];
|
||||
ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
|
||||
|
@ -114,10 +112,20 @@ TEST(FileNameTest, Construction) {
|
|||
ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
|
||||
ASSERT_EQ(999, number);
|
||||
ASSERT_EQ(kTempFile, type);
|
||||
|
||||
fname = InfoLogFileName("foo");
|
||||
ASSERT_EQ("foo/", std::string(fname.data(), 4));
|
||||
ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
|
||||
ASSERT_EQ(0, number);
|
||||
ASSERT_EQ(kInfoLogFile, type);
|
||||
|
||||
fname = OldInfoLogFileName("foo");
|
||||
ASSERT_EQ("foo/", std::string(fname.data(), 4));
|
||||
ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
|
||||
ASSERT_EQ(0, number);
|
||||
ASSERT_EQ(kInfoLogFile, type);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "leveldb/dumpfile.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/status.h"
|
||||
|
@ -12,14 +13,14 @@ 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(); }
|
||||
virtual std::string GetName() const { return "[stdout]"; }
|
||||
Status Close() override { return Status::OK(); }
|
||||
Status Flush() override { return Status::OK(); }
|
||||
Status Sync() override { return Status::OK(); }
|
||||
std::string GetName() const override { return "[stdout]"; }
|
||||
};
|
||||
|
||||
bool HandleDumpCommand(Env* env, char** files, int num) {
|
||||
|
@ -39,11 +40,9 @@ bool HandleDumpCommand(Env* env, char** files, int num) {
|
|||
} // namespace leveldb
|
||||
|
||||
static void Usage() {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage: leveldbutil command...\n"
|
||||
" dump files... -- dump contents of specified files\n"
|
||||
);
|
||||
fprintf(stderr,
|
||||
"Usage: leveldbutil command...\n"
|
||||
" dump files... -- dump contents of specified files\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -55,7 +54,7 @@ int main(int argc, char** argv) {
|
|||
} else {
|
||||
std::string command = argv[1];
|
||||
if (command == "dump") {
|
||||
ok = leveldb::HandleDumpCommand(env, argv+2, argc-2);
|
||||
ok = leveldb::HandleDumpCommand(env, argv + 2, argc - 2);
|
||||
} else {
|
||||
Usage();
|
||||
ok = false;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "db/log_reader.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
@ -12,8 +13,7 @@
|
|||
namespace leveldb {
|
||||
namespace log {
|
||||
|
||||
Reader::Reporter::~Reporter() {
|
||||
}
|
||||
Reader::Reporter::~Reporter() = default;
|
||||
|
||||
Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
|
||||
uint64_t initial_offset)
|
||||
|
@ -26,20 +26,16 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
|
|||
last_record_offset_(0),
|
||||
end_of_buffer_offset_(0),
|
||||
initial_offset_(initial_offset),
|
||||
resyncing_(initial_offset > 0) {
|
||||
}
|
||||
resyncing_(initial_offset > 0) {}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
}
|
||||
Reader::~Reader() { delete[] backing_store_; }
|
||||
|
||||
bool Reader::SkipToInitialBlock() {
|
||||
size_t offset_in_block = initial_offset_ % kBlockSize;
|
||||
const size_t offset_in_block = initial_offset_ % kBlockSize;
|
||||
uint64_t block_start_location = initial_offset_ - offset_in_block;
|
||||
|
||||
// Don't search a block if we'd be in the trailer
|
||||
if (offset_in_block > kBlockSize - 6) {
|
||||
offset_in_block = 0;
|
||||
block_start_location += kBlockSize;
|
||||
}
|
||||
|
||||
|
@ -99,9 +95,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
|
|||
// it could emit an empty kFirstType record at the tail end
|
||||
// of a block followed by a kFullType or kFirstType record
|
||||
// at the beginning of the next block.
|
||||
if (scratch->empty()) {
|
||||
in_fragmented_record = false;
|
||||
} else {
|
||||
if (!scratch->empty()) {
|
||||
ReportCorruption(scratch->size(), "partial record without end(1)");
|
||||
}
|
||||
}
|
||||
|
@ -117,9 +111,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
|
|||
// it could emit an empty kFirstType record at the tail end
|
||||
// of a block followed by a kFullType or kFirstType record
|
||||
// at the beginning of the next block.
|
||||
if (scratch->empty()) {
|
||||
in_fragmented_record = false;
|
||||
} else {
|
||||
if (!scratch->empty()) {
|
||||
ReportCorruption(scratch->size(), "partial record without end(2)");
|
||||
}
|
||||
}
|
||||
|
@ -181,16 +173,14 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint64_t Reader::LastRecordOffset() {
|
||||
return last_record_offset_;
|
||||
}
|
||||
uint64_t Reader::LastRecordOffset() { return last_record_offset_; }
|
||||
|
||||
void Reader::ReportCorruption(uint64_t bytes, const char* reason) {
|
||||
ReportDrop(bytes, Status::Corruption(reason, file_->GetName()));
|
||||
}
|
||||
|
||||
void Reader::ReportDrop(uint64_t bytes, const Status& reason) {
|
||||
if (reporter_ != NULL &&
|
||||
if (reporter_ != nullptr &&
|
||||
end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {
|
||||
reporter_->Corruption(static_cast<size_t>(bytes), reason);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class Reader {
|
|||
// Create a reader that will return log records from "*file".
|
||||
// "*file" must remain live while this Reader is in use.
|
||||
//
|
||||
// If "reporter" is non-NULL, it is notified whenever some data is
|
||||
// If "reporter" is non-null, it is notified whenever some data is
|
||||
// dropped due to a detected corruption. "*reporter" must remain
|
||||
// live while this Reader is in use.
|
||||
//
|
||||
|
@ -43,6 +43,9 @@ class Reader {
|
|||
Reader(SequentialFile* file, Reporter* reporter, bool checksum,
|
||||
uint64_t initial_offset);
|
||||
|
||||
Reader(const Reader&) = delete;
|
||||
Reader& operator=(const Reader&) = delete;
|
||||
|
||||
~Reader();
|
||||
|
||||
// Read the next record into *record. Returns true if read
|
||||
|
@ -58,26 +61,6 @@ class Reader {
|
|||
uint64_t LastRecordOffset();
|
||||
|
||||
private:
|
||||
SequentialFile* const file_;
|
||||
Reporter* const reporter_;
|
||||
bool const checksum_;
|
||||
char* const backing_store_;
|
||||
Slice buffer_;
|
||||
bool eof_; // Last Read() indicated EOF by returning < kBlockSize
|
||||
|
||||
// Offset of the last record returned by ReadRecord.
|
||||
uint64_t last_record_offset_;
|
||||
// Offset of the first location past the end of buffer_.
|
||||
uint64_t end_of_buffer_offset_;
|
||||
|
||||
// Offset at which to start looking for the first record to return
|
||||
uint64_t const initial_offset_;
|
||||
|
||||
// True if we are resynchronizing after a seek (initial_offset_ > 0). In
|
||||
// particular, a run of kMiddleType and kLastType records can be silently
|
||||
// skipped in this mode
|
||||
bool resyncing_;
|
||||
|
||||
// Extend record types with the following special values
|
||||
enum {
|
||||
kEof = kMaxRecordType + 1,
|
||||
|
@ -102,9 +85,25 @@ class Reader {
|
|||
void ReportCorruption(uint64_t bytes, const char* reason);
|
||||
void ReportDrop(uint64_t bytes, const Status& reason);
|
||||
|
||||
// No copying allowed
|
||||
Reader(const Reader&);
|
||||
void operator=(const Reader&);
|
||||
SequentialFile* const file_;
|
||||
Reporter* const reporter_;
|
||||
bool const checksum_;
|
||||
char* const backing_store_;
|
||||
Slice buffer_;
|
||||
bool eof_; // Last Read() indicated EOF by returning < kBlockSize
|
||||
|
||||
// Offset of the last record returned by ReadRecord.
|
||||
uint64_t last_record_offset_;
|
||||
// Offset of the first location past the end of buffer_.
|
||||
uint64_t end_of_buffer_offset_;
|
||||
|
||||
// Offset at which to start looking for the first record to return
|
||||
uint64_t const initial_offset_;
|
||||
|
||||
// True if we are resynchronizing after a seek (initial_offset_ > 0). In
|
||||
// particular, a run of kMiddleType and kLastType records can be silently
|
||||
// skipped in this mode
|
||||
bool resyncing_;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
|
|
@ -37,87 +37,12 @@ static std::string RandomSkewedString(int i, Random* rnd) {
|
|||
}
|
||||
|
||||
class LogTest {
|
||||
private:
|
||||
class StringDest : public WritableFile {
|
||||
public:
|
||||
std::string contents_;
|
||||
|
||||
virtual Status Close() { return Status::OK(); }
|
||||
virtual Status Flush() { return Status::OK(); }
|
||||
virtual Status Sync() { return Status::OK(); }
|
||||
virtual Status Append(const Slice& slice) {
|
||||
contents_.append(slice.data(), slice.size());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
class StringSource : public SequentialFile {
|
||||
public:
|
||||
Slice contents_;
|
||||
bool force_error_;
|
||||
bool returned_partial_;
|
||||
StringSource() : force_error_(false), returned_partial_(false) { }
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) {
|
||||
ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
|
||||
if (force_error_) {
|
||||
force_error_ = false;
|
||||
returned_partial_ = true;
|
||||
return Status::Corruption("read error");
|
||||
}
|
||||
|
||||
if (contents_.size() < n) {
|
||||
n = contents_.size();
|
||||
returned_partial_ = true;
|
||||
}
|
||||
*result = Slice(contents_.data(), n);
|
||||
contents_.remove_prefix(n);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Skip(uint64_t n) {
|
||||
if (n > contents_.size()) {
|
||||
contents_.clear();
|
||||
return Status::NotFound("in-memory file skipped past end");
|
||||
}
|
||||
|
||||
contents_.remove_prefix(n);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
class ReportCollector : public Reader::Reporter {
|
||||
public:
|
||||
size_t dropped_bytes_;
|
||||
std::string message_;
|
||||
|
||||
ReportCollector() : dropped_bytes_(0) { }
|
||||
virtual void Corruption(size_t bytes, const Status& status) {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
};
|
||||
|
||||
StringDest dest_;
|
||||
StringSource source_;
|
||||
ReportCollector report_;
|
||||
bool reading_;
|
||||
Writer* writer_;
|
||||
Reader* reader_;
|
||||
|
||||
// Record metadata for testing initial offset functionality
|
||||
static size_t initial_offset_record_sizes_[];
|
||||
static uint64_t initial_offset_last_record_offsets_[];
|
||||
static int num_initial_offset_records_;
|
||||
|
||||
public:
|
||||
LogTest() : reading_(false),
|
||||
writer_(new Writer(&dest_)),
|
||||
reader_(new Reader(&source_, &report_, true/*checksum*/,
|
||||
0/*initial_offset*/)) {
|
||||
}
|
||||
LogTest()
|
||||
: reading_(false),
|
||||
writer_(new Writer(&dest_)),
|
||||
reader_(new Reader(&source_, &report_, true /*checksum*/,
|
||||
0 /*initial_offset*/)) {}
|
||||
|
||||
~LogTest() {
|
||||
delete writer_;
|
||||
|
@ -134,9 +59,7 @@ class LogTest {
|
|||
writer_->AddRecord(Slice(msg));
|
||||
}
|
||||
|
||||
size_t WrittenBytes() const {
|
||||
return dest_.contents_.size();
|
||||
}
|
||||
size_t WrittenBytes() const { return dest_.contents_.size(); }
|
||||
|
||||
std::string Read() {
|
||||
if (!reading_) {
|
||||
|
@ -166,22 +89,16 @@ class LogTest {
|
|||
|
||||
void FixChecksum(int header_offset, int len) {
|
||||
// Compute crc of type/len/data
|
||||
uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len);
|
||||
uint32_t crc = crc32c::Value(&dest_.contents_[header_offset + 6], 1 + len);
|
||||
crc = crc32c::Mask(crc);
|
||||
EncodeFixed32(&dest_.contents_[header_offset], crc);
|
||||
}
|
||||
|
||||
void ForceError() {
|
||||
source_.force_error_ = true;
|
||||
}
|
||||
void ForceError() { source_.force_error_ = true; }
|
||||
|
||||
size_t DroppedBytes() const {
|
||||
return report_.dropped_bytes_;
|
||||
}
|
||||
size_t DroppedBytes() const { return report_.dropped_bytes_; }
|
||||
|
||||
std::string ReportMessage() const {
|
||||
return report_.message_;
|
||||
}
|
||||
std::string ReportMessage() const { return report_.message_; }
|
||||
|
||||
// Returns OK iff recorded error message contains "msg"
|
||||
std::string MatchError(const std::string& msg) const {
|
||||
|
@ -202,14 +119,14 @@ class LogTest {
|
|||
|
||||
void StartReadingAt(uint64_t initial_offset) {
|
||||
delete reader_;
|
||||
reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset);
|
||||
reader_ = new Reader(&source_, &report_, true /*checksum*/, initial_offset);
|
||||
}
|
||||
|
||||
void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) {
|
||||
WriteInitialOffsetLog();
|
||||
reading_ = true;
|
||||
source_.contents_ = Slice(dest_.contents_);
|
||||
Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
|
||||
Reader* offset_reader = new Reader(&source_, &report_, true /*checksum*/,
|
||||
WrittenBytes() + offset_past_end);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
|
@ -222,8 +139,8 @@ class LogTest {
|
|||
WriteInitialOffsetLog();
|
||||
reading_ = true;
|
||||
source_.contents_ = Slice(dest_.contents_);
|
||||
Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
|
||||
initial_offset);
|
||||
Reader* offset_reader =
|
||||
new Reader(&source_, &report_, true /*checksum*/, initial_offset);
|
||||
|
||||
// Read all records from expected_record_offset through the last one.
|
||||
ASSERT_LT(expected_record_offset, num_initial_offset_records_);
|
||||
|
@ -240,36 +157,110 @@ class LogTest {
|
|||
}
|
||||
delete offset_reader;
|
||||
}
|
||||
|
||||
private:
|
||||
class StringDest : public WritableFile {
|
||||
public:
|
||||
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();
|
||||
}
|
||||
std::string GetName() const override { return ""; }
|
||||
|
||||
std::string contents_;
|
||||
};
|
||||
|
||||
class StringSource : public SequentialFile {
|
||||
public:
|
||||
StringSource() : force_error_(false), returned_partial_(false) {}
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
|
||||
if (force_error_) {
|
||||
force_error_ = false;
|
||||
returned_partial_ = true;
|
||||
return Status::Corruption("read error");
|
||||
}
|
||||
|
||||
if (contents_.size() < n) {
|
||||
n = contents_.size();
|
||||
returned_partial_ = true;
|
||||
}
|
||||
*result = Slice(contents_.data(), n);
|
||||
contents_.remove_prefix(n);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
if (n > contents_.size()) {
|
||||
contents_.clear();
|
||||
return Status::NotFound("in-memory file skipped past end");
|
||||
}
|
||||
|
||||
contents_.remove_prefix(n);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
std::string GetName() const { return ""; }
|
||||
|
||||
Slice contents_;
|
||||
bool force_error_;
|
||||
bool returned_partial_;
|
||||
};
|
||||
|
||||
class ReportCollector : public Reader::Reporter {
|
||||
public:
|
||||
ReportCollector() : dropped_bytes_(0) {}
|
||||
void Corruption(size_t bytes, const Status& status) override {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
|
||||
size_t dropped_bytes_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
// Record metadata for testing initial offset functionality
|
||||
static size_t initial_offset_record_sizes_[];
|
||||
static uint64_t initial_offset_last_record_offsets_[];
|
||||
static int num_initial_offset_records_;
|
||||
|
||||
StringDest dest_;
|
||||
StringSource source_;
|
||||
ReportCollector report_;
|
||||
bool reading_;
|
||||
Writer* writer_;
|
||||
Reader* reader_;
|
||||
};
|
||||
|
||||
size_t LogTest::initial_offset_record_sizes_[] =
|
||||
{10000, // Two sizable records in first block
|
||||
10000,
|
||||
2 * log::kBlockSize - 1000, // Span three blocks
|
||||
1,
|
||||
13716, // Consume all but two bytes of block 3.
|
||||
log::kBlockSize - kHeaderSize, // Consume the entirety of block 4.
|
||||
};
|
||||
size_t LogTest::initial_offset_record_sizes_[] = {
|
||||
10000, // Two sizable records in first block
|
||||
10000,
|
||||
2 * log::kBlockSize - 1000, // Span three blocks
|
||||
1,
|
||||
13716, // Consume all but two bytes of block 3.
|
||||
log::kBlockSize - kHeaderSize, // Consume the entirety of block 4.
|
||||
};
|
||||
|
||||
uint64_t LogTest::initial_offset_last_record_offsets_[] =
|
||||
{0,
|
||||
kHeaderSize + 10000,
|
||||
2 * (kHeaderSize + 10000),
|
||||
2 * (kHeaderSize + 10000) +
|
||||
(2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
|
||||
2 * (kHeaderSize + 10000) +
|
||||
(2 * log::kBlockSize - 1000) + 3 * kHeaderSize
|
||||
+ kHeaderSize + 1,
|
||||
3 * log::kBlockSize,
|
||||
};
|
||||
uint64_t LogTest::initial_offset_last_record_offsets_[] = {
|
||||
0,
|
||||
kHeaderSize + 10000,
|
||||
2 * (kHeaderSize + 10000),
|
||||
2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
|
||||
2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize +
|
||||
kHeaderSize + 1,
|
||||
3 * log::kBlockSize,
|
||||
};
|
||||
|
||||
// LogTest::initial_offset_last_record_offsets_ must be defined before this.
|
||||
int LogTest::num_initial_offset_records_ =
|
||||
sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t);
|
||||
sizeof(LogTest::initial_offset_last_record_offsets_) / sizeof(uint64_t);
|
||||
|
||||
TEST(LogTest, Empty) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
TEST(LogTest, Empty) { ASSERT_EQ("EOF", Read()); }
|
||||
|
||||
TEST(LogTest, ReadWrite) {
|
||||
Write("foo");
|
||||
|
@ -306,7 +297,7 @@ TEST(LogTest, Fragmentation) {
|
|||
|
||||
TEST(LogTest, MarginalTrailer) {
|
||||
// Make a trailer that is exactly the same length as an empty record.
|
||||
const int n = kBlockSize - 2*kHeaderSize;
|
||||
const int n = kBlockSize - 2 * kHeaderSize;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());
|
||||
Write("");
|
||||
|
@ -319,7 +310,7 @@ TEST(LogTest, MarginalTrailer) {
|
|||
|
||||
TEST(LogTest, MarginalTrailer2) {
|
||||
// Make a trailer that is exactly the same length as an empty record.
|
||||
const int n = kBlockSize - 2*kHeaderSize;
|
||||
const int n = kBlockSize - 2 * kHeaderSize;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());
|
||||
Write("bar");
|
||||
|
@ -331,7 +322,7 @@ TEST(LogTest, MarginalTrailer2) {
|
|||
}
|
||||
|
||||
TEST(LogTest, ShortTrailer) {
|
||||
const int n = kBlockSize - 2*kHeaderSize + 4;
|
||||
const int n = kBlockSize - 2 * kHeaderSize + 4;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
|
||||
Write("");
|
||||
|
@ -343,7 +334,7 @@ TEST(LogTest, ShortTrailer) {
|
|||
}
|
||||
|
||||
TEST(LogTest, AlignedEof) {
|
||||
const int n = kBlockSize - 2*kHeaderSize + 4;
|
||||
const int n = kBlockSize - 2 * kHeaderSize + 4;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
|
||||
ASSERT_EQ(BigString("foo", n), Read());
|
||||
|
@ -394,7 +385,7 @@ TEST(LogTest, BadRecordType) {
|
|||
|
||||
TEST(LogTest, TruncatedTrailingRecordIsIgnored) {
|
||||
Write("foo");
|
||||
ShrinkSize(4); // Drop all payload as well as a header byte
|
||||
ShrinkSize(4); // Drop all payload as well as a header byte
|
||||
ASSERT_EQ("EOF", Read());
|
||||
// Truncated last record is ignored, not treated as an error.
|
||||
ASSERT_EQ(0, DroppedBytes());
|
||||
|
@ -492,7 +483,7 @@ TEST(LogTest, SkipIntoMultiRecord) {
|
|||
// If initial_offset points to a record after first(R1) but before first(R2)
|
||||
// incomplete fragment errors are not actual errors, and must be suppressed
|
||||
// until a new first or full record is encountered.
|
||||
Write(BigString("foo", 3*kBlockSize));
|
||||
Write(BigString("foo", 3 * kBlockSize));
|
||||
Write("correct");
|
||||
StartReadingAt(kBlockSize);
|
||||
|
||||
|
@ -514,44 +505,30 @@ TEST(LogTest, ErrorJoinsRecords) {
|
|||
Write("correct");
|
||||
|
||||
// Wipe the middle block
|
||||
for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) {
|
||||
for (int offset = kBlockSize; offset < 2 * kBlockSize; offset++) {
|
||||
SetByte(offset, 'x');
|
||||
}
|
||||
|
||||
ASSERT_EQ("correct", Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
const size_t dropped = DroppedBytes();
|
||||
ASSERT_LE(dropped, 2*kBlockSize + 100);
|
||||
ASSERT_GE(dropped, 2*kBlockSize);
|
||||
ASSERT_LE(dropped, 2 * kBlockSize + 100);
|
||||
ASSERT_GE(dropped, 2 * kBlockSize);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadStart) {
|
||||
CheckInitialOffsetRecord(0, 0);
|
||||
}
|
||||
TEST(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); }
|
||||
|
||||
TEST(LogTest, ReadSecondOneOff) {
|
||||
CheckInitialOffsetRecord(1, 1);
|
||||
}
|
||||
TEST(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); }
|
||||
|
||||
TEST(LogTest, ReadSecondTenThousand) {
|
||||
CheckInitialOffsetRecord(10000, 1);
|
||||
}
|
||||
TEST(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); }
|
||||
|
||||
TEST(LogTest, ReadSecondStart) {
|
||||
CheckInitialOffsetRecord(10007, 1);
|
||||
}
|
||||
TEST(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); }
|
||||
|
||||
TEST(LogTest, ReadThirdOneOff) {
|
||||
CheckInitialOffsetRecord(10008, 2);
|
||||
}
|
||||
TEST(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); }
|
||||
|
||||
TEST(LogTest, ReadThirdStart) {
|
||||
CheckInitialOffsetRecord(20014, 2);
|
||||
}
|
||||
TEST(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); }
|
||||
|
||||
TEST(LogTest, ReadFourthOneOff) {
|
||||
CheckInitialOffsetRecord(20015, 3);
|
||||
}
|
||||
TEST(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); }
|
||||
|
||||
TEST(LogTest, ReadFourthFirstBlockTrailer) {
|
||||
CheckInitialOffsetRecord(log::kBlockSize - 4, 3);
|
||||
|
@ -575,17 +552,11 @@ TEST(LogTest, ReadInitialOffsetIntoBlockPadding) {
|
|||
CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadEnd) {
|
||||
CheckOffsetPastEndReturnsNoRecords(0);
|
||||
}
|
||||
TEST(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); }
|
||||
|
||||
TEST(LogTest, ReadPastEnd) {
|
||||
CheckOffsetPastEndReturnsNoRecords(5);
|
||||
}
|
||||
TEST(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); }
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "db/log_writer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
@ -19,9 +20,7 @@ static void InitTypeCrc(uint32_t* type_crc) {
|
|||
}
|
||||
}
|
||||
|
||||
Writer::Writer(WritableFile* dest)
|
||||
: dest_(dest),
|
||||
block_offset_(0) {
|
||||
Writer::Writer(WritableFile* dest) : dest_(dest), block_offset_(0) {
|
||||
InitTypeCrc(type_crc_);
|
||||
}
|
||||
|
||||
|
@ -30,8 +29,7 @@ Writer::Writer(WritableFile* dest, uint64_t dest_length)
|
|||
InitTypeCrc(type_crc_);
|
||||
}
|
||||
|
||||
Writer::~Writer() {
|
||||
}
|
||||
Writer::~Writer() = default;
|
||||
|
||||
Status Writer::AddRecord(const Slice& slice) {
|
||||
const char* ptr = slice.data();
|
||||
|
@ -49,7 +47,7 @@ Status Writer::AddRecord(const Slice& slice) {
|
|||
// Switch to a new block
|
||||
if (leftover > 0) {
|
||||
// Fill the trailer (literal below relies on kHeaderSize being 7)
|
||||
assert(kHeaderSize == 7);
|
||||
static_assert(kHeaderSize == 7, "");
|
||||
dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
|
||||
}
|
||||
block_offset_ = 0;
|
||||
|
@ -81,30 +79,31 @@ Status Writer::AddRecord(const Slice& slice) {
|
|||
return s;
|
||||
}
|
||||
|
||||
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
assert(n <= 0xffff); // Must fit in two bytes
|
||||
assert(block_offset_ + kHeaderSize + n <= kBlockSize);
|
||||
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr,
|
||||
size_t length) {
|
||||
assert(length <= 0xffff); // Must fit in two bytes
|
||||
assert(block_offset_ + kHeaderSize + length <= kBlockSize);
|
||||
|
||||
// Format the header
|
||||
char buf[kHeaderSize];
|
||||
buf[4] = static_cast<char>(n & 0xff);
|
||||
buf[5] = static_cast<char>(n >> 8);
|
||||
buf[4] = static_cast<char>(length & 0xff);
|
||||
buf[5] = static_cast<char>(length >> 8);
|
||||
buf[6] = static_cast<char>(t);
|
||||
|
||||
// Compute the crc of the record type and the payload.
|
||||
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n);
|
||||
crc = crc32c::Mask(crc); // Adjust for storage
|
||||
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length);
|
||||
crc = crc32c::Mask(crc); // Adjust for storage
|
||||
EncodeFixed32(buf, crc);
|
||||
|
||||
// Write the header and the payload
|
||||
Status s = dest_->Append(Slice(buf, kHeaderSize));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n));
|
||||
s = dest_->Append(Slice(ptr, length));
|
||||
if (s.ok()) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
block_offset_ += kHeaderSize + n;
|
||||
block_offset_ += kHeaderSize + length;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define STORAGE_LEVELDB_DB_LOG_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "db/log_format.h"
|
||||
#include "leveldb/slice.h"
|
||||
#include "leveldb/status.h"
|
||||
|
@ -28,24 +29,23 @@ class Writer {
|
|||
// "*dest" must remain live while this Writer is in use.
|
||||
Writer(WritableFile* dest, uint64_t dest_length);
|
||||
|
||||
Writer(const Writer&) = delete;
|
||||
Writer& operator=(const Writer&) = delete;
|
||||
|
||||
~Writer();
|
||||
|
||||
Status AddRecord(const Slice& slice);
|
||||
|
||||
private:
|
||||
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
WritableFile* dest_;
|
||||
int block_offset_; // Current offset in block
|
||||
int block_offset_; // Current offset in block
|
||||
|
||||
// crc32c values for all supported record types. These are
|
||||
// pre-computed to reduce the overhead of computing the crc of the
|
||||
// record type stored in the header.
|
||||
uint32_t type_crc_[kMaxRecordType + 1];
|
||||
|
||||
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
// No copying allowed
|
||||
Writer(const Writer&);
|
||||
void operator=(const Writer&);
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
|
|
@ -18,20 +18,15 @@ static Slice GetLengthPrefixedSlice(const char* data) {
|
|||
return Slice(p, len);
|
||||
}
|
||||
|
||||
MemTable::MemTable(const InternalKeyComparator& cmp)
|
||||
: comparator_(cmp),
|
||||
refs_(0),
|
||||
table_(comparator_, &arena_) {
|
||||
}
|
||||
MemTable::MemTable(const InternalKeyComparator& comparator)
|
||||
: comparator_(comparator), refs_(0), table_(comparator_, &arena_) {}
|
||||
|
||||
MemTable::~MemTable() {
|
||||
assert(refs_ == 0);
|
||||
}
|
||||
MemTable::~MemTable() { assert(refs_ == 0); }
|
||||
|
||||
size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); }
|
||||
|
||||
int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr)
|
||||
const {
|
||||
int MemTable::KeyComparator::operator()(const char* aptr,
|
||||
const char* bptr) const {
|
||||
// Internal keys are encoded as length-prefixed strings.
|
||||
Slice a = GetLengthPrefixedSlice(aptr);
|
||||
Slice b = GetLengthPrefixedSlice(bptr);
|
||||
|
@ -48,39 +43,37 @@ static const char* EncodeKey(std::string* scratch, const Slice& target) {
|
|||
return scratch->data();
|
||||
}
|
||||
|
||||
class MemTableIterator: public Iterator {
|
||||
class MemTableIterator : public Iterator {
|
||||
public:
|
||||
explicit MemTableIterator(MemTable::Table* table) : iter_(table) { }
|
||||
explicit MemTableIterator(MemTable::Table* table) : iter_(table) {}
|
||||
|
||||
virtual bool Valid() const { return iter_.Valid(); }
|
||||
virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); }
|
||||
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&);
|
||||
std::string tmp_; // For passing to EncodeKey
|
||||
};
|
||||
|
||||
Iterator* MemTable::NewIterator() {
|
||||
return new MemTableIterator(&table_);
|
||||
}
|
||||
Iterator* MemTable::NewIterator() { return new MemTableIterator(&table_); }
|
||||
|
||||
void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
const Slice& key,
|
||||
void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key,
|
||||
const Slice& value) {
|
||||
// Format of an entry is concatenation of:
|
||||
// key_size : varint32 of internal_key.size()
|
||||
|
@ -90,9 +83,9 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
|||
size_t key_size = key.size();
|
||||
size_t val_size = value.size();
|
||||
size_t internal_key_size = key_size + 8;
|
||||
const size_t encoded_len =
|
||||
VarintLength(internal_key_size) + internal_key_size +
|
||||
VarintLength(val_size) + val_size;
|
||||
const size_t encoded_len = VarintLength(internal_key_size) +
|
||||
internal_key_size + VarintLength(val_size) +
|
||||
val_size;
|
||||
char* buf = arena_.Allocate(encoded_len);
|
||||
char* p = EncodeVarint32(buf, internal_key_size);
|
||||
memcpy(p, key.data(), key_size);
|
||||
|
@ -121,10 +114,9 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {
|
|||
// all entries with overly large sequence numbers.
|
||||
const char* entry = iter.key();
|
||||
uint32_t key_length;
|
||||
const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length);
|
||||
const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);
|
||||
if (comparator_.comparator.user_comparator()->Compare(
|
||||
Slice(key_ptr, key_length - 8),
|
||||
key.user_key()) == 0) {
|
||||
Slice(key_ptr, key_length - 8), key.user_key()) == 0) {
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
switch (static_cast<ValueType>(tag & 0xff)) {
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
#define STORAGE_LEVELDB_DB_MEMTABLE_H_
|
||||
|
||||
#include <string>
|
||||
#include "leveldb/db.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/skiplist.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "util/arena.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class InternalKeyComparator;
|
||||
class Mutex;
|
||||
class MemTableIterator;
|
||||
|
||||
class MemTable {
|
||||
|
@ -23,6 +23,9 @@ class MemTable {
|
|||
// is zero and the caller must call Ref() at least once.
|
||||
explicit MemTable(const InternalKeyComparator& comparator);
|
||||
|
||||
MemTable(const MemTable&) = delete;
|
||||
MemTable& operator=(const MemTable&) = delete;
|
||||
|
||||
// Increase reference count.
|
||||
void Ref() { ++refs_; }
|
||||
|
||||
|
@ -50,8 +53,7 @@ class MemTable {
|
|||
// Add an entry into memtable that maps key to value at the
|
||||
// specified sequence number and with the specified type.
|
||||
// Typically value will be empty if type==kTypeDeletion.
|
||||
void Add(SequenceNumber seq, ValueType type,
|
||||
const Slice& key,
|
||||
void Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
const Slice& value);
|
||||
|
||||
// If memtable contains a value for key, store it in *value and return true.
|
||||
|
@ -61,26 +63,23 @@ class MemTable {
|
|||
bool Get(const LookupKey& key, std::string* value, Status* s);
|
||||
|
||||
private:
|
||||
~MemTable(); // Private since only Unref() should be used to delete it
|
||||
|
||||
struct KeyComparator {
|
||||
const InternalKeyComparator comparator;
|
||||
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }
|
||||
int operator()(const char* a, const char* b) const;
|
||||
};
|
||||
friend class MemTableIterator;
|
||||
friend class MemTableBackwardIterator;
|
||||
|
||||
struct KeyComparator {
|
||||
const InternalKeyComparator comparator;
|
||||
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {}
|
||||
int operator()(const char* a, const char* b) const;
|
||||
};
|
||||
|
||||
typedef SkipList<const char*, KeyComparator> Table;
|
||||
|
||||
~MemTable(); // Private since only Unref() should be used to delete it
|
||||
|
||||
KeyComparator comparator_;
|
||||
int refs_;
|
||||
Arena arena_;
|
||||
Table table_;
|
||||
|
||||
// No copying allowed
|
||||
MemTable(const MemTable&);
|
||||
void operator=(const MemTable&);
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace leveldb {
|
|||
|
||||
class RecoveryTest {
|
||||
public:
|
||||
RecoveryTest() : env_(Env::Default()), db_(NULL) {
|
||||
RecoveryTest() : env_(Env::Default()), db_(nullptr) {
|
||||
dbname_ = test::TmpDir() + "/recovery_test";
|
||||
DestroyDB(dbname_, Options());
|
||||
Open();
|
||||
|
@ -44,22 +44,26 @@ class RecoveryTest {
|
|||
|
||||
void Close() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
void Open(Options* options = NULL) {
|
||||
Status OpenWithStatus(Options* options = nullptr) {
|
||||
Close();
|
||||
Options opts;
|
||||
if (options != NULL) {
|
||||
if (options != nullptr) {
|
||||
opts = *options;
|
||||
} else {
|
||||
opts.reuse_logs = true; // TODO(sanjay): test both ways
|
||||
opts.create_if_missing = true;
|
||||
}
|
||||
if (opts.env == NULL) {
|
||||
if (opts.env == nullptr) {
|
||||
opts.env = env_;
|
||||
}
|
||||
ASSERT_OK(DB::Open(opts, dbname_, &db_));
|
||||
return DB::Open(opts, dbname_, &db_);
|
||||
}
|
||||
|
||||
void Open(Options* options = nullptr) {
|
||||
ASSERT_OK(OpenWithStatus(options));
|
||||
ASSERT_EQ(1, NumLogs());
|
||||
}
|
||||
|
||||
|
@ -67,7 +71,7 @@ class RecoveryTest {
|
|||
return db_->Put(WriteOptions(), k, v);
|
||||
}
|
||||
|
||||
std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {
|
||||
std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) {
|
||||
std::string result;
|
||||
Status s = db_->Get(ReadOptions(), k, &result);
|
||||
if (s.IsNotFound()) {
|
||||
|
@ -82,17 +86,18 @@ class RecoveryTest {
|
|||
std::string current;
|
||||
ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t));
|
||||
size_t len = current.size();
|
||||
if (len > 0 && current[len-1] == '\n') {
|
||||
if (len > 0 && current[len - 1] == '\n') {
|
||||
current.resize(len - 1);
|
||||
}
|
||||
return dbname_ + "/" + current;
|
||||
}
|
||||
|
||||
std::string LogName(uint64_t number) {
|
||||
return LogFileName(dbname_, number);
|
||||
}
|
||||
std::string LogName(uint64_t number) { return LogFileName(dbname_, number); }
|
||||
|
||||
size_t DeleteLogFiles() {
|
||||
// Linux allows unlinking open files, but Windows does not.
|
||||
// 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]);
|
||||
|
@ -100,9 +105,9 @@ class RecoveryTest {
|
|||
return logs.size();
|
||||
}
|
||||
|
||||
uint64_t FirstLogFile() {
|
||||
return GetFiles(kLogFile)[0];
|
||||
}
|
||||
void DeleteManifestFile() { ASSERT_OK(env_->DeleteFile(ManifestFileName())); }
|
||||
|
||||
uint64_t FirstLogFile() { return GetFiles(kLogFile)[0]; }
|
||||
|
||||
std::vector<uint64_t> GetFiles(FileType t) {
|
||||
std::vector<std::string> filenames;
|
||||
|
@ -118,13 +123,9 @@ class RecoveryTest {
|
|||
return result;
|
||||
}
|
||||
|
||||
int NumLogs() {
|
||||
return GetFiles(kLogFile).size();
|
||||
}
|
||||
int NumLogs() { return GetFiles(kLogFile).size(); }
|
||||
|
||||
int NumTables() {
|
||||
return GetFiles(kTableFile).size();
|
||||
}
|
||||
int NumTables() { return GetFiles(kTableFile).size(); }
|
||||
|
||||
uint64_t FileSize(const std::string& fname) {
|
||||
uint64_t result;
|
||||
|
@ -132,9 +133,7 @@ class RecoveryTest {
|
|||
return result;
|
||||
}
|
||||
|
||||
void CompactMemTable() {
|
||||
dbfull()->TEST_CompactMemTable();
|
||||
}
|
||||
void CompactMemTable() { dbfull()->TEST_CompactMemTable(); }
|
||||
|
||||
// Directly construct a log file that sets key to val.
|
||||
void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) {
|
||||
|
@ -186,7 +185,7 @@ TEST(RecoveryTest, LargeManifestCompacted) {
|
|||
uint64_t len = FileSize(old_manifest);
|
||||
WritableFile* file;
|
||||
ASSERT_OK(env()->NewAppendableFile(old_manifest, &file));
|
||||
std::string zeroes(3*1048576 - static_cast<size_t>(len), 0);
|
||||
std::string zeroes(3 * 1048576 - static_cast<size_t>(len), 0);
|
||||
ASSERT_OK(file->Append(zeroes));
|
||||
ASSERT_OK(file->Flush());
|
||||
delete file;
|
||||
|
@ -259,7 +258,7 @@ TEST(RecoveryTest, MultipleMemTables) {
|
|||
// Force creation of multiple memtables by reducing the write buffer size.
|
||||
Options opt;
|
||||
opt.reuse_logs = true;
|
||||
opt.write_buffer_size = (kNum*100) / 2;
|
||||
opt.write_buffer_size = (kNum * 100) / 2;
|
||||
Open(&opt);
|
||||
ASSERT_LE(2, NumTables());
|
||||
ASSERT_EQ(1, NumLogs());
|
||||
|
@ -278,16 +277,16 @@ TEST(RecoveryTest, MultipleLogFiles) {
|
|||
|
||||
// Make a bunch of uncompacted log files.
|
||||
uint64_t old_log = FirstLogFile();
|
||||
MakeLogFile(old_log+1, 1000, "hello", "world");
|
||||
MakeLogFile(old_log+2, 1001, "hi", "there");
|
||||
MakeLogFile(old_log+3, 1002, "foo", "bar2");
|
||||
MakeLogFile(old_log + 1, 1000, "hello", "world");
|
||||
MakeLogFile(old_log + 2, 1001, "hi", "there");
|
||||
MakeLogFile(old_log + 3, 1002, "foo", "bar2");
|
||||
|
||||
// Recover and check that all log files were processed.
|
||||
Open();
|
||||
ASSERT_LE(1, NumTables());
|
||||
ASSERT_EQ(1, NumLogs());
|
||||
uint64_t new_log = FirstLogFile();
|
||||
ASSERT_LE(old_log+3, new_log);
|
||||
ASSERT_LE(old_log + 3, new_log);
|
||||
ASSERT_EQ("bar2", Get("foo"));
|
||||
ASSERT_EQ("world", Get("hello"));
|
||||
ASSERT_EQ("there", Get("hi"));
|
||||
|
@ -305,7 +304,7 @@ TEST(RecoveryTest, MultipleLogFiles) {
|
|||
|
||||
// Check that introducing an older log file does not cause it to be re-read.
|
||||
Close();
|
||||
MakeLogFile(old_log+1, 2000, "hello", "stale write");
|
||||
MakeLogFile(old_log + 1, 2000, "hello", "stale write");
|
||||
Open();
|
||||
ASSERT_LE(1, NumTables());
|
||||
ASSERT_EQ(1, NumLogs());
|
||||
|
@ -317,8 +316,15 @@ TEST(RecoveryTest, MultipleLogFiles) {
|
|||
ASSERT_EQ("there", Get("hi"));
|
||||
}
|
||||
|
||||
TEST(RecoveryTest, ManifestMissing) {
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
Close();
|
||||
DeleteManifestFile();
|
||||
|
||||
Status status = OpenWithStatus();
|
||||
ASSERT_TRUE(status.IsCorruption());
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -54,7 +54,7 @@ class Repairer {
|
|||
owns_cache_(options_.block_cache != options.block_cache),
|
||||
next_file_number_(1) {
|
||||
// TableCache can be small since we expect each table to be opened once.
|
||||
table_cache_ = new TableCache(dbname_, &options_, 10);
|
||||
table_cache_ = new TableCache(dbname_, options_, 10);
|
||||
}
|
||||
|
||||
~Repairer() {
|
||||
|
@ -84,9 +84,7 @@ class Repairer {
|
|||
"recovered %d files; %llu bytes. "
|
||||
"Some data may have been lost. "
|
||||
"****",
|
||||
dbname_.c_str(),
|
||||
static_cast<int>(tables_.size()),
|
||||
bytes);
|
||||
dbname_.c_str(), static_cast<int>(tables_.size()), bytes);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -97,22 +95,6 @@ class Repairer {
|
|||
SequenceNumber max_sequence;
|
||||
};
|
||||
|
||||
std::string const dbname_;
|
||||
Env* const env_;
|
||||
InternalKeyComparator const icmp_;
|
||||
InternalFilterPolicy const ipolicy_;
|
||||
Options const options_;
|
||||
bool owns_info_log_;
|
||||
bool owns_cache_;
|
||||
TableCache* table_cache_;
|
||||
VersionEdit edit_;
|
||||
|
||||
std::vector<std::string> manifests_;
|
||||
std::vector<uint64_t> table_numbers_;
|
||||
std::vector<uint64_t> logs_;
|
||||
std::vector<TableInfo> tables_;
|
||||
uint64_t next_file_number_;
|
||||
|
||||
Status FindFiles() {
|
||||
std::vector<std::string> filenames;
|
||||
Status status = env_->GetChildren(dbname_, &filenames);
|
||||
|
@ -152,8 +134,7 @@ class Repairer {
|
|||
Status status = ConvertLogToTable(logs_[i]);
|
||||
if (!status.ok()) {
|
||||
Log(options_.info_log, "Log #%llu: ignoring conversion error: %s",
|
||||
(unsigned long long) logs_[i],
|
||||
status.ToString().c_str());
|
||||
(unsigned long long)logs_[i], status.ToString().c_str());
|
||||
}
|
||||
ArchiveFile(logname);
|
||||
}
|
||||
|
@ -164,11 +145,10 @@ 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),
|
||||
(unsigned long long)lognum, static_cast<int>(bytes),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
@ -190,8 +170,8 @@ class Repairer {
|
|||
// corruptions cause entire commits to be skipped instead of
|
||||
// propagating bad information (like overly large sequence
|
||||
// numbers).
|
||||
log::Reader reader(lfile, &reporter, false/*do not checksum*/,
|
||||
0/*initial_offset*/);
|
||||
log::Reader reader(lfile, &reporter, false /*do not checksum*/,
|
||||
0 /*initial_offset*/);
|
||||
|
||||
// Read all the records and add to a memtable
|
||||
std::string scratch;
|
||||
|
@ -202,8 +182,8 @@ class Repairer {
|
|||
int counter = 0;
|
||||
while (reader.ReadRecord(&record, &scratch)) {
|
||||
if (record.size() < 12) {
|
||||
reporter.Corruption(
|
||||
record.size(), Status::Corruption("log record too small", logname));
|
||||
reporter.Corruption(record.size(),
|
||||
Status::Corruption("log record too small", logname));
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
|
@ -212,8 +192,7 @@ class Repairer {
|
|||
counter += WriteBatchInternal::Count(&batch);
|
||||
} else {
|
||||
Log(options_.info_log, "Log #%llu: ignoring %s",
|
||||
(unsigned long long) log,
|
||||
status.ToString().c_str());
|
||||
(unsigned long long)log, status.ToString().c_str());
|
||||
status = Status::OK(); // Keep going with rest of file
|
||||
}
|
||||
}
|
||||
|
@ -227,16 +206,14 @@ class Repairer {
|
|||
status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);
|
||||
delete iter;
|
||||
mem->Unref();
|
||||
mem = NULL;
|
||||
mem = nullptr;
|
||||
if (status.ok()) {
|
||||
if (meta.file_size > 0) {
|
||||
table_numbers_.push_back(meta.number);
|
||||
}
|
||||
}
|
||||
Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s",
|
||||
(unsigned long long) log,
|
||||
counter,
|
||||
(unsigned long long) meta.number,
|
||||
(unsigned long long)log, counter, (unsigned long long)meta.number,
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
|
@ -272,8 +249,7 @@ class Repairer {
|
|||
ArchiveFile(TableFileName(dbname_, number));
|
||||
ArchiveFile(SSTTableFileName(dbname_, number));
|
||||
Log(options_.info_log, "Table #%llu: dropped: %s",
|
||||
(unsigned long long) t.meta.number,
|
||||
status.ToString().c_str());
|
||||
(unsigned long long)t.meta.number, status.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -287,8 +263,7 @@ class Repairer {
|
|||
Slice key = iter->key();
|
||||
if (!ParseInternalKey(key, &parsed)) {
|
||||
Log(options_.info_log, "Table #%llu: unparsable key %s",
|
||||
(unsigned long long) t.meta.number,
|
||||
EscapeString(key).c_str());
|
||||
(unsigned long long)t.meta.number, EscapeString(key).c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -307,9 +282,7 @@ class Repairer {
|
|||
}
|
||||
delete iter;
|
||||
Log(options_.info_log, "Table #%llu: %d entries %s",
|
||||
(unsigned long long) t.meta.number,
|
||||
counter,
|
||||
status.ToString().c_str());
|
||||
(unsigned long long)t.meta.number, counter, status.ToString().c_str());
|
||||
|
||||
if (status.ok()) {
|
||||
tables_.push_back(t);
|
||||
|
@ -350,20 +323,20 @@ class Repairer {
|
|||
}
|
||||
}
|
||||
delete builder;
|
||||
builder = NULL;
|
||||
builder = nullptr;
|
||||
|
||||
if (s.ok()) {
|
||||
s = file->Close();
|
||||
}
|
||||
delete file;
|
||||
file = NULL;
|
||||
file = nullptr;
|
||||
|
||||
if (counter > 0 && s.ok()) {
|
||||
std::string orig = TableFileName(dbname_, t.meta.number);
|
||||
s = env_->RenameFile(copy, orig);
|
||||
if (s.ok()) {
|
||||
Log(options_.info_log, "Table #%llu: %d entries repaired",
|
||||
(unsigned long long) t.meta.number, counter);
|
||||
(unsigned long long)t.meta.number, counter);
|
||||
tables_.push_back(t);
|
||||
}
|
||||
}
|
||||
|
@ -395,11 +368,11 @@ class Repairer {
|
|||
for (size_t i = 0; i < tables_.size(); i++) {
|
||||
// TODO(opt): separate out into multiple levels
|
||||
const TableInfo& t = tables_[i];
|
||||
edit_.AddFile(0, t.meta.number, t.meta.file_size,
|
||||
t.meta.smallest, t.meta.largest);
|
||||
edit_.AddFile(0, t.meta.number, t.meta.file_size, t.meta.smallest,
|
||||
t.meta.largest);
|
||||
}
|
||||
|
||||
//fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
|
||||
// fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
|
||||
{
|
||||
log::Writer log(file);
|
||||
std::string record;
|
||||
|
@ -410,7 +383,7 @@ class Repairer {
|
|||
status = file->Close();
|
||||
}
|
||||
delete file;
|
||||
file = NULL;
|
||||
file = nullptr;
|
||||
|
||||
if (!status.ok()) {
|
||||
env_->DeleteFile(tmp);
|
||||
|
@ -438,18 +411,34 @@ class Repairer {
|
|||
// dir/lost/foo
|
||||
const char* slash = strrchr(fname.c_str(), '/');
|
||||
std::string new_dir;
|
||||
if (slash != NULL) {
|
||||
if (slash != nullptr) {
|
||||
new_dir.assign(fname.data(), slash - fname.data());
|
||||
}
|
||||
new_dir.append("/lost");
|
||||
env_->CreateDir(new_dir); // Ignore error
|
||||
std::string new_file = new_dir;
|
||||
new_file.append("/");
|
||||
new_file.append((slash == NULL) ? fname.c_str() : slash + 1);
|
||||
new_file.append((slash == nullptr) ? fname.c_str() : slash + 1);
|
||||
Status s = env_->RenameFile(fname, new_file);
|
||||
Log(options_.info_log, "Archiving %s: %s\n",
|
||||
fname.c_str(), s.ToString().c_str());
|
||||
Log(options_.info_log, "Archiving %s: %s\n", fname.c_str(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
const std::string dbname_;
|
||||
Env* const env_;
|
||||
InternalKeyComparator const icmp_;
|
||||
InternalFilterPolicy const ipolicy_;
|
||||
const Options options_;
|
||||
bool owns_info_log_;
|
||||
bool owns_cache_;
|
||||
TableCache* table_cache_;
|
||||
VersionEdit edit_;
|
||||
|
||||
std::vector<std::string> manifests_;
|
||||
std::vector<uint64_t> table_numbers_;
|
||||
std::vector<uint64_t> logs_;
|
||||
std::vector<TableInfo> tables_;
|
||||
uint64_t next_file_number_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
|
|
@ -27,9 +27,10 @@
|
|||
//
|
||||
// ... prev vs. next pointer ordering ...
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "port/port.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util/arena.h"
|
||||
#include "util/random.h"
|
||||
|
||||
|
@ -37,7 +38,7 @@ namespace leveldb {
|
|||
|
||||
class Arena;
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
template <typename Key, class Comparator>
|
||||
class SkipList {
|
||||
private:
|
||||
struct Node;
|
||||
|
@ -48,6 +49,9 @@ class SkipList {
|
|||
// must remain allocated for the lifetime of the skiplist object.
|
||||
explicit SkipList(Comparator cmp, Arena* arena);
|
||||
|
||||
SkipList(const SkipList&) = delete;
|
||||
SkipList& operator=(const SkipList&) = delete;
|
||||
|
||||
// Insert key into the list.
|
||||
// REQUIRES: nothing that compares equal to key is currently in the list.
|
||||
void Insert(const Key& key);
|
||||
|
@ -97,24 +101,10 @@ class SkipList {
|
|||
private:
|
||||
enum { kMaxHeight = 12 };
|
||||
|
||||
// Immutable after construction
|
||||
Comparator const compare_;
|
||||
Arena* const arena_; // Arena used for allocations of nodes
|
||||
|
||||
Node* const head_;
|
||||
|
||||
// Modified only by Insert(). Read racily by readers, but stale
|
||||
// values are ok.
|
||||
port::AtomicPointer max_height_; // Height of the entire list
|
||||
|
||||
inline int GetMaxHeight() const {
|
||||
return static_cast<int>(
|
||||
reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load()));
|
||||
return max_height_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Read/written only by Insert().
|
||||
Random rnd_;
|
||||
|
||||
Node* NewNode(const Key& key, int height);
|
||||
int RandomHeight();
|
||||
bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }
|
||||
|
@ -123,9 +113,9 @@ class SkipList {
|
|||
bool KeyIsAfterNode(const Key& key, Node* n) const;
|
||||
|
||||
// Return the earliest node that comes at or after key.
|
||||
// Return NULL if there is no such node.
|
||||
// Return nullptr if there is no such node.
|
||||
//
|
||||
// If prev is non-NULL, fills prev[level] with pointer to previous
|
||||
// If prev is non-null, fills prev[level] with pointer to previous
|
||||
// node at "level" for every level in [0..max_height_-1].
|
||||
Node* FindGreaterOrEqual(const Key& key, Node** prev) const;
|
||||
|
||||
|
@ -137,15 +127,24 @@ class SkipList {
|
|||
// Return head_ if list is empty.
|
||||
Node* FindLast() const;
|
||||
|
||||
// No copying allowed
|
||||
SkipList(const SkipList&);
|
||||
void operator=(const SkipList&);
|
||||
// Immutable after construction
|
||||
Comparator const compare_;
|
||||
Arena* const arena_; // Arena used for allocations of nodes
|
||||
|
||||
Node* const head_;
|
||||
|
||||
// Modified only by Insert(). Read racily by readers, but stale
|
||||
// values are ok.
|
||||
std::atomic<int> max_height_; // Height of the entire list
|
||||
|
||||
// Read/written only by Insert().
|
||||
Random rnd_;
|
||||
};
|
||||
|
||||
// Implementation details follow
|
||||
template<typename Key, class Comparator>
|
||||
struct SkipList<Key,Comparator>::Node {
|
||||
explicit Node(const Key& k) : key(k) { }
|
||||
template <typename Key, class Comparator>
|
||||
struct SkipList<Key, Comparator>::Node {
|
||||
explicit Node(const Key& k) : key(k) {}
|
||||
|
||||
Key const key;
|
||||
|
||||
|
@ -155,92 +154,92 @@ struct SkipList<Key,Comparator>::Node {
|
|||
assert(n >= 0);
|
||||
// Use an 'acquire load' so that we observe a fully initialized
|
||||
// version of the returned Node.
|
||||
return reinterpret_cast<Node*>(next_[n].Acquire_Load());
|
||||
return next_[n].load(std::memory_order_acquire);
|
||||
}
|
||||
void SetNext(int n, Node* x) {
|
||||
assert(n >= 0);
|
||||
// Use a 'release store' so that anybody who reads through this
|
||||
// pointer observes a fully initialized version of the inserted node.
|
||||
next_[n].Release_Store(x);
|
||||
next_[n].store(x, std::memory_order_release);
|
||||
}
|
||||
|
||||
// No-barrier variants that can be safely used in a few locations.
|
||||
Node* NoBarrier_Next(int n) {
|
||||
assert(n >= 0);
|
||||
return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());
|
||||
return next_[n].load(std::memory_order_relaxed);
|
||||
}
|
||||
void NoBarrier_SetNext(int n, Node* x) {
|
||||
assert(n >= 0);
|
||||
next_[n].NoBarrier_Store(x);
|
||||
next_[n].store(x, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
// Array of length equal to the node height. next_[0] is lowest level link.
|
||||
port::AtomicPointer next_[1];
|
||||
std::atomic<Node*> next_[1];
|
||||
};
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
typename SkipList<Key,Comparator>::Node*
|
||||
SkipList<Key,Comparator>::NewNode(const Key& key, int height) {
|
||||
char* mem = arena_->AllocateAligned(
|
||||
sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));
|
||||
return new (mem) Node(key);
|
||||
template <typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node* SkipList<Key, Comparator>::NewNode(
|
||||
const Key& key, int height) {
|
||||
char* const node_memory = arena_->AllocateAligned(
|
||||
sizeof(Node) + sizeof(std::atomic<Node*>) * (height - 1));
|
||||
return new (node_memory) Node(key);
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) {
|
||||
template <typename Key, class Comparator>
|
||||
inline SkipList<Key, Comparator>::Iterator::Iterator(const SkipList* list) {
|
||||
list_ = list;
|
||||
node_ = NULL;
|
||||
node_ = nullptr;
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline bool SkipList<Key,Comparator>::Iterator::Valid() const {
|
||||
return node_ != NULL;
|
||||
template <typename Key, class Comparator>
|
||||
inline bool SkipList<Key, Comparator>::Iterator::Valid() const {
|
||||
return node_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline const Key& SkipList<Key,Comparator>::Iterator::key() const {
|
||||
template <typename Key, class Comparator>
|
||||
inline const Key& SkipList<Key, Comparator>::Iterator::key() const {
|
||||
assert(Valid());
|
||||
return node_->key;
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline void SkipList<Key,Comparator>::Iterator::Next() {
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::Next() {
|
||||
assert(Valid());
|
||||
node_ = node_->Next(0);
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline void SkipList<Key,Comparator>::Iterator::Prev() {
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::Prev() {
|
||||
// Instead of using explicit "prev" links, we just search for the
|
||||
// last node that falls before key.
|
||||
assert(Valid());
|
||||
node_ = list_->FindLessThan(node_->key);
|
||||
if (node_ == list_->head_) {
|
||||
node_ = NULL;
|
||||
node_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) {
|
||||
node_ = list_->FindGreaterOrEqual(target, NULL);
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::Seek(const Key& target) {
|
||||
node_ = list_->FindGreaterOrEqual(target, nullptr);
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() {
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::SeekToFirst() {
|
||||
node_ = list_->head_->Next(0);
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
inline void SkipList<Key,Comparator>::Iterator::SeekToLast() {
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::SeekToLast() {
|
||||
node_ = list_->FindLast();
|
||||
if (node_ == list_->head_) {
|
||||
node_ = NULL;
|
||||
node_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
int SkipList<Key,Comparator>::RandomHeight() {
|
||||
template <typename Key, class Comparator>
|
||||
int SkipList<Key, Comparator>::RandomHeight() {
|
||||
// Increase height with probability 1 in kBranching
|
||||
static const unsigned int kBranching = 4;
|
||||
int height = 1;
|
||||
|
@ -252,15 +251,16 @@ int SkipList<Key,Comparator>::RandomHeight() {
|
|||
return height;
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
|
||||
// NULL n is considered infinite
|
||||
return (n != NULL) && (compare_(n->key, key) < 0);
|
||||
template <typename Key, class Comparator>
|
||||
bool SkipList<Key, Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
|
||||
// null n is considered infinite
|
||||
return (n != nullptr) && (compare_(n->key, key) < 0);
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)
|
||||
const {
|
||||
template <typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node*
|
||||
SkipList<Key, Comparator>::FindGreaterOrEqual(const Key& key,
|
||||
Node** prev) const {
|
||||
Node* x = head_;
|
||||
int level = GetMaxHeight() - 1;
|
||||
while (true) {
|
||||
|
@ -269,7 +269,7 @@ typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOr
|
|||
// Keep searching in this list
|
||||
x = next;
|
||||
} else {
|
||||
if (prev != NULL) prev[level] = x;
|
||||
if (prev != nullptr) prev[level] = x;
|
||||
if (level == 0) {
|
||||
return next;
|
||||
} else {
|
||||
|
@ -280,15 +280,15 @@ typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOr
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
typename SkipList<Key,Comparator>::Node*
|
||||
SkipList<Key,Comparator>::FindLessThan(const Key& key) const {
|
||||
template <typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node*
|
||||
SkipList<Key, Comparator>::FindLessThan(const Key& key) const {
|
||||
Node* x = head_;
|
||||
int level = GetMaxHeight() - 1;
|
||||
while (true) {
|
||||
assert(x == head_ || compare_(x->key, key) < 0);
|
||||
Node* next = x->Next(level);
|
||||
if (next == NULL || compare_(next->key, key) >= 0) {
|
||||
if (next == nullptr || compare_(next->key, key) >= 0) {
|
||||
if (level == 0) {
|
||||
return x;
|
||||
} else {
|
||||
|
@ -301,14 +301,14 @@ SkipList<Key,Comparator>::FindLessThan(const Key& key) const {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()
|
||||
template <typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node* SkipList<Key, Comparator>::FindLast()
|
||||
const {
|
||||
Node* x = head_;
|
||||
int level = GetMaxHeight() - 1;
|
||||
while (true) {
|
||||
Node* next = x->Next(level);
|
||||
if (next == NULL) {
|
||||
if (next == nullptr) {
|
||||
if (level == 0) {
|
||||
return x;
|
||||
} else {
|
||||
|
@ -321,43 +321,41 @@ typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena)
|
||||
template <typename Key, class Comparator>
|
||||
SkipList<Key, Comparator>::SkipList(Comparator cmp, Arena* arena)
|
||||
: compare_(cmp),
|
||||
arena_(arena),
|
||||
head_(NewNode(0 /* any key will do */, kMaxHeight)),
|
||||
max_height_(reinterpret_cast<void*>(1)),
|
||||
max_height_(1),
|
||||
rnd_(0xdeadbeef) {
|
||||
for (int i = 0; i < kMaxHeight; i++) {
|
||||
head_->SetNext(i, NULL);
|
||||
head_->SetNext(i, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
void SkipList<Key,Comparator>::Insert(const Key& key) {
|
||||
template <typename Key, class Comparator>
|
||||
void SkipList<Key, Comparator>::Insert(const Key& key) {
|
||||
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
|
||||
// here since Insert() is externally synchronized.
|
||||
Node* prev[kMaxHeight];
|
||||
Node* x = FindGreaterOrEqual(key, prev);
|
||||
|
||||
// Our data structure does not allow duplicate insertion
|
||||
assert(x == NULL || !Equal(key, x->key));
|
||||
assert(x == nullptr || !Equal(key, x->key));
|
||||
|
||||
int height = RandomHeight();
|
||||
if (height > GetMaxHeight()) {
|
||||
for (int i = GetMaxHeight(); i < height; i++) {
|
||||
prev[i] = head_;
|
||||
}
|
||||
//fprintf(stderr, "Change height from %d to %d\n", max_height_, height);
|
||||
|
||||
// It is ok to mutate max_height_ without any synchronization
|
||||
// with concurrent readers. A concurrent reader that observes
|
||||
// the new value of max_height_ will see either the old value of
|
||||
// new level pointers from head_ (NULL), or a new value set in
|
||||
// new level pointers from head_ (nullptr), or a new value set in
|
||||
// the loop below. In the former case the reader will
|
||||
// immediately drop to the next level since NULL sorts after all
|
||||
// immediately drop to the next level since nullptr sorts after all
|
||||
// keys. In the latter case the reader will use the new node.
|
||||
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
|
||||
max_height_.store(height, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
x = NewNode(key, height);
|
||||
|
@ -369,10 +367,10 @@ void SkipList<Key,Comparator>::Insert(const Key& key) {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Key, class Comparator>
|
||||
bool SkipList<Key,Comparator>::Contains(const Key& key) const {
|
||||
Node* x = FindGreaterOrEqual(key, NULL);
|
||||
if (x != NULL && Equal(key, x->key)) {
|
||||
template <typename Key, class Comparator>
|
||||
bool SkipList<Key, Comparator>::Contains(const Key& key) const {
|
||||
Node* x = FindGreaterOrEqual(key, nullptr);
|
||||
if (x != nullptr && Equal(key, x->key)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/skiplist.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
|
||||
#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"
|
||||
|
@ -26,7 +31,7 @@ struct Comparator {
|
|||
}
|
||||
};
|
||||
|
||||
class SkipTest { };
|
||||
class SkipTest {};
|
||||
|
||||
TEST(SkipTest, Empty) {
|
||||
Arena arena;
|
||||
|
@ -112,8 +117,7 @@ TEST(SkipTest, InsertAndLookup) {
|
|||
|
||||
// Compare against model iterator
|
||||
for (std::set<Key>::reverse_iterator model_iter = keys.rbegin();
|
||||
model_iter != keys.rend();
|
||||
++model_iter) {
|
||||
model_iter != keys.rend(); ++model_iter) {
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*model_iter, iter.key());
|
||||
iter.Prev();
|
||||
|
@ -126,7 +130,7 @@ TEST(SkipTest, InsertAndLookup) {
|
|||
// concurrent readers (with no synchronization other than when a
|
||||
// reader's iterator is created), the reader always observes all the
|
||||
// data that was present in the skip list when the iterator was
|
||||
// constructor. Because insertions are happening concurrently, we may
|
||||
// constructed. Because insertions are happening concurrently, we may
|
||||
// also observe new values that were inserted since the iterator was
|
||||
// constructed, but we should never miss any values that were present
|
||||
// at iterator construction time.
|
||||
|
@ -155,12 +159,12 @@ class ConcurrentTest {
|
|||
static uint64_t hash(Key key) { return key & 0xff; }
|
||||
|
||||
static uint64_t HashNumbers(uint64_t k, uint64_t g) {
|
||||
uint64_t data[2] = { k, g };
|
||||
uint64_t data[2] = {k, g};
|
||||
return Hash(reinterpret_cast<char*>(data), sizeof(data), 0);
|
||||
}
|
||||
|
||||
static Key MakeKey(uint64_t k, uint64_t g) {
|
||||
assert(sizeof(Key) == sizeof(uint64_t));
|
||||
static_assert(sizeof(Key) == sizeof(uint64_t), "");
|
||||
assert(k <= K); // We sometimes pass K to seek to the end of the skiplist
|
||||
assert(g <= 0xffffffffu);
|
||||
return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff));
|
||||
|
@ -186,13 +190,11 @@ class ConcurrentTest {
|
|||
|
||||
// Per-key generation
|
||||
struct State {
|
||||
port::AtomicPointer generation[K];
|
||||
void Set(int k, intptr_t v) {
|
||||
generation[k].Release_Store(reinterpret_cast<void*>(v));
|
||||
}
|
||||
intptr_t Get(int k) {
|
||||
return reinterpret_cast<intptr_t>(generation[k].Acquire_Load());
|
||||
std::atomic<int> generation[K];
|
||||
void Set(int k, int v) {
|
||||
generation[k].store(v, std::memory_order_release);
|
||||
}
|
||||
int Get(int k) { return generation[k].load(std::memory_order_acquire); }
|
||||
|
||||
State() {
|
||||
for (int k = 0; k < K; k++) {
|
||||
|
@ -211,7 +213,7 @@ class ConcurrentTest {
|
|||
SkipList<Key, Comparator> list_;
|
||||
|
||||
public:
|
||||
ConcurrentTest() : list_(Comparator(), &arena_) { }
|
||||
ConcurrentTest() : list_(Comparator(), &arena_) {}
|
||||
|
||||
// REQUIRES: External synchronization
|
||||
void WriteStep(Random* rnd) {
|
||||
|
@ -250,11 +252,9 @@ class ConcurrentTest {
|
|||
// Note that generation 0 is never inserted, so it is ok if
|
||||
// <*,0,*> is missing.
|
||||
ASSERT_TRUE((gen(pos) == 0) ||
|
||||
(gen(pos) > static_cast<Key>(initial_state.Get(key(pos))))
|
||||
) << "key: " << key(pos)
|
||||
<< "; gen: " << gen(pos)
|
||||
<< "; initgen: "
|
||||
<< initial_state.Get(key(pos));
|
||||
(gen(pos) > static_cast<Key>(initial_state.Get(key(pos)))))
|
||||
<< "key: " << key(pos) << "; gen: " << gen(pos)
|
||||
<< "; initgen: " << initial_state.Get(key(pos));
|
||||
|
||||
// Advance to next key in the valid key space
|
||||
if (key(pos) < key(current)) {
|
||||
|
@ -298,21 +298,14 @@ class TestState {
|
|||
public:
|
||||
ConcurrentTest t_;
|
||||
int seed_;
|
||||
port::AtomicPointer quit_flag_;
|
||||
std::atomic<bool> quit_flag_;
|
||||
|
||||
enum ReaderState {
|
||||
STARTING,
|
||||
RUNNING,
|
||||
DONE
|
||||
};
|
||||
enum ReaderState { STARTING, RUNNING, DONE };
|
||||
|
||||
explicit TestState(int s)
|
||||
: seed_(s),
|
||||
quit_flag_(NULL),
|
||||
state_(STARTING),
|
||||
state_cv_(&mu_) {}
|
||||
: seed_(s), quit_flag_(false), state_(STARTING), state_cv_(&mu_) {}
|
||||
|
||||
void Wait(ReaderState s) {
|
||||
void Wait(ReaderState s) LOCKS_EXCLUDED(mu_) {
|
||||
mu_.Lock();
|
||||
while (state_ != s) {
|
||||
state_cv_.Wait();
|
||||
|
@ -320,7 +313,7 @@ class TestState {
|
|||
mu_.Unlock();
|
||||
}
|
||||
|
||||
void Change(ReaderState s) {
|
||||
void Change(ReaderState s) LOCKS_EXCLUDED(mu_) {
|
||||
mu_.Lock();
|
||||
state_ = s;
|
||||
state_cv_.Signal();
|
||||
|
@ -329,8 +322,8 @@ class TestState {
|
|||
|
||||
private:
|
||||
port::Mutex mu_;
|
||||
ReaderState state_;
|
||||
port::CondVar state_cv_;
|
||||
ReaderState state_ GUARDED_BY(mu_);
|
||||
port::CondVar state_cv_ GUARDED_BY(mu_);
|
||||
};
|
||||
|
||||
static void ConcurrentReader(void* arg) {
|
||||
|
@ -338,7 +331,7 @@ static void ConcurrentReader(void* arg) {
|
|||
Random rnd(state->seed_);
|
||||
int64_t reads = 0;
|
||||
state->Change(TestState::RUNNING);
|
||||
while (!state->quit_flag_.Acquire_Load()) {
|
||||
while (!state->quit_flag_.load(std::memory_order_acquire)) {
|
||||
state->t_.ReadStep(&rnd);
|
||||
++reads;
|
||||
}
|
||||
|
@ -360,7 +353,7 @@ static void RunConcurrent(int run) {
|
|||
for (int i = 0; i < kSize; i++) {
|
||||
state.t_.WriteStep(&rnd);
|
||||
}
|
||||
state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do
|
||||
state.quit_flag_.store(true, std::memory_order_release);
|
||||
state.Wait(TestState::DONE);
|
||||
}
|
||||
}
|
||||
|
@ -373,6 +366,4 @@ TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
|||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -16,50 +16,78 @@ class SnapshotList;
|
|||
// Each SnapshotImpl corresponds to a particular sequence number.
|
||||
class SnapshotImpl : public Snapshot {
|
||||
public:
|
||||
SequenceNumber number_; // const after creation
|
||||
SnapshotImpl(SequenceNumber sequence_number)
|
||||
: sequence_number_(sequence_number) {}
|
||||
|
||||
SequenceNumber sequence_number() const { return sequence_number_; }
|
||||
|
||||
private:
|
||||
friend class SnapshotList;
|
||||
|
||||
// SnapshotImpl is kept in a doubly-linked circular list
|
||||
// SnapshotImpl is kept in a doubly-linked circular list. The SnapshotList
|
||||
// implementation operates on the next/previous fields direcly.
|
||||
SnapshotImpl* prev_;
|
||||
SnapshotImpl* next_;
|
||||
|
||||
SnapshotList* list_; // just for sanity checks
|
||||
const SequenceNumber sequence_number_;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
SnapshotList* list_ = nullptr;
|
||||
#endif // !defined(NDEBUG)
|
||||
};
|
||||
|
||||
class SnapshotList {
|
||||
public:
|
||||
SnapshotList() {
|
||||
list_.prev_ = &list_;
|
||||
list_.next_ = &list_;
|
||||
SnapshotList() : head_(0) {
|
||||
head_.prev_ = &head_;
|
||||
head_.next_ = &head_;
|
||||
}
|
||||
|
||||
bool empty() const { return list_.next_ == &list_; }
|
||||
SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; }
|
||||
SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; }
|
||||
|
||||
const SnapshotImpl* New(SequenceNumber seq) {
|
||||
SnapshotImpl* s = new SnapshotImpl;
|
||||
s->number_ = seq;
|
||||
s->list_ = this;
|
||||
s->next_ = &list_;
|
||||
s->prev_ = list_.prev_;
|
||||
s->prev_->next_ = s;
|
||||
s->next_->prev_ = s;
|
||||
return s;
|
||||
bool empty() const { return head_.next_ == &head_; }
|
||||
SnapshotImpl* oldest() const {
|
||||
assert(!empty());
|
||||
return head_.next_;
|
||||
}
|
||||
SnapshotImpl* newest() const {
|
||||
assert(!empty());
|
||||
return head_.prev_;
|
||||
}
|
||||
|
||||
void Delete(const SnapshotImpl* s) {
|
||||
assert(s->list_ == this);
|
||||
s->prev_->next_ = s->next_;
|
||||
s->next_->prev_ = s->prev_;
|
||||
delete s;
|
||||
// Creates a SnapshotImpl and appends it to the end of the list.
|
||||
SnapshotImpl* New(SequenceNumber sequence_number) {
|
||||
assert(empty() || newest()->sequence_number_ <= sequence_number);
|
||||
|
||||
SnapshotImpl* snapshot = new SnapshotImpl(sequence_number);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
snapshot->list_ = this;
|
||||
#endif // !defined(NDEBUG)
|
||||
snapshot->next_ = &head_;
|
||||
snapshot->prev_ = head_.prev_;
|
||||
snapshot->prev_->next_ = snapshot;
|
||||
snapshot->next_->prev_ = snapshot;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
// Removes a SnapshotImpl from this list.
|
||||
//
|
||||
// The snapshot must have been created by calling New() on this list.
|
||||
//
|
||||
// The snapshot pointer should not be const, because its memory is
|
||||
// deallocated. However, that would force us to change DB::ReleaseSnapshot(),
|
||||
// which is in the API, and currently takes a const Snapshot.
|
||||
void Delete(const SnapshotImpl* snapshot) {
|
||||
#if !defined(NDEBUG)
|
||||
assert(snapshot->list_ == this);
|
||||
#endif // !defined(NDEBUG)
|
||||
snapshot->prev_->next_ = snapshot->next_;
|
||||
snapshot->next_->prev_ = snapshot->prev_;
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
// Dummy head of doubly-linked list of snapshots
|
||||
SnapshotImpl list_;
|
||||
SnapshotImpl head_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
|
|
@ -29,18 +29,14 @@ static void UnrefEntry(void* arg1, void* arg2) {
|
|||
cache->Release(h);
|
||||
}
|
||||
|
||||
TableCache::TableCache(const std::string& dbname,
|
||||
const Options* options,
|
||||
TableCache::TableCache(const std::string& dbname, const Options& options,
|
||||
int entries)
|
||||
: env_(options->env),
|
||||
: env_(options.env),
|
||||
dbname_(dbname),
|
||||
options_(options),
|
||||
cache_(NewLRUCache(entries)) {
|
||||
}
|
||||
cache_(NewLRUCache(entries)) {}
|
||||
|
||||
TableCache::~TableCache() {
|
||||
delete cache_;
|
||||
}
|
||||
TableCache::~TableCache() { delete cache_; }
|
||||
|
||||
Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
|
||||
Cache::Handle** handle) {
|
||||
|
@ -49,10 +45,10 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
|
|||
EncodeFixed64(buf, file_number);
|
||||
Slice key(buf, sizeof(buf));
|
||||
*handle = cache_->Lookup(key);
|
||||
if (*handle == NULL) {
|
||||
if (*handle == nullptr) {
|
||||
std::string fname = TableFileName(dbname_, file_number);
|
||||
RandomAccessFile* file = NULL;
|
||||
Table* table = NULL;
|
||||
RandomAccessFile* file = nullptr;
|
||||
Table* table = nullptr;
|
||||
s = env_->NewRandomAccessFile(fname, &file);
|
||||
if (!s.ok()) {
|
||||
std::string old_fname = SSTTableFileName(dbname_, file_number);
|
||||
|
@ -61,11 +57,11 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
|
|||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = Table::Open(*options_, file, file_size, &table);
|
||||
s = Table::Open(options_, file, file_size, &table);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
assert(table == NULL);
|
||||
assert(table == nullptr);
|
||||
delete file;
|
||||
// We do not cache error results so that if the error is transient,
|
||||
// or somebody repairs the file, we recover automatically.
|
||||
|
@ -80,14 +76,13 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
|
|||
}
|
||||
|
||||
Iterator* TableCache::NewIterator(const ReadOptions& options,
|
||||
uint64_t file_number,
|
||||
uint64_t file_size,
|
||||
uint64_t file_number, uint64_t file_size,
|
||||
Table** tableptr) {
|
||||
if (tableptr != NULL) {
|
||||
*tableptr = NULL;
|
||||
if (tableptr != nullptr) {
|
||||
*tableptr = nullptr;
|
||||
}
|
||||
|
||||
Cache::Handle* handle = NULL;
|
||||
Cache::Handle* handle = nullptr;
|
||||
Status s = FindTable(file_number, file_size, &handle);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
|
@ -96,23 +91,21 @@ Iterator* TableCache::NewIterator(const ReadOptions& options,
|
|||
Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
|
||||
Iterator* result = table->NewIterator(options);
|
||||
result->RegisterCleanup(&UnrefEntry, cache_, handle);
|
||||
if (tableptr != NULL) {
|
||||
if (tableptr != nullptr) {
|
||||
*tableptr = table;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Status TableCache::Get(const ReadOptions& options,
|
||||
uint64_t file_number,
|
||||
uint64_t file_size,
|
||||
const Slice& k,
|
||||
void* arg,
|
||||
void (*saver)(void*, const Slice&, const Slice&)) {
|
||||
Cache::Handle* handle = NULL;
|
||||
Status TableCache::Get(const ReadOptions& options, uint64_t file_number,
|
||||
uint64_t file_size, const Slice& k, void* arg,
|
||||
void (*handle_result)(void*, const Slice&,
|
||||
const Slice&)) {
|
||||
Cache::Handle* handle = nullptr;
|
||||
Status s = FindTable(file_number, file_size, &handle);
|
||||
if (s.ok()) {
|
||||
Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
|
||||
s = t->InternalGet(options, k, arg, saver);
|
||||
s = t->InternalGet(options, k, arg, handle_result);
|
||||
cache_->Release(handle);
|
||||
}
|
||||
return s;
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_
|
||||
#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/table.h"
|
||||
|
@ -20,40 +22,35 @@ class Env;
|
|||
|
||||
class TableCache {
|
||||
public:
|
||||
TableCache(const std::string& dbname, const Options* options, int entries);
|
||||
TableCache(const std::string& dbname, const Options& options, int entries);
|
||||
~TableCache();
|
||||
|
||||
// Return an iterator for the specified file number (the corresponding
|
||||
// file length must be exactly "file_size" bytes). If "tableptr" is
|
||||
// non-NULL, also sets "*tableptr" to point to the Table object
|
||||
// underlying the returned iterator, or NULL if no Table object underlies
|
||||
// the returned iterator. The returned "*tableptr" object is owned by
|
||||
// the cache and should not be deleted, and is valid for as long as the
|
||||
// non-null, also sets "*tableptr" to point to the Table object
|
||||
// underlying the returned iterator, or to nullptr if no Table object
|
||||
// underlies the returned iterator. The returned "*tableptr" object is owned
|
||||
// by the cache and should not be deleted, and is valid for as long as the
|
||||
// returned iterator is live.
|
||||
Iterator* NewIterator(const ReadOptions& options,
|
||||
uint64_t file_number,
|
||||
uint64_t file_size,
|
||||
Table** tableptr = NULL);
|
||||
Iterator* NewIterator(const ReadOptions& options, uint64_t file_number,
|
||||
uint64_t file_size, Table** tableptr = nullptr);
|
||||
|
||||
// If a seek to internal key "k" in specified file finds an entry,
|
||||
// call (*handle_result)(arg, found_key, found_value).
|
||||
Status Get(const ReadOptions& options,
|
||||
uint64_t file_number,
|
||||
uint64_t file_size,
|
||||
const Slice& k,
|
||||
void* arg,
|
||||
Status Get(const ReadOptions& options, uint64_t file_number,
|
||||
uint64_t file_size, const Slice& k, void* arg,
|
||||
void (*handle_result)(void*, const Slice&, const Slice&));
|
||||
|
||||
// Evict any entry for the specified file number
|
||||
void Evict(uint64_t file_number);
|
||||
|
||||
private:
|
||||
Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**);
|
||||
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
const Options* options_;
|
||||
const Options& options_;
|
||||
Cache* cache_;
|
||||
|
||||
Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**);
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
|
|
@ -12,15 +12,15 @@ namespace leveldb {
|
|||
// Tag numbers for serialized VersionEdit. These numbers are written to
|
||||
// disk and should not be changed.
|
||||
enum Tag {
|
||||
kComparator = 1,
|
||||
kLogNumber = 2,
|
||||
kNextFileNumber = 3,
|
||||
kLastSequence = 4,
|
||||
kCompactPointer = 5,
|
||||
kDeletedFile = 6,
|
||||
kNewFile = 7,
|
||||
kComparator = 1,
|
||||
kLogNumber = 2,
|
||||
kNextFileNumber = 3,
|
||||
kLastSequence = 4,
|
||||
kCompactPointer = 5,
|
||||
kDeletedFile = 6,
|
||||
kNewFile = 7,
|
||||
// 8 was used for large value refs
|
||||
kPrevLogNumber = 9
|
||||
kPrevLogNumber = 9
|
||||
};
|
||||
|
||||
void VersionEdit::Clear() {
|
||||
|
@ -66,12 +66,10 @@ void VersionEdit::EncodeTo(std::string* dst) const {
|
|||
PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode());
|
||||
}
|
||||
|
||||
for (DeletedFileSet::const_iterator iter = deleted_files_.begin();
|
||||
iter != deleted_files_.end();
|
||||
++iter) {
|
||||
for (const auto& deleted_file_kvp : deleted_files_) {
|
||||
PutVarint32(dst, kDeletedFile);
|
||||
PutVarint32(dst, iter->first); // level
|
||||
PutVarint64(dst, iter->second); // file number
|
||||
PutVarint32(dst, deleted_file_kvp.first); // level
|
||||
PutVarint64(dst, deleted_file_kvp.second); // file number
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < new_files_.size(); i++) {
|
||||
|
@ -88,8 +86,7 @@ void VersionEdit::EncodeTo(std::string* dst) const {
|
|||
static bool GetInternalKey(Slice* input, InternalKey* dst) {
|
||||
Slice str;
|
||||
if (GetLengthPrefixedSlice(input, &str)) {
|
||||
dst->DecodeFrom(str);
|
||||
return true;
|
||||
return dst->DecodeFrom(str);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -97,8 +94,7 @@ static bool GetInternalKey(Slice* input, InternalKey* dst) {
|
|||
|
||||
static bool GetLevel(Slice* input, int* level) {
|
||||
uint32_t v;
|
||||
if (GetVarint32(input, &v) &&
|
||||
v < config::kNumLevels) {
|
||||
if (GetVarint32(input, &v) && v < config::kNumLevels) {
|
||||
*level = v;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -109,7 +105,7 @@ static bool GetLevel(Slice* input, int* level) {
|
|||
Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
Clear();
|
||||
Slice input = src;
|
||||
const char* msg = NULL;
|
||||
const char* msg = nullptr;
|
||||
uint32_t tag;
|
||||
|
||||
// Temporary storage for parsing
|
||||
|
@ -119,7 +115,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
|||
Slice str;
|
||||
InternalKey key;
|
||||
|
||||
while (msg == NULL && GetVarint32(&input, &tag)) {
|
||||
while (msg == nullptr && GetVarint32(&input, &tag)) {
|
||||
switch (tag) {
|
||||
case kComparator:
|
||||
if (GetLengthPrefixedSlice(&input, &str)) {
|
||||
|
@ -163,8 +159,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
|||
break;
|
||||
|
||||
case kCompactPointer:
|
||||
if (GetLevel(&input, &level) &&
|
||||
GetInternalKey(&input, &key)) {
|
||||
if (GetLevel(&input, &level) && GetInternalKey(&input, &key)) {
|
||||
compact_pointers_.push_back(std::make_pair(level, key));
|
||||
} else {
|
||||
msg = "compaction pointer";
|
||||
|
@ -172,8 +167,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
|||
break;
|
||||
|
||||
case kDeletedFile:
|
||||
if (GetLevel(&input, &level) &&
|
||||
GetVarint64(&input, &number)) {
|
||||
if (GetLevel(&input, &level) && GetVarint64(&input, &number)) {
|
||||
deleted_files_.insert(std::make_pair(level, number));
|
||||
} else {
|
||||
msg = "deleted file";
|
||||
|
@ -181,8 +175,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
|||
break;
|
||||
|
||||
case kNewFile:
|
||||
if (GetLevel(&input, &level) &&
|
||||
GetVarint64(&input, &f.number) &&
|
||||
if (GetLevel(&input, &level) && GetVarint64(&input, &f.number) &&
|
||||
GetVarint64(&input, &f.file_size) &&
|
||||
GetInternalKey(&input, &f.smallest) &&
|
||||
GetInternalKey(&input, &f.largest)) {
|
||||
|
@ -198,12 +191,12 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
|||
}
|
||||
}
|
||||
|
||||
if (msg == NULL && !input.empty()) {
|
||||
if (msg == nullptr && !input.empty()) {
|
||||
msg = "invalid tag";
|
||||
}
|
||||
|
||||
Status result;
|
||||
if (msg != NULL) {
|
||||
if (msg != nullptr) {
|
||||
result = Status::Corruption("VersionEdit", msg);
|
||||
}
|
||||
return result;
|
||||
|
@ -238,13 +231,11 @@ std::string VersionEdit::DebugString() const {
|
|||
r.append(" ");
|
||||
r.append(compact_pointers_[i].second.DebugString());
|
||||
}
|
||||
for (DeletedFileSet::const_iterator iter = deleted_files_.begin();
|
||||
iter != deleted_files_.end();
|
||||
++iter) {
|
||||
for (const auto& deleted_files_kvp : deleted_files_) {
|
||||
r.append("\n DeleteFile: ");
|
||||
AppendNumberTo(&r, iter->first);
|
||||
AppendNumberTo(&r, deleted_files_kvp.first);
|
||||
r.append(" ");
|
||||
AppendNumberTo(&r, iter->second);
|
||||
AppendNumberTo(&r, deleted_files_kvp.second);
|
||||
}
|
||||
for (size_t i = 0; i < new_files_.size(); i++) {
|
||||
const FileMetaData& f = new_files_[i].second;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
@ -15,20 +16,20 @@ namespace leveldb {
|
|||
class VersionSet;
|
||||
|
||||
struct FileMetaData {
|
||||
int refs;
|
||||
int allowed_seeks; // Seeks allowed until compaction
|
||||
uint64_t number;
|
||||
uint64_t file_size; // File size in bytes
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {}
|
||||
|
||||
FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { }
|
||||
int refs;
|
||||
int allowed_seeks; // Seeks allowed until compaction
|
||||
uint64_t number;
|
||||
uint64_t file_size; // File size in bytes
|
||||
InternalKey smallest; // Smallest internal key served by table
|
||||
InternalKey largest; // Largest internal key served by table
|
||||
};
|
||||
|
||||
class VersionEdit {
|
||||
public:
|
||||
VersionEdit() { Clear(); }
|
||||
~VersionEdit() { }
|
||||
~VersionEdit() = default;
|
||||
|
||||
void Clear();
|
||||
|
||||
|
@ -59,10 +60,8 @@ class VersionEdit {
|
|||
// Add the specified file at the specified number.
|
||||
// REQUIRES: This version has not been saved (see VersionSet::SaveTo)
|
||||
// REQUIRES: "smallest" and "largest" are smallest and largest keys in file
|
||||
void AddFile(int level, uint64_t file,
|
||||
uint64_t file_size,
|
||||
const InternalKey& smallest,
|
||||
const InternalKey& largest) {
|
||||
void AddFile(int level, uint64_t file, uint64_t file_size,
|
||||
const InternalKey& smallest, const InternalKey& largest) {
|
||||
FileMetaData f;
|
||||
f.number = file;
|
||||
f.file_size = file_size;
|
||||
|
@ -84,7 +83,7 @@ class VersionEdit {
|
|||
private:
|
||||
friend class VersionSet;
|
||||
|
||||
typedef std::set< std::pair<int, uint64_t> > DeletedFileSet;
|
||||
typedef std::set<std::pair<int, uint64_t>> DeletedFileSet;
|
||||
|
||||
std::string comparator_;
|
||||
uint64_t log_number_;
|
||||
|
@ -97,9 +96,9 @@ class VersionEdit {
|
|||
bool has_next_file_number_;
|
||||
bool has_last_sequence_;
|
||||
|
||||
std::vector< std::pair<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
|
||||
|
|
|
@ -17,7 +17,7 @@ static void TestEncodeDecode(const VersionEdit& edit) {
|
|||
ASSERT_EQ(encoded, encoded2);
|
||||
}
|
||||
|
||||
class VersionEditTest { };
|
||||
class VersionEditTest {};
|
||||
|
||||
TEST(VersionEditTest, EncodeDecode) {
|
||||
static const uint64_t kBig = 1ull << 50;
|
||||
|
@ -41,6 +41,4 @@ TEST(VersionEditTest, EncodeDecode) {
|
|||
|
||||
} // namespace leveldb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return leveldb::test::RunAllTests();
|
||||
}
|
||||
int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
#include "db/version_set.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
|
@ -84,8 +86,7 @@ Version::~Version() {
|
|||
}
|
||||
|
||||
int FindFile(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const Slice& key) {
|
||||
const std::vector<FileMetaData*>& files, const Slice& key) {
|
||||
uint32_t left = 0;
|
||||
uint32_t right = files.size();
|
||||
while (left < right) {
|
||||
|
@ -104,26 +105,25 @@ int FindFile(const InternalKeyComparator& icmp,
|
|||
return right;
|
||||
}
|
||||
|
||||
static bool AfterFile(const Comparator* ucmp,
|
||||
const Slice* user_key, const FileMetaData* f) {
|
||||
// NULL user_key occurs before all keys and is therefore never after *f
|
||||
return (user_key != NULL &&
|
||||
static bool AfterFile(const Comparator* ucmp, const Slice* user_key,
|
||||
const FileMetaData* f) {
|
||||
// null user_key occurs before all keys and is therefore never after *f
|
||||
return (user_key != nullptr &&
|
||||
ucmp->Compare(*user_key, f->largest.user_key()) > 0);
|
||||
}
|
||||
|
||||
static bool BeforeFile(const Comparator* ucmp,
|
||||
const Slice* user_key, const FileMetaData* f) {
|
||||
// NULL user_key occurs after all keys and is therefore never before *f
|
||||
return (user_key != NULL &&
|
||||
static bool BeforeFile(const Comparator* ucmp, const Slice* user_key,
|
||||
const FileMetaData* f) {
|
||||
// null user_key occurs after all keys and is therefore never before *f
|
||||
return (user_key != nullptr &&
|
||||
ucmp->Compare(*user_key, f->smallest.user_key()) < 0);
|
||||
}
|
||||
|
||||
bool SomeFileOverlapsRange(
|
||||
const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
bool SomeFileOverlapsRange(const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
const Comparator* ucmp = icmp.user_comparator();
|
||||
if (!disjoint_sorted_files) {
|
||||
// Need to check against all files
|
||||
|
@ -141,10 +141,11 @@ bool SomeFileOverlapsRange(
|
|||
|
||||
// Binary search over file list
|
||||
uint32_t index = 0;
|
||||
if (smallest_user_key != NULL) {
|
||||
if (smallest_user_key != nullptr) {
|
||||
// Find the earliest possible internal key for smallest_user_key
|
||||
InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);
|
||||
index = FindFile(icmp, files, small.Encode());
|
||||
InternalKey small_key(*smallest_user_key, kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
index = FindFile(icmp, files, small_key.Encode());
|
||||
}
|
||||
|
||||
if (index >= files.size()) {
|
||||
|
@ -164,25 +165,21 @@ class Version::LevelFileNumIterator : public Iterator {
|
|||
public:
|
||||
LevelFileNumIterator(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>* flist)
|
||||
: icmp_(icmp),
|
||||
flist_(flist),
|
||||
index_(flist->size()) { // Marks as invalid
|
||||
: 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
|
||||
|
@ -190,17 +187,18 @@ 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);
|
||||
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_;
|
||||
const std::vector<FileMetaData*>* const flist_;
|
||||
|
@ -210,16 +208,14 @@ class Version::LevelFileNumIterator : public Iterator {
|
|||
mutable char value_buf_[16];
|
||||
};
|
||||
|
||||
static Iterator* GetFileIterator(void* arg,
|
||||
const ReadOptions& options,
|
||||
static Iterator* GetFileIterator(void* arg, const ReadOptions& options,
|
||||
const Slice& file_value) {
|
||||
TableCache* cache = reinterpret_cast<TableCache*>(arg);
|
||||
if (file_value.size() != 16) {
|
||||
return NewErrorIterator(
|
||||
Status::Corruption("FileReader invoked with unexpected value"));
|
||||
} else {
|
||||
return cache->NewIterator(options,
|
||||
DecodeFixed64(file_value.data()),
|
||||
return cache->NewIterator(options, DecodeFixed64(file_value.data()),
|
||||
DecodeFixed64(file_value.data() + 8));
|
||||
}
|
||||
}
|
||||
|
@ -227,17 +223,16 @@ static Iterator* GetFileIterator(void* arg,
|
|||
Iterator* Version::NewConcatenatingIterator(const ReadOptions& options,
|
||||
int level) const {
|
||||
return NewTwoLevelIterator(
|
||||
new LevelFileNumIterator(vset_->icmp_, &files_[level]),
|
||||
&GetFileIterator, vset_->table_cache_, options);
|
||||
new LevelFileNumIterator(vset_->icmp_, &files_[level]), &GetFileIterator,
|
||||
vset_->table_cache_, options);
|
||||
}
|
||||
|
||||
void Version::AddIterators(const ReadOptions& options,
|
||||
std::vector<Iterator*>* iters) {
|
||||
// Merge all level zero files together since they may overlap
|
||||
for (size_t i = 0; i < files_[0].size(); i++) {
|
||||
iters->push_back(
|
||||
vset_->table_cache_->NewIterator(
|
||||
options, files_[0][i]->number, files_[0][i]->file_size));
|
||||
iters->push_back(vset_->table_cache_->NewIterator(
|
||||
options, files_[0][i]->number, files_[0][i]->file_size));
|
||||
}
|
||||
|
||||
// For levels > 0, we can use a concatenating iterator that sequentially
|
||||
|
@ -264,7 +259,7 @@ struct Saver {
|
|||
Slice user_key;
|
||||
std::string* value;
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
static void SaveValue(void* arg, const Slice& ikey, const Slice& v) {
|
||||
Saver* s = reinterpret_cast<Saver*>(arg);
|
||||
ParsedInternalKey parsed_key;
|
||||
|
@ -284,10 +279,8 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
|
|||
return a->number > b->number;
|
||||
}
|
||||
|
||||
void Version::ForEachOverlapping(Slice user_key, Slice internal_key,
|
||||
void* arg,
|
||||
void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg,
|
||||
bool (*func)(void*, int, FileMetaData*)) {
|
||||
// TODO(sanjay): Change Version::Get() to use this function.
|
||||
const Comparator* ucmp = vset_->icmp_.user_comparator();
|
||||
|
||||
// Search level-0 in order from newest to oldest.
|
||||
|
@ -329,110 +322,89 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key,
|
|||
}
|
||||
}
|
||||
|
||||
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 = NULL;
|
||||
Status Version::Get(const ReadOptions& options, const LookupKey& k,
|
||||
std::string* value, GetStats* stats) {
|
||||
stats->seek_file = nullptr;
|
||||
stats->seek_file_level = -1;
|
||||
FileMetaData* last_file_read = NULL;
|
||||
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 an 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 = NULL;
|
||||
num_files = 0;
|
||||
} else {
|
||||
tmp2 = files[index];
|
||||
if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) {
|
||||
// All of "tmp2" is past any data for user_key
|
||||
files = NULL;
|
||||
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 != NULL && stats->seek_file == NULL) {
|
||||
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) {
|
||||
FileMetaData* f = stats.seek_file;
|
||||
if (f != NULL) {
|
||||
if (f != nullptr) {
|
||||
f->allowed_seeks--;
|
||||
if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) {
|
||||
if (f->allowed_seeks <= 0 && file_to_compact_ == nullptr) {
|
||||
file_to_compact_ = f;
|
||||
file_to_compact_level_ = stats.seek_file_level;
|
||||
return true;
|
||||
|
@ -479,9 +451,7 @@ bool Version::RecordReadSample(Slice internal_key) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Version::Ref() {
|
||||
++refs_;
|
||||
}
|
||||
void Version::Ref() { ++refs_; }
|
||||
|
||||
void Version::Unref() {
|
||||
assert(this != &vset_->dummy_versions_);
|
||||
|
@ -492,16 +462,14 @@ void Version::Unref() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Version::OverlapInLevel(int level,
|
||||
const Slice* smallest_user_key,
|
||||
bool Version::OverlapInLevel(int level, const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level],
|
||||
smallest_user_key, largest_user_key);
|
||||
}
|
||||
|
||||
int Version::PickLevelForMemTableOutput(
|
||||
const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key) {
|
||||
int Version::PickLevelForMemTableOutput(const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key) {
|
||||
int level = 0;
|
||||
if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) {
|
||||
// Push to next level if there is no overlap in next level,
|
||||
|
@ -528,40 +496,39 @@ int Version::PickLevelForMemTableOutput(
|
|||
}
|
||||
|
||||
// Store in "*inputs" all files in "level" that overlap [begin,end]
|
||||
void Version::GetOverlappingInputs(
|
||||
int level,
|
||||
const InternalKey* begin,
|
||||
const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs) {
|
||||
void Version::GetOverlappingInputs(int level, const InternalKey* begin,
|
||||
const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs) {
|
||||
assert(level >= 0);
|
||||
assert(level < config::kNumLevels);
|
||||
inputs->clear();
|
||||
Slice user_begin, user_end;
|
||||
if (begin != NULL) {
|
||||
if (begin != nullptr) {
|
||||
user_begin = begin->user_key();
|
||||
}
|
||||
if (end != NULL) {
|
||||
if (end != nullptr) {
|
||||
user_end = end->user_key();
|
||||
}
|
||||
const Comparator* user_cmp = vset_->icmp_.user_comparator();
|
||||
for (size_t i = 0; i < files_[level].size(); ) {
|
||||
for (size_t i = 0; i < files_[level].size();) {
|
||||
FileMetaData* f = files_[level][i++];
|
||||
const Slice file_start = f->smallest.user_key();
|
||||
const Slice file_limit = f->largest.user_key();
|
||||
if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) {
|
||||
if (begin != nullptr && user_cmp->Compare(file_limit, user_begin) < 0) {
|
||||
// "f" is completely before specified range; skip it
|
||||
} else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) {
|
||||
} else if (end != nullptr && user_cmp->Compare(file_start, user_end) > 0) {
|
||||
// "f" is completely after specified range; skip it
|
||||
} else {
|
||||
inputs->push_back(f);
|
||||
if (level == 0) {
|
||||
// Level-0 files may overlap each other. So check if the newly
|
||||
// added file has expanded the range. If so, restart search.
|
||||
if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) {
|
||||
if (begin != nullptr && user_cmp->Compare(file_start, user_begin) < 0) {
|
||||
user_begin = file_start;
|
||||
inputs->clear();
|
||||
i = 0;
|
||||
} else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) {
|
||||
} else if (end != nullptr &&
|
||||
user_cmp->Compare(file_limit, user_end) > 0) {
|
||||
user_end = file_limit;
|
||||
inputs->clear();
|
||||
i = 0;
|
||||
|
@ -629,9 +596,7 @@ class VersionSet::Builder {
|
|||
|
||||
public:
|
||||
// Initialize a builder with the files from *base and other info from *vset
|
||||
Builder(VersionSet* vset, Version* base)
|
||||
: vset_(vset),
|
||||
base_(base) {
|
||||
Builder(VersionSet* vset, Version* base) : vset_(vset), base_(base) {
|
||||
base_->Ref();
|
||||
BySmallestKey cmp;
|
||||
cmp.internal_comparator = &vset_->icmp_;
|
||||
|
@ -645,8 +610,8 @@ class VersionSet::Builder {
|
|||
const FileSet* added = levels_[level].added_files;
|
||||
std::vector<FileMetaData*> to_unref;
|
||||
to_unref.reserve(added->size());
|
||||
for (FileSet::const_iterator it = added->begin();
|
||||
it != added->end(); ++it) {
|
||||
for (FileSet::const_iterator it = added->begin(); it != added->end();
|
||||
++it) {
|
||||
to_unref.push_back(*it);
|
||||
}
|
||||
delete added;
|
||||
|
@ -671,12 +636,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);
|
||||
}
|
||||
|
||||
|
@ -699,7 +661,7 @@ class VersionSet::Builder {
|
|||
// same as the compaction of 40KB of data. We are a little
|
||||
// conservative and allow approximately one seek for every 16KB
|
||||
// of data before triggering a compaction.
|
||||
f->allowed_seeks = (f->file_size / 16384);
|
||||
f->allowed_seeks = static_cast<int>((f->file_size / 16384U));
|
||||
if (f->allowed_seeks < 100) f->allowed_seeks = 100;
|
||||
|
||||
levels_[level].deleted_files.erase(f->number);
|
||||
|
@ -717,20 +679,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);
|
||||
base_iter != bpos;
|
||||
++base_iter) {
|
||||
for (std::vector<FileMetaData*>::const_iterator bpos =
|
||||
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
|
||||
|
@ -742,7 +701,7 @@ class VersionSet::Builder {
|
|||
// Make sure there is no overlap in levels > 0
|
||||
if (level > 0) {
|
||||
for (uint32_t i = 1; i < v->files_[level].size(); i++) {
|
||||
const InternalKey& prev_end = v->files_[level][i-1]->largest;
|
||||
const InternalKey& prev_end = v->files_[level][i - 1]->largest;
|
||||
const InternalKey& this_begin = v->files_[level][i]->smallest;
|
||||
if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) {
|
||||
fprintf(stderr, "overlapping ranges in same level %s vs. %s\n",
|
||||
|
@ -763,7 +722,7 @@ class VersionSet::Builder {
|
|||
std::vector<FileMetaData*>* files = &v->files_[level];
|
||||
if (level > 0 && !files->empty()) {
|
||||
// Must not overlap
|
||||
assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest,
|
||||
assert(vset_->icmp_.Compare((*files)[files->size() - 1]->largest,
|
||||
f->smallest) < 0);
|
||||
}
|
||||
f->refs++;
|
||||
|
@ -772,8 +731,7 @@ class VersionSet::Builder {
|
|||
}
|
||||
};
|
||||
|
||||
VersionSet::VersionSet(const std::string& dbname,
|
||||
const Options* options,
|
||||
VersionSet::VersionSet(const std::string& dbname, const Options* options,
|
||||
TableCache* table_cache,
|
||||
const InternalKeyComparator* cmp)
|
||||
: env_(options->env),
|
||||
|
@ -786,10 +744,10 @@ VersionSet::VersionSet(const std::string& dbname,
|
|||
last_sequence_(0),
|
||||
log_number_(0),
|
||||
prev_log_number_(0),
|
||||
descriptor_file_(NULL),
|
||||
descriptor_log_(NULL),
|
||||
descriptor_file_(nullptr),
|
||||
descriptor_log_(nullptr),
|
||||
dummy_versions_(this),
|
||||
current_(NULL) {
|
||||
current_(nullptr) {
|
||||
AppendVersion(new Version(this));
|
||||
}
|
||||
|
||||
|
@ -804,7 +762,7 @@ void VersionSet::AppendVersion(Version* v) {
|
|||
// Make "v" current
|
||||
assert(v->refs_ == 0);
|
||||
assert(v != current_);
|
||||
if (current_ != NULL) {
|
||||
if (current_ != nullptr) {
|
||||
current_->Unref();
|
||||
}
|
||||
current_ = v;
|
||||
|
@ -844,10 +802,10 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
|
|||
// a temporary file that contains a snapshot of the current version.
|
||||
std::string new_manifest_file;
|
||||
Status s;
|
||||
if (descriptor_log_ == NULL) {
|
||||
if (descriptor_log_ == nullptr) {
|
||||
// No reason to unlock *mu here since we only hit this path in the
|
||||
// first call to LogAndApply (when opening the database).
|
||||
assert(descriptor_file_ == NULL);
|
||||
assert(descriptor_file_ == nullptr);
|
||||
new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_);
|
||||
edit->SetNextFile(next_file_number_);
|
||||
s = env_->NewWritableFile(new_manifest_file, &descriptor_file_);
|
||||
|
@ -893,8 +851,8 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
|
|||
if (!new_manifest_file.empty()) {
|
||||
delete descriptor_log_;
|
||||
delete descriptor_file_;
|
||||
descriptor_log_ = NULL;
|
||||
descriptor_file_ = NULL;
|
||||
descriptor_log_ = nullptr;
|
||||
descriptor_file_ = nullptr;
|
||||
env_->DeleteFile(new_manifest_file);
|
||||
}
|
||||
}
|
||||
|
@ -902,10 +860,10 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {
|
|||
return s;
|
||||
}
|
||||
|
||||
Status VersionSet::Recover(bool *save_manifest) {
|
||||
Status VersionSet::Recover(bool* save_manifest) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Status* status;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
if (this->status->ok()) *this->status = s;
|
||||
}
|
||||
};
|
||||
|
@ -916,7 +874,7 @@ Status VersionSet::Recover(bool *save_manifest) {
|
|||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (current.empty() || current[current.size()-1] != '\n') {
|
||||
if (current.empty() || current[current.size() - 1] != '\n') {
|
||||
return Status::Corruption("CURRENT file does not end with newline");
|
||||
}
|
||||
current.resize(current.size() - 1);
|
||||
|
@ -925,6 +883,10 @@ Status VersionSet::Recover(bool *save_manifest) {
|
|||
SequentialFile* file;
|
||||
s = env_->NewSequentialFile(dscname, &file);
|
||||
if (!s.ok()) {
|
||||
if (s.IsNotFound()) {
|
||||
return Status::Corruption("CURRENT points to a non-existent file",
|
||||
s.ToString());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -941,7 +903,8 @@ Status VersionSet::Recover(bool *save_manifest) {
|
|||
{
|
||||
LogReporter reporter;
|
||||
reporter.status = &s;
|
||||
log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/);
|
||||
log::Reader reader(file, &reporter, true /*checksum*/,
|
||||
0 /*initial_offset*/);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
while (reader.ReadRecord(&record, &scratch) && s.ok()) {
|
||||
|
@ -982,7 +945,7 @@ Status VersionSet::Recover(bool *save_manifest) {
|
|||
}
|
||||
}
|
||||
delete file;
|
||||
file = NULL;
|
||||
file = nullptr;
|
||||
|
||||
if (s.ok()) {
|
||||
if (!have_next_file) {
|
||||
|
@ -1040,12 +1003,12 @@ bool VersionSet::ReuseManifest(const std::string& dscname,
|
|||
return false;
|
||||
}
|
||||
|
||||
assert(descriptor_file_ == NULL);
|
||||
assert(descriptor_log_ == NULL);
|
||||
assert(descriptor_file_ == nullptr);
|
||||
assert(descriptor_log_ == nullptr);
|
||||
Status r = env_->NewAppendableFile(dscname, &descriptor_file_);
|
||||
if (!r.ok()) {
|
||||
Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str());
|
||||
assert(descriptor_file_ == NULL);
|
||||
assert(descriptor_file_ == nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1066,7 +1029,7 @@ void VersionSet::Finalize(Version* v) {
|
|||
int best_level = -1;
|
||||
double best_score = -1;
|
||||
|
||||
for (int level = 0; level < config::kNumLevels-1; level++) {
|
||||
for (int level = 0; level < config::kNumLevels - 1; level++) {
|
||||
double score;
|
||||
if (level == 0) {
|
||||
// We treat level-0 specially by bounding the number of files
|
||||
|
@ -1081,7 +1044,7 @@ void VersionSet::Finalize(Version* v) {
|
|||
// setting, or very high compression ratios, or lots of
|
||||
// overwrites/deletions).
|
||||
score = v->files_[level].size() /
|
||||
static_cast<double>(config::kL0_CompactionTrigger);
|
||||
static_cast<double>(config::kL0_CompactionTrigger);
|
||||
} else {
|
||||
// Compute the ratio of current size to size limit.
|
||||
const uint64_t level_bytes = TotalFileSize(v->files_[level]);
|
||||
|
@ -1137,16 +1100,12 @@ int VersionSet::NumLevelFiles(int level) const {
|
|||
|
||||
const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const {
|
||||
// Update code if kNumLevels changes
|
||||
assert(config::kNumLevels == 7);
|
||||
static_assert(config::kNumLevels == 7, "");
|
||||
snprintf(scratch->buffer, sizeof(scratch->buffer),
|
||||
"files[ %d %d %d %d %d %d %d ]",
|
||||
int(current_->files_[0].size()),
|
||||
int(current_->files_[1].size()),
|
||||
int(current_->files_[2].size()),
|
||||
int(current_->files_[3].size()),
|
||||
int(current_->files_[4].size()),
|
||||
int(current_->files_[5].size()),
|
||||
int(current_->files_[6].size()));
|
||||
"files[ %d %d %d %d %d %d %d ]", int(current_->files_[0].size()),
|
||||
int(current_->files_[1].size()), int(current_->files_[2].size()),
|
||||
int(current_->files_[3].size()), int(current_->files_[4].size()),
|
||||
int(current_->files_[5].size()), int(current_->files_[6].size()));
|
||||
return scratch->buffer;
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1131,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) {
|
|||
Table* tableptr;
|
||||
Iterator* iter = table_cache_->NewIterator(
|
||||
ReadOptions(), files[i]->number, files[i]->file_size, &tableptr);
|
||||
if (tableptr != NULL) {
|
||||
if (tableptr != nullptr) {
|
||||
result += tableptr->ApproximateOffsetOf(ikey.Encode());
|
||||
}
|
||||
delete iter;
|
||||
|
@ -1183,8 +1142,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) {
|
|||
}
|
||||
|
||||
void VersionSet::AddLiveFiles(std::set<uint64_t>* live) {
|
||||
for (Version* v = dummy_versions_.next_;
|
||||
v != &dummy_versions_;
|
||||
for (Version* v = dummy_versions_.next_; v != &dummy_versions_;
|
||||
v = v->next_) {
|
||||
for (int level = 0; level < config::kNumLevels; level++) {
|
||||
const std::vector<FileMetaData*>& files = v->files_[level];
|
||||
|
@ -1207,7 +1165,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() {
|
|||
for (int level = 1; level < config::kNumLevels - 1; level++) {
|
||||
for (size_t i = 0; i < current_->files_[level].size(); i++) {
|
||||
const FileMetaData* f = current_->files_[level][i];
|
||||
current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest,
|
||||
current_->GetOverlappingInputs(level + 1, &f->smallest, &f->largest,
|
||||
&overlaps);
|
||||
const int64_t sum = TotalFileSize(overlaps);
|
||||
if (sum > result) {
|
||||
|
@ -1222,8 +1180,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() {
|
|||
// *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void VersionSet::GetRange(const std::vector<FileMetaData*>& inputs,
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) {
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
assert(!inputs.empty());
|
||||
smallest->Clear();
|
||||
largest->Clear();
|
||||
|
@ -1248,8 +1205,7 @@ void VersionSet::GetRange(const std::vector<FileMetaData*>& inputs,
|
|||
// REQUIRES: inputs is not empty
|
||||
void VersionSet::GetRange2(const std::vector<FileMetaData*>& inputs1,
|
||||
const std::vector<FileMetaData*>& inputs2,
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) {
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
std::vector<FileMetaData*> all = inputs1;
|
||||
all.insert(all.end(), inputs2.begin(), inputs2.end());
|
||||
GetRange(all, smallest, largest);
|
||||
|
@ -1271,8 +1227,8 @@ Iterator* VersionSet::MakeInputIterator(Compaction* c) {
|
|||
if (c->level() + which == 0) {
|
||||
const std::vector<FileMetaData*>& files = c->inputs_[which];
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
list[num++] = table_cache_->NewIterator(
|
||||
options, files[i]->number, files[i]->file_size);
|
||||
list[num++] = table_cache_->NewIterator(options, files[i]->number,
|
||||
files[i]->file_size);
|
||||
}
|
||||
} else {
|
||||
// Create concatenating iterator for the files from this level
|
||||
|
@ -1295,11 +1251,11 @@ Compaction* VersionSet::PickCompaction() {
|
|||
// We prefer compactions triggered by too much data in a level over
|
||||
// the compactions triggered by seeks.
|
||||
const bool size_compaction = (current_->compaction_score_ >= 1);
|
||||
const bool seek_compaction = (current_->file_to_compact_ != NULL);
|
||||
const bool seek_compaction = (current_->file_to_compact_ != nullptr);
|
||||
if (size_compaction) {
|
||||
level = current_->compaction_level_;
|
||||
assert(level >= 0);
|
||||
assert(level+1 < config::kNumLevels);
|
||||
assert(level + 1 < config::kNumLevels);
|
||||
c = new Compaction(options_, level);
|
||||
|
||||
// Pick the first file that comes after compact_pointer_[level]
|
||||
|
@ -1320,7 +1276,7 @@ Compaction* VersionSet::PickCompaction() {
|
|||
c = new Compaction(options_, level);
|
||||
c->inputs_[0].push_back(current_->file_to_compact_);
|
||||
} else {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
c->input_version_ = current_;
|
||||
|
@ -1342,12 +1298,94 @@ Compaction* VersionSet::PickCompaction() {
|
|||
return c;
|
||||
}
|
||||
|
||||
// Finds the largest key in a vector of files. Returns true if files it not
|
||||
// empty.
|
||||
bool FindLargestKey(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
InternalKey* largest_key) {
|
||||
if (files.empty()) {
|
||||
return false;
|
||||
}
|
||||
*largest_key = files[0]->largest;
|
||||
for (size_t i = 1; i < files.size(); ++i) {
|
||||
FileMetaData* f = files[i];
|
||||
if (icmp.Compare(f->largest, *largest_key) > 0) {
|
||||
*largest_key = f->largest;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finds minimum file b2=(l2, u2) in level file for which l2 > u1 and
|
||||
// user_key(l2) = user_key(u1)
|
||||
FileMetaData* FindSmallestBoundaryFile(
|
||||
const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& level_files,
|
||||
const InternalKey& largest_key) {
|
||||
const Comparator* user_cmp = icmp.user_comparator();
|
||||
FileMetaData* smallest_boundary_file = nullptr;
|
||||
for (size_t i = 0; i < level_files.size(); ++i) {
|
||||
FileMetaData* f = level_files[i];
|
||||
if (icmp.Compare(f->smallest, largest_key) > 0 &&
|
||||
user_cmp->Compare(f->smallest.user_key(), largest_key.user_key()) ==
|
||||
0) {
|
||||
if (smallest_boundary_file == nullptr ||
|
||||
icmp.Compare(f->smallest, smallest_boundary_file->smallest) < 0) {
|
||||
smallest_boundary_file = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return smallest_boundary_file;
|
||||
}
|
||||
|
||||
// Extracts the largest file b1 from |compaction_files| and then searches for a
|
||||
// b2 in |level_files| for which user_key(u1) = user_key(l2). If it finds such a
|
||||
// file b2 (known as a boundary file) it adds it to |compaction_files| and then
|
||||
// searches again using this new upper bound.
|
||||
//
|
||||
// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and
|
||||
// user_key(u1) = user_key(l2), and if we compact b1 but not b2 then a
|
||||
// subsequent get operation will yield an incorrect result because it will
|
||||
// return the record from b2 in level i rather than from b1 because it searches
|
||||
// level by level for records matching the supplied user key.
|
||||
//
|
||||
// parameters:
|
||||
// in level_files: List of files to search for boundary files.
|
||||
// in/out compaction_files: List of files to extend by adding boundary files.
|
||||
void AddBoundaryInputs(const InternalKeyComparator& icmp,
|
||||
const std::vector<FileMetaData*>& level_files,
|
||||
std::vector<FileMetaData*>* compaction_files) {
|
||||
InternalKey largest_key;
|
||||
|
||||
// Quick return if compaction_files is empty.
|
||||
if (!FindLargestKey(icmp, *compaction_files, &largest_key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool continue_searching = true;
|
||||
while (continue_searching) {
|
||||
FileMetaData* smallest_boundary_file =
|
||||
FindSmallestBoundaryFile(icmp, level_files, largest_key);
|
||||
|
||||
// If a boundary file was found advance largest_key, otherwise we're done.
|
||||
if (smallest_boundary_file != NULL) {
|
||||
compaction_files->push_back(smallest_boundary_file);
|
||||
largest_key = smallest_boundary_file->largest;
|
||||
} else {
|
||||
continue_searching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSet::SetupOtherInputs(Compaction* c) {
|
||||
const int level = c->level();
|
||||
InternalKey smallest, largest;
|
||||
|
||||
AddBoundaryInputs(icmp_, current_->files_[level], &c->inputs_[0]);
|
||||
GetRange(c->inputs_[0], &smallest, &largest);
|
||||
|
||||
current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]);
|
||||
current_->GetOverlappingInputs(level + 1, &smallest, &largest,
|
||||
&c->inputs_[1]);
|
||||
|
||||
// Get entire range covered by compaction
|
||||
InternalKey all_start, all_limit;
|
||||
|
@ -1358,6 +1396,7 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|||
if (!c->inputs_[1].empty()) {
|
||||
std::vector<FileMetaData*> expanded0;
|
||||
current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0);
|
||||
AddBoundaryInputs(icmp_, current_->files_[level], &expanded0);
|
||||
const int64_t inputs0_size = TotalFileSize(c->inputs_[0]);
|
||||
const int64_t inputs1_size = TotalFileSize(c->inputs_[1]);
|
||||
const int64_t expanded0_size = TotalFileSize(expanded0);
|
||||
|
@ -1367,18 +1406,14 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded0, &new_start, &new_limit);
|
||||
std::vector<FileMetaData*> expanded1;
|
||||
current_->GetOverlappingInputs(level+1, &new_start, &new_limit,
|
||||
current_->GetOverlappingInputs(level + 1, &new_start, &new_limit,
|
||||
&expanded1);
|
||||
if (expanded1.size() == c->inputs_[1].size()) {
|
||||
Log(options_->info_log,
|
||||
"Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n",
|
||||
level,
|
||||
int(c->inputs_[0].size()),
|
||||
int(c->inputs_[1].size()),
|
||||
long(inputs0_size), long(inputs1_size),
|
||||
int(expanded0.size()),
|
||||
int(expanded1.size()),
|
||||
long(expanded0_size), long(inputs1_size));
|
||||
level, int(c->inputs_[0].size()), int(c->inputs_[1].size()),
|
||||
long(inputs0_size), long(inputs1_size), int(expanded0.size()),
|
||||
int(expanded1.size()), long(expanded0_size), long(inputs1_size));
|
||||
smallest = new_start;
|
||||
largest = new_limit;
|
||||
c->inputs_[0] = expanded0;
|
||||
|
@ -1395,13 +1430,6 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|||
&c->grandparents_);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
Log(options_->info_log, "Compacting %d '%s' .. '%s'",
|
||||
level,
|
||||
smallest.DebugString().c_str(),
|
||||
largest.DebugString().c_str());
|
||||
}
|
||||
|
||||
// Update the place where we will do the next compaction for this level.
|
||||
// We update this immediately instead of waiting for the VersionEdit
|
||||
// to be applied so that if the compaction fails, we will try a different
|
||||
|
@ -1410,14 +1438,12 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|||
c->edit_.SetCompactPointer(level, largest);
|
||||
}
|
||||
|
||||
Compaction* VersionSet::CompactRange(
|
||||
int level,
|
||||
const InternalKey* begin,
|
||||
const InternalKey* end) {
|
||||
Compaction* VersionSet::CompactRange(int level, const InternalKey* begin,
|
||||
const InternalKey* end) {
|
||||
std::vector<FileMetaData*> inputs;
|
||||
current_->GetOverlappingInputs(level, begin, end, &inputs);
|
||||
if (inputs.empty()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Avoid compacting too much in one shot in case the range is large.
|
||||
|
@ -1448,7 +1474,7 @@ Compaction* VersionSet::CompactRange(
|
|||
Compaction::Compaction(const Options* options, int level)
|
||||
: level_(level),
|
||||
max_output_file_size_(MaxFileSizeForLevel(options, level)),
|
||||
input_version_(NULL),
|
||||
input_version_(nullptr),
|
||||
grandparent_index_(0),
|
||||
seen_key_(false),
|
||||
overlapped_bytes_(0) {
|
||||
|
@ -1458,7 +1484,7 @@ Compaction::Compaction(const Options* options, int level)
|
|||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
if (input_version_ != NULL) {
|
||||
if (input_version_ != nullptr) {
|
||||
input_version_->Unref();
|
||||
}
|
||||
}
|
||||
|
@ -1486,7 +1512,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
|
||||
|
@ -1507,8 +1533,9 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) {
|
|||
// Scan to find earliest grandparent file that contains key.
|
||||
const InternalKeyComparator* icmp = &vset->icmp_;
|
||||
while (grandparent_index_ < grandparents_.size() &&
|
||||
icmp->Compare(internal_key,
|
||||
grandparents_[grandparent_index_]->largest.Encode()) > 0) {
|
||||
icmp->Compare(internal_key,
|
||||
grandparents_[grandparent_index_]->largest.Encode()) >
|
||||
0) {
|
||||
if (seen_key_) {
|
||||
overlapped_bytes_ += grandparents_[grandparent_index_]->file_size;
|
||||
}
|
||||
|
@ -1526,9 +1553,9 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) {
|
|||
}
|
||||
|
||||
void Compaction::ReleaseInputs() {
|
||||
if (input_version_ != NULL) {
|
||||
if (input_version_ != nullptr) {
|
||||
input_version_->Unref();
|
||||
input_version_ = NULL;
|
||||
input_version_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue