Internal changes

PiperOrigin-RevId: 621907514
This commit is contained in:
Richard O'Grady
2024-04-04 17:48:40 +00:00
committed by Danila Kutenin
parent 0212163f6e
commit e1e7329dc7
18 changed files with 83 additions and 391 deletions

View File

@@ -1,4 +1,2 @@
# googletest requires C++14 or above
build --cxxopt='-std=c++17'
# Enable Bzlmod for every Bazel command
common --enable_bzlmod

View File

@@ -1,43 +0,0 @@
name: riscv64-qemu-test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
env:
RISCV_CROSSCOMPILE: "ON"
riscv_gnu_toolchain_download_path: https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.07.03/riscv64-glibc-ubuntu-24.04-gcc-nightly-2025.07.03-nightly.tar.xz
RISCV_PATH: /opt/riscv
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y --no-install-recommends \
qemu-user qemu-user-static \
build-essential \
cmake \
git
sudo mkdir -p $RISCV_PATH
wget ${riscv_gnu_toolchain_download_path} -O riscv-toolchain.tar.xz
sudo tar -xvf riscv-toolchain.tar.xz -C $RISCV_PATH --strip-components=1
sudo sed -i "s|libdir='/mnt/riscv/riscv64-unknown-linux-gnu/lib'|libdir='$RISCV_PATH/riscv64-unknown-linux-gnu/lib'|g" $RISCV_PATH/riscv64-unknown-linux-gnu/lib/libatomic.la
- name: Build and Run Unit Tests
run: |
export PATH=$RISCV_PATH/bin:$PATH
export LD_LIBRARY_PATH="/opt/riscv/lib:$LD_LIBRARY_PATH"
export QEMU_LD_PREFIX=$RISCV_PATH/sysroot
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ../
make -j$(nproc)
make test
- name: Run Benchmark
run: ./build/snappy_benchmark
working-directory: ./

1
.gitignore vendored
View File

@@ -6,5 +6,4 @@
# Build directory.
build/
/bazel-*
MODULE.bazel.lock
out/

View File

@@ -26,21 +26,11 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
exports_files([
"COPYING",
"COPYING.notestdata",
])
SNAPPY_VERSION = (1, 2, 2)
SNAPPY_VERSION = (1, 1, 10)
config_setting(
name = "windows",
@@ -50,7 +40,7 @@ config_setting(
cc_library(
name = "config",
hdrs = ["config.h"],
defines = ["HAVE_CONFIG_H"],
defines = ["HAVE_CONFIG_H"]
)
cc_library(
@@ -62,10 +52,8 @@ cc_library(
name = "snappy-stubs-internal",
srcs = ["snappy-stubs-internal.cc"],
hdrs = ["snappy-stubs-internal.h"],
implementation_deps = [
":config",
],
deps = [
":config",
":snappy-stubs-public",
],
)
@@ -82,15 +70,12 @@ cc_library(
"snappy-sinksource.h",
],
copts = select({
":windows": [],
"//conditions:default": [
"-Wno-sign-compare",
],
}),
implementation_deps = [
":config",
],
":windows": [],
"//conditions:default": [
"-Wno-sign-compare",
]}),
deps = [
":config",
":snappy-stubs-internal",
":snappy-stubs-public",
],
@@ -129,7 +114,7 @@ cc_test(
deps = [
":snappy",
":snappy-test",
"@com_google_benchmark//:benchmark_main",
"//third_party/benchmark:benchmark_main",
],
)
@@ -142,15 +127,15 @@ cc_test(
deps = [
":snappy",
":snappy-test",
"@com_google_googletest//:gtest_main",
"//third_party/googletest:gtest_main",
],
)
# Generate a config.h similar to what cmake would produce.
write_file(
genrule(
name = "config_h",
out = "config.h",
content = """\
outs = ["config.h"],
cmd = """cat <<EOF >$@
#define HAVE_STDDEF_H 1
#define HAVE_STDINT_H 1
#ifdef __has_builtin
@@ -207,17 +192,19 @@ write_file(
# define SNAPPY_IS_BIG_ENDIAN 1
# endif
#endif
""".splitlines(),
EOF
""",
)
expand_template(
genrule(
name = "snappy_stubs_public_h",
out = "snappy-stubs-public.h",
substitutions = {
"${HAVE_SYS_UIO_H_01}": "!_WIN32",
"${PROJECT_VERSION_MAJOR}": str(SNAPPY_VERSION[0]),
"${PROJECT_VERSION_MINOR}": str(SNAPPY_VERSION[1]),
"${PROJECT_VERSION_PATCH}": str(SNAPPY_VERSION[2]),
},
template = "snappy-stubs-public.h.in",
srcs = ["snappy-stubs-public.h.in"],
outs = ["snappy-stubs-public.h"],
# Assume sys/uio.h is available on non-Windows.
# Set the version numbers.
cmd = ("""sed -e 's/$${HAVE_SYS_UIO_H_01}/!_WIN32/g' \
-e 's/$${PROJECT_VERSION_MAJOR}/%d/g' \
-e 's/$${PROJECT_VERSION_MINOR}/%d/g' \
-e 's/$${PROJECT_VERSION_PATCH}/%d/g' \
$< >$@""" % SNAPPY_VERSION),
)

View File

@@ -26,8 +26,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.10)
project(Snappy VERSION 1.2.2 LANGUAGES C CXX)
cmake_minimum_required(VERSION 3.1)
project(Snappy VERSION 1.1.10 LANGUAGES C CXX)
# C++ standard can be overridden when this is used as a sub-project.
if(NOT CMAKE_CXX_STANDARD)
@@ -38,7 +38,7 @@ if(NOT CMAKE_CXX_STANDARD)
endif(NOT CMAKE_CXX_STANDARD)
# https://github.com/izenecloud/cmake/blob/master/SetCompilerWarningAll.cmake
if(MSVC)
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]")
@@ -55,7 +55,7 @@ if(MSVC)
# Disable RTTI.
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else(MSVC)
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")
@@ -85,7 +85,7 @@ else(MSVC)
# Disable RTTI.
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif(MSVC)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
# it prominent in the GUI.
@@ -155,20 +155,10 @@ int main() {
return __builtin_expect(0, 1);
}" HAVE_BUILTIN_EXPECT)
# Check if the built-in __builtin_ctz (count trailing zeros) is available.
# Require either a non-RISC-V target, or a RISC-V core that implements
# the Zbb bit-manipulation extension where ctz is guaranteed.
check_cxx_source_compiles("
#ifdef __riscv
#ifdef __riscv_zbb
int main() { return __builtin_ctzll(0); }
#else
#error \"ZBB not enabled in current config\"
#endif
#else
int main() { return __builtin_ctzll(0); }
#endif
" HAVE_BUILTIN_CTZ)
int main() {
return __builtin_ctzll(0);
}" HAVE_BUILTIN_CTZ)
check_cxx_source_compiles("
int main() {
@@ -216,41 +206,13 @@ int main() {
check_cxx_source_compiles("
#include <arm_neon.h>
#include <stdint.h>
int main() {
uint8_t val = 3, dup[8];
uint8x16_t v1 = vld1q_dup_u8(&val);
uint8x16_t v2 = vqtbl1q_u8(v1, v1);
vst1q_u8(dup, v1);
vst1q_u8(dup, v2);
uint8x16_t v = vld1q_dup_u8(&val);
vst1q_u8(dup, v);
return 0;
}" SNAPPY_HAVE_NEON)
#check RVV 1.0 need __riscv_ prefix
check_cxx_source_compiles("
#include <riscv_vector.h>
#include <stdint.h>
#include <stddef.h>
int main() {
uint8_t val = 3, dup[8];
size_t vl = __riscv_vsetvl_e8m1(8);
vuint8m1_t v = __riscv_vmv_v_x_u8m1(val, vl);
return 0;
}" SNAPPY_RVV_1)
#check RVV 0.7.1 not __riscv_ prefix
check_cxx_source_compiles("
#include <riscv_vector.h>
#include <stdint.h>
#include <stddef.h>
int main() {
uint8_t val = 3, dup[8];
size_t vl = vsetvl_e8m1(8);
vuint8m1_t v = vmv_v_x_u8m1(val, vl);
return 0;
}" SNAPPY_RVV_0_7)
include(CheckSymbolExists)
check_symbol_exists("mmap" "sys/mman.h" HAVE_FUNC_MMAP)
check_symbol_exists("sysconf" "unistd.h" HAVE_FUNC_SYSCONF)
@@ -296,7 +258,9 @@ target_sources(snappy
"snappy-stubs-internal.cc"
"snappy.cc"
"${PROJECT_BINARY_DIR}/config.h"
PUBLIC
# Only CMake 3.3+ supports PUBLIC sources in targets exported by "install".
$<$<VERSION_GREATER:CMAKE_VERSION,3.2>:PUBLIC>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/snappy-c.h>
$<INSTALL_INTERFACE:include/snappy-c.h>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/snappy-sinksource.h>
@@ -333,9 +297,6 @@ if(SNAPPY_BUILD_TESTS OR SNAPPY_BUILD_BENCHMARKS)
# Test files include snappy-test.h, HAVE_CONFIG_H must be defined.
target_compile_definitions(snappy_test_support PUBLIC -DHAVE_CONFIG_H)
if(BUILD_SHARED_LIBS)
set_target_properties(snappy_test_support PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif(BUILD_SHARED_LIBS)
target_link_libraries(snappy_test_support snappy)

View File

@@ -1,28 +0,0 @@
Copyright 2011, Google Inc.
All rights reserved.
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.

View File

@@ -1,26 +0,0 @@
module(
name = "snappy",
version = "1.2.2",
compatibility_level = 1,
)
bazel_dep(name = "rules_cc", version = "0.2.4")
bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(
name = "googletest",
version = "1.17.0.bcr.2",
dev_dependency = True,
repo_name = "com_google_googletest",
)
bazel_dep(
name = "google_benchmark",
version = "1.9.5",
dev_dependency = True,
repo_name = "com_google_benchmark",
)
bazel_dep(
name = "platforms",
version = "1.0.0",
)

15
NEWS
View File

@@ -1,18 +1,3 @@
Snappy v1.2.2, Mar 26th 2025:
* We added a new compression level in v1.2.1 which compresses a bit
denser but slower. Decompression speed should be even faster with it.
* We fixed a very old issue of data corruption when compressed size
exceeds 4GB. This can happen when you compress data close to 4GB
and it's incompressible, for example, random data.
* Started to use minimum CMake 3.10 because older ones are not
planned to be supported.
* Various other small fixes and performance improvements (especially
for clang).
Snappy v1.1.10, Mar 8th 2023:
* Performance improvements

View File

@@ -140,10 +140,10 @@ explicitly supports the following:
1. C++11
2. Clang (gcc and MSVC are best-effort).
3. Low level optimizations (e.g. assembly or equivalent intrinsics) for:
- [x86](https://en.wikipedia.org/wiki/X86)
- [x86-64](https://en.wikipedia.org/wiki/X86-64)
- ARMv7 (32-bit)
- ARMv8 (AArch64)
1. [x86](https://en.wikipedia.org/wiki/X86)
2. [x86-64](https://en.wikipedia.org/wiki/X86-64)
3. ARMv7 (32-bit)
4. ARMv8 (AArch64)
4. Supports only the Snappy compression scheme as described in
[format_description.txt](format_description.txt).
5. CMake for building

View File

View File

@@ -58,12 +58,6 @@
/* Define to 1 if you target processors with NEON and have <arm_neon.h>. */
#cmakedefine01 SNAPPY_HAVE_NEON
/* Define to 1 if you target processors with RVV1.0 and have <riscv_vector.h>. */
#cmakedefine01 SNAPPY_RVV_1
/* Define to 1 if you target processors with RVV0.7 and have <riscv_vector.h>. */
#cmakedefine01 SNAPPY_RVV_0_7
/* Define to 1 if you have <arm_neon.h> and <arm_acle.h> and want to optimize
compression speed by using __crc32cw from <arm_acle.h>. */
#cmakedefine01 SNAPPY_HAVE_NEON_CRC32

View File

@@ -46,24 +46,7 @@
#include <arm_neon.h>
#endif
#if SNAPPY_RVV_1 || SNAPPY_RVV_0_7
#define SNAPPY_HAVE_RVV 1
#include <riscv_vector.h>
#else
#define SNAPPY_HAVE_RVV 0
#endif
#ifdef SNAPPY_RVV_1
#define VSETVL_E8M2 __riscv_vsetvl_e8m2
#define VLE8_V_U8M2 __riscv_vle8_v_u8m2
#define VSE8_V_U8M2 __riscv_vse8_v_u8m2
#elif SNAPPY_RVV_0_7
#define VSETVL_E8M2 vsetvl_e8m2
#define VLE8_V_U8M2 vle8_v_u8m2
#define VSE8_V_U8M2 vse8_v_u8m2
#endif
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1
#else
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0
@@ -78,7 +61,7 @@ using V128 = __m128i;
#elif SNAPPY_HAVE_NEON
using V128 = uint8x16_t;
#endif
// Load 128 bits of integer data. `src` must be 16-byte aligned.
inline V128 V128_Load(const V128* src);
@@ -127,8 +110,6 @@ inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
}
inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); }
#endif
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
@@ -191,10 +172,9 @@ char* CompressFragment(const char* input,
// loading from s2 + n.
//
// Separate implementation for 64-bit, little-endian cpus.
// riscv and little-endian cpu choose this routinue can be done faster too.
#if !SNAPPY_IS_BIG_ENDIAN && \
(defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || \
defined(ARCH_ARM) || (defined(__riscv) && (__riscv_xlen == 64)))
defined(ARCH_ARM))
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
const char* s2,
const char* s2_limit,

View File

@@ -304,8 +304,8 @@ class Bits {
void operator=(const Bits&);
};
// In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension.
#if HAVE_BUILTIN_CTZ
inline int Bits::Log2FloorNonZero(uint32_t n) {
assert(n != 0);
// (31 ^ x) is equivalent to (31 - x) for x in [0, 31]. An easy proof
@@ -393,7 +393,6 @@ inline int Bits::FindLSBSetNonZero(uint32_t n) {
#endif // End portable versions.
// In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension.
#if HAVE_BUILTIN_CTZ
inline int Bits::FindLSBSetNonZero64(uint64_t n) {

117
snappy.cc
View File

@@ -74,7 +74,6 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <utility>
@@ -281,8 +280,6 @@ inline char* IncrementalCopySlow(const char* src, char* op,
// 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by
// calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and
// MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively.
template <size_t... indexes>
inline constexpr std::array<char, sizeof...(indexes)> MakePatternMaskBytes(
int index_offset, int pattern_size, index_sequence<indexes...>) {
@@ -300,6 +297,7 @@ MakePatternMaskBytesTable(int index_offset,
MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1,
make_index_sequence</*indexes=*/sizeof(V128)>())...};
}
// This is an array of shuffle control masks that can be used as the source
// operand for PSHUFB to permute the contents of the destination XMM register
// into a repeating byte pattern.
@@ -330,6 +328,7 @@ static inline V128 LoadPattern(const char* src, const size_t pattern_size) {
return V128_Shuffle(V128_LoadU(reinterpret_cast<const V128*>(src)),
generation_mask);
}
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
static inline std::pair<V128 /* pattern */, V128 /* reshuffle_mask */>
LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) {
@@ -345,6 +344,7 @@ LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) {
pattern_reshuffle_masks[pattern_size - 1].data()));
return {pattern, reshuffle_mask};
}
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Fallback for when we need to copy while extending the pattern, for example
@@ -493,6 +493,7 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
LoadPatternAndReshuffleMask(src, pattern_size);
V128 pattern = pattern_and_reshuffle_mask.first;
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
// There is at least one, and at most four 16-byte blocks. Writing four
// conditionals instead of a loop allows FDO to layout the code with
// respect to the actual probabilities of each length.
@@ -519,6 +520,7 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
LoadPatternAndReshuffleMask(src, pattern_size);
V128 pattern = pattern_and_reshuffle_mask.first;
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
// This code path is relatively cold however so we save code size
// by avoiding unrolling and vectorizing.
//
@@ -957,8 +959,6 @@ emit_remainder:
char* CompressFragmentDoubleHash(const char* input, size_t input_size, char* op,
uint16_t* table, const int table_size,
uint16_t* table2, const int table_size2) {
(void)table_size2;
assert(table_size == table_size2);
// "ip" is the input pointer, and "op" is the output pointer.
const char* ip = input;
assert(input_size <= kBlockSize);
@@ -1224,7 +1224,7 @@ inline bool Copy64BytesWithPatternExtension(ptrdiff_t dst, size_t offset) {
void MemCopy64(char* dst, const void* src, size_t size) {
// Always copy this many bytes. If that's below size then copy the full 64.
constexpr int kShortMemCopy = 32;
(void)kShortMemCopy;
assert(size <= 64);
assert(std::less_equal<const void*>()(static_cast<const char*>(src) + size,
dst) ||
@@ -1243,27 +1243,6 @@ void MemCopy64(char* dst, const void* src, size_t size) {
data = _mm256_lddqu_si256(static_cast<const __m256i *>(src) + 1);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
}
// RVV acceleration available on RISC-V when compiled with -march=rv64gcv
#elif defined(__riscv) && SNAPPY_HAVE_RVV
// Cast pointers to the type we will operate on.
unsigned char* dst_ptr = reinterpret_cast<unsigned char*>(dst);
const unsigned char* src_ptr = reinterpret_cast<const unsigned char*>(src);
size_t remaining_bytes = size;
// Loop as long as there are bytes remaining to be copied.
while (remaining_bytes > 0) {
// Set vector configuration: e8 (8-bit elements), m2 (LMUL=2).
// Use e8m2 configuration to maximize throughput.
size_t vl = VSETVL_E8M2(remaining_bytes);
// Load data from the current source pointer.
vuint8m2_t vec = VLE8_V_U8M2(src_ptr, vl);
// Store data to the current destination pointer.
VSE8_V_U8M2(dst_ptr, vec, vl);
// Update pointers and the remaining count.
src_ptr += vl;
dst_ptr += vl;
remaining_bytes -= vl;
}
#else
std::memmove(dst, src, kShortMemCopy);
// Profiling shows that nearly all copies are short.
@@ -1363,32 +1342,6 @@ inline size_t AdvanceToNextTagX86Optimized(const uint8_t** ip_p, size_t* tag) {
return tag_type;
}
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
inline size_t AdvanceToNextTagRVOptimized(const uint8_t** ip_p, size_t* tag) {
const uint8_t*& ip = *ip_p;
// This section is crucial for the throughput of the decompression loop.
// The latency of an iteration is fundamentally constrained by the data chain on ip:
// ip -> c = *tag -> literal_len = c >> 2, tag_type = c & 3
// -> literal_advance = literal_len + 2, copy_advance = tag_type + 1
// -> next_ip = ip + literal_advance OR ip + copy_advance (literal vs copy)
// -> *tag = byte at (next_ip - 1); ip = next_ip
//
// Base RISC-V has no x86-style cmov and no AArch64 csinc on the same shape; this
// computes both candidate advances and both load offsets, then selects with
// (is_literal ? ... : ...). With the Zicond extension (czero.eqz / czero.nez), those
// selections typically lower to branchless conditional-zero ops instead of a
// hard-to-predict literal/copy branch, which is why this form tends to win there.
const size_t literal_len = *tag >> 2;
const size_t tag_type = *tag & 3;
const bool is_literal = (tag_type == 0);
const size_t copy_advance = tag_type + 1;
const size_t literal_advance = literal_len + 2;
const uint8_t* next_ip = is_literal ? (ip + literal_advance) : (ip + copy_advance);
*tag = is_literal ? ip[literal_advance - 1] : ip[copy_advance - 1];
ip = next_ip;
return tag_type;
}
// Extract the offset for copy-1 and copy-2 returns 0 for literals or copy-4.
inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) {
// For x86 non-static storage works better. For ARM static storage is better.
@@ -1401,13 +1354,7 @@ inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) {
reinterpret_cast<const char*>(&kExtractMasksCombined) + 2 * tag_type,
sizeof(result));
return val & result;
// For AArch64 and RISC-V, use a bit-twiddling trick to extract the mask from a
// single combined constant instead of a lookup table. The constant packs multiple
// 16-bit masks based on tag_type (see implementation below). The code calculates
// the shift amount from tag_type, right-shifts the constant to move the desired
// mask to the LSB position, then extracts it with & 0xFFFF. This branchless
// approach is often more performant on modern CPUs.
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
#elif defined(__aarch64__)
constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
return val & static_cast<uint32_t>(
(kExtractMasksCombined >> (tag_type * 16)) & 0xFFFF);
@@ -1471,11 +1418,6 @@ std::pair<const uint8_t*, ptrdiff_t> DecompressBranchless(
// We never need more than 16 bits. Doing a Load16 allows the compiler
// to elide the masking operation in ExtractOffset.
next = LittleEndian::Load16(old_ip);
#elif defined(__riscv)
size_t tag_type = AdvanceToNextTagRVOptimized(&ip, &tag);
// We never need more than 16 bits. Doing a Load16 allows the compiler
// to elide the masking operation in ExtractOffset.
next = LittleEndian::Load16(old_ip);
#else
size_t tag_type = AdvanceToNextTagX86Optimized(&ip, &tag);
next = LittleEndian::Load32(old_ip);
@@ -1554,7 +1496,7 @@ class SnappyDecompressor {
// If ip < ip_limit_min_maxtaglen_ it's safe to read kMaxTagLength from
// buffer.
const char* ip_limit_min_maxtaglen_;
uint64_t peeked_; // Bytes peeked from reader (need to skip)
uint32_t peeked_; // Bytes peeked from reader (need to skip)
bool eof_; // Hit end of input without an error?
char scratch_[kMaximumTagLength]; // See RefillTag().
@@ -1745,8 +1687,7 @@ constexpr uint32_t CalculateNeeded(uint8_t tag) {
#if __cplusplus >= 201402L
constexpr bool VerifyCalculateNeeded() {
for (int i = 0; i < 1; i++) {
if (CalculateNeeded(i) != static_cast<uint32_t>((char_table[i] >> 11)) + 1)
return false;
if (CalculateNeeded(i) != (char_table[i] >> 11) + 1) return false;
}
return true;
}
@@ -1782,7 +1723,7 @@ bool SnappyDecompressor::RefillTag() {
assert(needed <= sizeof(scratch_));
// Read more bytes from reader if needed
uint64_t nbuf = ip_limit_ - ip;
uint32_t nbuf = ip_limit_ - ip;
if (nbuf < needed) {
// Stitch together bytes from ip and reader to form the word
// contents. We store the needed bytes in "scratch_". They
@@ -1795,7 +1736,7 @@ bool SnappyDecompressor::RefillTag() {
size_t length;
const char* src = reader_->Peek(&length);
if (length == 0) return false;
uint64_t to_add = std::min<uint64_t>(needed - nbuf, length);
uint32_t to_add = std::min<uint32_t>(needed - nbuf, length);
std::memcpy(scratch_ + nbuf, src, to_add);
nbuf += to_add;
reader_->Skip(to_add);
@@ -1849,16 +1790,11 @@ bool GetUncompressedLength(Source* source, uint32_t* result) {
return decompressor.ReadUncompressedLength(result);
}
size_t Compress(Source* reader, Sink* writer) {
return Compress(reader, writer, CompressionOptions{});
}
size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
assert(options.level == 1 || options.level == 2);
int token = 0;
size_t written = 0;
size_t N = reader->Available();
assert(N <= 0xFFFFFFFFu);
const size_t uncompressed_size = N;
char ulength[Varint::kMax32];
char* p = Varint::Encode32(ulength, N);
@@ -1950,16 +1886,16 @@ class SnappyIOVecReader : public Source {
if (total_size > 0 && curr_size_remaining_ == 0) Advance();
}
~SnappyIOVecReader() override = default;
~SnappyIOVecReader() = default;
size_t Available() const override { return total_size_remaining_; }
size_t Available() const { return total_size_remaining_; }
const char* Peek(size_t* len) override {
const char* Peek(size_t* len) {
*len = curr_size_remaining_;
return curr_pos_;
}
void Skip(size_t n) override {
void Skip(size_t n) {
while (n >= curr_size_remaining_ && n > 0) {
n -= curr_size_remaining_;
Advance();
@@ -2360,12 +2296,6 @@ bool IsValidCompressed(Source* compressed) {
return InternalUncompress(compressed, &writer);
}
void RawCompress(const char* input, size_t input_length, char* compressed,
size_t* compressed_length) {
RawCompress(input, input_length, compressed, compressed_length,
CompressionOptions{});
}
void RawCompress(const char* input, size_t input_length, char* compressed,
size_t* compressed_length, CompressionOptions options) {
ByteArraySource reader(input, input_length);
@@ -2376,12 +2306,6 @@ void RawCompress(const char* input, size_t input_length, char* compressed,
*compressed_length = (writer.CurrentDestination() - compressed);
}
void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
char* compressed, size_t* compressed_length) {
RawCompressFromIOVec(iov, uncompressed_length, compressed, compressed_length,
CompressionOptions{});
}
void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
char* compressed, size_t* compressed_length,
CompressionOptions options) {
@@ -2393,11 +2317,6 @@ void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
*compressed_length = writer.CurrentDestination() - compressed;
}
size_t Compress(const char* input, size_t input_length,
std::string* compressed) {
return Compress(input, input_length, compressed, CompressionOptions{});
}
size_t Compress(const char* input, size_t input_length, std::string* compressed,
CompressionOptions options) {
// Pre-grow the buffer to the max length of the compressed output
@@ -2410,11 +2329,6 @@ size_t Compress(const char* input, size_t input_length, std::string* compressed,
return compressed_length;
}
size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt,
std::string* compressed) {
return CompressFromIOVec(iov, iov_cnt, compressed, CompressionOptions{});
}
size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt,
std::string* compressed, CompressionOptions options) {
// Compute the number of bytes to be compressed.
@@ -2619,6 +2533,7 @@ bool SnappyScatteredWriter<Allocator>::SlowAppendFromSelf(size_t offset,
class SnappySinkAllocator {
public:
explicit SnappySinkAllocator(Sink* dest) : dest_(dest) {}
~SnappySinkAllocator() {}
char* Allocate(int size) {
Datablock block(new char[size], size);

View File

@@ -64,9 +64,6 @@ namespace snappy {
// faster decompression speeds than snappy:1 and zstd:-3.
int level = DefaultCompressionLevel();
constexpr CompressionOptions() = default;
constexpr CompressionOptions(int compression_level)
: level(compression_level) {}
static constexpr int MinCompressionLevel() { return 1; }
static constexpr int MaxCompressionLevel() { return 2; }
static constexpr int DefaultCompressionLevel() { return 1; }
@@ -78,10 +75,8 @@ namespace snappy {
// Compress the bytes read from "*reader" and append to "*writer". Return the
// number of bytes written.
// First version is to preserve ABI.
size_t Compress(Source* reader, Sink* writer);
size_t Compress(Source* reader, Sink* writer,
CompressionOptions options);
CompressionOptions options = {});
// Find the uncompressed length of the given stream, as given by the header.
// Note that the true length could deviate from this; the stream could e.g.
@@ -100,22 +95,16 @@ namespace snappy {
// Original contents of *compressed are lost.
//
// REQUIRES: "input[]" is not an alias of "*compressed".
// First version is to preserve ABI.
size_t Compress(const char* input, size_t input_length,
std::string* compressed);
size_t Compress(const char* input, size_t input_length,
std::string* compressed, CompressionOptions options);
std::string* compressed, CompressionOptions options = {});
// Same as `Compress` above but taking an `iovec` array as input. Note that
// this function preprocesses the inputs to compute the sum of
// `iov[0..iov_cnt-1].iov_len` before reading. To avoid this, use
// `RawCompressFromIOVec` below.
// First version is to preserve ABI.
size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt,
std::string* compressed);
size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt,
std::string* compressed,
CompressionOptions options);
CompressionOptions options = {});
// Decompresses "compressed[0..compressed_length-1]" to "*uncompressed".
// Original contents of "*uncompressed" are lost.
@@ -159,18 +148,14 @@ namespace snappy {
// ... Process(output, output_length) ...
// delete [] output;
void RawCompress(const char* input, size_t input_length, char* compressed,
size_t* compressed_length);
void RawCompress(const char* input, size_t input_length, char* compressed,
size_t* compressed_length, CompressionOptions options);
size_t* compressed_length, CompressionOptions options = {});
// Same as `RawCompress` above but taking an `iovec` array as input. Note that
// `uncompressed_length` is the total number of bytes to be read from the
// elements of `iov` (_not_ the number of elements in `iov`).
void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
char* compressed, size_t* compressed_length);
void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
char* compressed, size_t* compressed_length,
CompressionOptions options);
CompressionOptions options = {});
// Given data in "compressed[0..compressed_length-1]" generated by
// calling the Snappy::Compress routine, this routine

View File

@@ -31,10 +31,12 @@
#include <string>
#include <vector>
#include "snappy-test.h"
#include "benchmark/benchmark.h"
#include "snappy-internal.h"
#include "snappy-sinksource.h"
#include "snappy-test.h"
#include "snappy.h"
#include "snappy_test_data.h"
@@ -42,7 +44,7 @@ namespace snappy {
namespace {
void FilesAndLevels(benchmark::internal::Benchmark* benchmark) {
void FilesAndLevels(::testing::Benchmark* benchmark) {
for (int i = 0; i < ARRAYSIZE(kTestDataFiles); ++i) {
for (int level = snappy::CompressionOptions::MinCompressionLevel();
level <= snappy::CompressionOptions::MaxCompressionLevel(); ++level) {
@@ -62,9 +64,8 @@ void BM_UFlat(benchmark::State& state) {
kTestDataFiles[file_index].size_limit);
std::string zcontents;
snappy::Compress(
contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{/*level=*/static_cast<int>(state.range(1))});
snappy::Compress(contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{.level = state.range(1)});
char* dst = new char[contents.size()];
for (auto s : state) {
@@ -128,9 +129,8 @@ void BM_UValidate(benchmark::State& state) {
kTestDataFiles[file_index].size_limit);
std::string zcontents;
snappy::Compress(
contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{/*level=*/static_cast<int>(state.range(1))});
snappy::Compress(contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{.level = state.range(1)});
for (auto s : state) {
CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
@@ -193,7 +193,7 @@ void BM_UIOVecSource(benchmark::State& state) {
size_t zsize = 0;
for (auto s : state) {
snappy::RawCompressFromIOVec(iov, contents.size(), dst, &zsize,
snappy::CompressionOptions{/*level=*/level});
snappy::CompressionOptions{.level = level});
benchmark::DoNotOptimize(iov);
}
state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
@@ -226,7 +226,7 @@ void BM_UIOVecSink(benchmark::State& state) {
// Uncompress into an iovec containing ten entries.
const int kNumEntries = 10;
struct iovec iov[kNumEntries];
char* dst = new char[contents.size()];
char *dst = new char[contents.size()];
size_t used_so_far = 0;
for (int i = 0; i < kNumEntries; ++i) {
iov[i].iov_base = dst + used_so_far;
@@ -267,9 +267,8 @@ void BM_UFlatSink(benchmark::State& state) {
kTestDataFiles[file_index].size_limit);
std::string zcontents;
snappy::Compress(
contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{/*level=*/static_cast<int>(state.range(1))});
snappy::Compress(contents.data(), contents.size(), &zcontents,
snappy::CompressionOptions{.level = state.range(1)});
char* dst = new char[contents.size()];
for (auto s : state) {
@@ -305,7 +304,7 @@ void BM_ZFlat(benchmark::State& state) {
size_t zsize = 0;
for (auto s : state) {
snappy::RawCompress(contents.data(), contents.size(), dst, &zsize,
snappy::CompressionOptions{/*level=*/level});
snappy::CompressionOptions{.level = level});
benchmark::DoNotOptimize(dst);
}
state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
@@ -341,7 +340,7 @@ void BM_ZFlatAll(benchmark::State& state) {
for (auto s : state) {
for (int i = 0; i < num_files; ++i) {
snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i],
&zsize, snappy::CompressionOptions{/*level=*/level});
&zsize, snappy::CompressionOptions{.level = level});
benchmark::DoNotOptimize(dst);
}
}
@@ -378,7 +377,7 @@ void BM_ZFlatIncreasingTableSize(benchmark::State& state) {
for (auto s : state) {
for (size_t i = 0; i < contents.size(); ++i) {
snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i],
&zsize, snappy::CompressionOptions{/*level=*/level});
&zsize, snappy::CompressionOptions{.level = level});
benchmark::DoNotOptimize(dst);
}
}

View File

@@ -44,7 +44,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::string compressed;
size_t compressed_size =
snappy::Compress(input.data(), input.size(), &compressed,
snappy::CompressionOptions{/*level=*/level});
snappy::CompressionOptions{.level = level});
(void)compressed_size; // Variable only used in debug builds.
assert(compressed_size == compressed.size());

View File

@@ -27,7 +27,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <random>
@@ -485,18 +484,6 @@ TEST(Snappy, MaxBlowup) {
Verify(input);
}
// Issue #201, when output is more than 4GB, we had a data corruption bug.
// We cannot run this test always because of CI constraints.
TEST(Snappy, DISABLED_MoreThan4GB) {
std::mt19937 rng;
std::uniform_int_distribution<int> uniform_byte(0, 255);
std::string input;
input.resize((1ull << 32) - 1);
for (uint64_t i = 0; i < ((1ull << 32) - 1); ++i)
input[i] = static_cast<char>(uniform_byte(rng));
Verify(input);
}
TEST(Snappy, RandomData) {
std::minstd_rand0 rng(snappy::GetFlag(FLAGS_test_random_seed));
std::uniform_int_distribution<int> uniform_0_to_3(0, 3);