Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
- name: Run clang-format check
run: |
# Find all C/C++ files and check formatting
FILES=$(find . -regex '.*\.\(cpp\|hpp\|cc\|c\|h\)')
FILES=$(find . -path './third_party' -prune -o \
-regex '.*\.\(cpp\|hpp\|cc\|c\|h\)' -print)
echo "Checking formatting on:"
echo "$FILES"
clang-format --version
Expand Down
30 changes: 27 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,17 @@ if (PIXIE_TESTS)
include(GoogleTest)
endif ()

# Downstream consumers link this target; Pixie itself remains header-only.
# PivCo-Huffman is Pixie's compiled codec backend.
include(cmake/PivCo.cmake)

# Downstream consumers link this target. Most Pixie implementations remain
# header-only; the Huffman codec is carried as a transitive static dependency.
add_library(pixie INTERFACE)
add_library(pixie::pixie ALIAS pixie)
target_compile_features(pixie INTERFACE cxx_std_20)
target_include_directories(pixie INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(pixie INTERFACE pixie::pivco)

# Keep Pixie's own targets warning-clean without imposing -Werror on consumers.
function (pixie_enable_project_warnings target)
Expand Down Expand Up @@ -300,6 +305,15 @@ if (PIXIE_TESTS)
PRIVATE ${sdsl_lite_SOURCE_DIR}/include)
endif ()

add_executable(huffman_tests
src/tests/huffman_tests.cpp)
target_link_libraries(huffman_tests
PRIVATE
pixie::pixie
gtest
gtest_main
${PIXIE_DIAGNOSTICS_LIBS})

set(PIXIE_TEST_TARGETS
bit_algorithms_unittests
rank_select_unittests
Expand All @@ -314,7 +328,8 @@ if (PIXIE_TESTS)
excess_positions_tests
select512_experimental_tests
excess_record_lows_tests
rmq_tests)
rmq_tests
huffman_tests)
foreach (test_target IN LISTS PIXIE_TEST_TARGETS)
pixie_enable_project_warnings(${test_target})
gtest_discover_tests(${test_target}
Expand Down Expand Up @@ -475,6 +490,14 @@ if (PIXIE_BENCHMARKS)
benchmark_main
${PIXIE_DIAGNOSTICS_LIBS})

add_executable(huffman_file_benchmarks
src/benchmarks/huffman_file_benchmarks.cpp)
target_link_libraries(huffman_file_benchmarks
PRIVATE
pixie::pixie
benchmark
${PIXIE_DIAGNOSTICS_LIBS})

set(PIXIE_BENCHMARK_TARGETS
rank_select_benchmarks
rmm_benchmarks
Expand All @@ -488,7 +511,8 @@ if (PIXIE_BENCHMARKS)
dfuds_tree_benchmarks
alignment_comparison_benchmarks
excess_positions_benchmarks
select512_benchmarks)
select512_benchmarks
huffman_file_benchmarks)
if (PIXIE_THIRD_PARTY_BACKENDS)
list(APPEND PIXIE_BENCHMARK_TARGETS
rmm_sdsl_benchmarks)
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
- **Range min-max tree**: fundamental primitive for efficient computation of excess queries mainly for navigation in balanced parenthesis sequences.
- **Succinct trees**: static variants of $2$-bit per entry trees, i.e. LOUDS, DFUDS, BP (Based on Euler tour and Ferrada-Navarro style).
- **Wavelet tree**, i.e. static structure that supposts rank/select on arbitrary finite alphabets, supports building a Huffman archieve with fast extraction of arbitrary segment.
- **PivCo-Huffman codec** with scalar, AVX2, AVX-512, and NEON kernels selected
for the build host.
- Succinct **cartesian tree** and a state of the art solution to static **RMQ** (array is immutable, queries are not known in advance).

---
Expand Down Expand Up @@ -143,5 +145,5 @@ int main() {

Copyright 2026 Pixie contributors.

Pixie is licensed under the [Apache License 2.0](LICENSE). Optional
third-party benchmark and backend integrations retain their own licenses.
Pixie is licensed under the [Apache License 2.0](LICENSE). Vendored components
and optional backend integrations retain their own license notices.
129 changes: 129 additions & 0 deletions cmake/PivCo.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Compiled Apache-2.0 PivCo-Huffman backend.
#
# The codec is compiled once per available architecture tier. The facade in
# include/pixie/huffman/pivco_huffman.h remains a thin C++ CRTP adapter.

set(PIXIE_PIVCO_ROOT
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/pivco")

if (NOT EXISTS "${PIXIE_PIVCO_ROOT}/LICENSE")
message(FATAL_ERROR
"The PivCo-Huffman sources and Apache-2.0 LICENSE are required")
endif ()

set(PIXIE_PIVCO_INCLUDE
"${PIXIE_PIVCO_ROOT}/include")
set(PIXIE_PIVCO_SRC
"${PIXIE_PIVCO_ROOT}/src")

set(PIXIE_PIVCO_COMMON_SOURCES
"${PIXIE_PIVCO_SRC}/huffman_table.c"
"${PIXIE_PIVCO_SRC}/joint_lengths.c"
"${PIXIE_PIVCO_SRC}/pivco_huffman.c"
"${PIXIE_PIVCO_SRC}/pivcohuf_file.c")

add_library(pixie_pivco_scalar OBJECT
"${PIXIE_PIVCO_SRC}/pivco_huffman_codec.c")
target_compile_definitions(pixie_pivco_scalar
PRIVATE PIVCO_BACKEND_SCALAR=1)

set(PIXIE_PIVCO_OBJECTS
$<TARGET_OBJECTS:pixie_pivco_scalar>)
set(PIXIE_PIVCO_DEFINITIONS)
set(PIXIE_PIVCO_OPTIONS)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" PIXIE_PIVCO_PROCESSOR)

if (PIXIE_PIVCO_PROCESSOR MATCHES "^(x86_64|amd64)$")
list(APPEND PIXIE_PIVCO_DEFINITIONS
PIVCO_HAS_AVX2=1
PIVCO_HAS_SSE4=1)
list(APPEND PIXIE_PIVCO_OPTIONS
-mavx2
-mbmi2
-msse4.1
-mpopcnt)
Comment on lines +37 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate the x86 backend on the required CPU features

On every x86_64/amd64 build, this unconditionally defines the AVX2 backend and compiles all codec and common sources with AVX2, BMI2, SSE4.1, and POPCNT. Since pivco_encode and pivco_decode statically select the x86 worker whenever these definitions exist, binaries run on older x86-64 CPUs—or builds configured with PIXIE_DISABLE_BMI2=ON—can execute unsupported instructions instead of using the included scalar fallback. Detect the required features or honor the fallback options before enabling this tier.

AGENTS.md reference: AGENTS.md:L348-L353

Useful? React with 👍 / 👎.


add_library(pixie_pivco_x86 OBJECT
"${PIXIE_PIVCO_SRC}/pivco_huffman_codec.c")
target_compile_definitions(pixie_pivco_x86
PRIVATE PIVCO_BACKEND_X86=1)
list(APPEND PIXIE_PIVCO_OBJECTS
$<TARGET_OBJECTS:pixie_pivco_x86>)
list(APPEND PIXIE_PIVCO_COMMON_SOURCES
"${PIXIE_PIVCO_SRC}/pivco_huffman_x86_tables.c")

if (NOT DISABLE_AVX512 AND EXISTS "/proc/cpuinfo")
file(READ "/proc/cpuinfo" PIXIE_PIVCO_CPUINFO)
string(FIND "${PIXIE_PIVCO_CPUINFO}" "avx512_vbmi2"
PIXIE_PIVCO_AVX512_POSITION)
include(CheckCCompilerFlag)
check_c_compiler_flag("-mavx512vbmi2" PIXIE_PIVCO_COMPILER_HAS_AVX512)
if (PIXIE_PIVCO_COMPILER_HAS_AVX512 AND
NOT PIXIE_PIVCO_AVX512_POSITION EQUAL -1)
list(APPEND PIXIE_PIVCO_DEFINITIONS
PIVCO_HAS_AVX512=1)
list(APPEND PIXIE_PIVCO_OPTIONS
-mavx512f
-mavx512bw
-mavx512vl
-mavx512vbmi
-mavx512vbmi2
-mavx512vpopcntdq)
add_library(pixie_pivco_avx512 OBJECT
"${PIXIE_PIVCO_SRC}/pivco_huffman_codec.c")
target_compile_definitions(pixie_pivco_avx512
PRIVATE PIVCO_BACKEND_AVX512=1)
list(APPEND PIXIE_PIVCO_OBJECTS
$<TARGET_OBJECTS:pixie_pivco_avx512>)
endif ()
endif ()
elseif (PIXIE_PIVCO_PROCESSOR MATCHES "^(aarch64|arm64)$")
list(APPEND PIXIE_PIVCO_DEFINITIONS PIVCO_HAS_NEON=1)
add_library(pixie_pivco_neon OBJECT
"${PIXIE_PIVCO_SRC}/pivco_huffman_codec.c")
target_compile_definitions(pixie_pivco_neon
PRIVATE PIVCO_BACKEND_NEON=1)
list(APPEND PIXIE_PIVCO_OBJECTS
$<TARGET_OBJECTS:pixie_pivco_neon>)
list(APPEND PIXIE_PIVCO_COMMON_SOURCES
"${PIXIE_PIVCO_SRC}/pivco_huffman_neon_tables.c")
endif ()

set(PIXIE_PIVCO_CODEC_TARGETS pixie_pivco_scalar)
if (TARGET pixie_pivco_x86)
list(APPEND PIXIE_PIVCO_CODEC_TARGETS pixie_pivco_x86)
endif ()
if (TARGET pixie_pivco_avx512)
list(APPEND PIXIE_PIVCO_CODEC_TARGETS pixie_pivco_avx512)
endif ()
if (TARGET pixie_pivco_neon)
list(APPEND PIXIE_PIVCO_CODEC_TARGETS pixie_pivco_neon)
endif ()

foreach (codec_target IN LISTS PIXIE_PIVCO_CODEC_TARGETS)
target_include_directories(${codec_target}
PRIVATE
"${PIXIE_PIVCO_INCLUDE}"
"${PIXIE_PIVCO_SRC}")
target_compile_definitions(${codec_target}
PRIVATE ${PIXIE_PIVCO_DEFINITIONS})
target_compile_options(${codec_target}
PRIVATE -O3 ${PIXIE_PIVCO_OPTIONS})
Comment on lines +110 to +111

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Instrument the new C backend in the ASan preset

When the repository's asan preset is used, ENABLE_ADDRESS_SANITIZER adds sanitizer flags only to CMAKE_CXX_FLAGS; these newly added .c targets therefore compile without -fsanitize=address and are additionally forced to -O3. As a result, memory errors inside the codec—including out-of-bounds SIMD stores—are not checked by the fallback validation job even though the C++ test executable links ASan. Apply the sanitizer settings to the C targets as well.

AGENTS.md reference: AGENTS.md:L356-L357

Useful? React with 👍 / 👎.

endforeach ()

add_library(pixie_pivco STATIC
${PIXIE_PIVCO_COMMON_SOURCES}
${PIXIE_PIVCO_OBJECTS})
add_library(pixie::pivco ALIAS pixie_pivco)
target_include_directories(pixie_pivco
PUBLIC
"${PIXIE_PIVCO_INCLUDE}"
PRIVATE
"${PIXIE_PIVCO_SRC}")
target_compile_definitions(pixie_pivco
PRIVATE ${PIXIE_PIVCO_DEFINITIONS})
target_compile_options(pixie_pivco
PRIVATE -O3 ${PIXIE_PIVCO_OPTIONS})
if (UNIX AND NOT APPLE)
target_link_libraries(pixie_pivco PUBLIC m)
endif ()
89 changes: 89 additions & 0 deletions include/pixie/huffman.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma once

/**
* @file huffman.h
* @brief Common CRTP contract for Huffman entropy codecs.
*
* PivCo-Huffman ("Pivot-Coded Huffman") reuses the wavelet-tree "tree of
* bitmaps" layout to turn sequential, bit-by-bit Huffman-tree traversals into
* vectorizable operations. A codec encodes a byte sequence into a compressed
* stream by building a Huffman-shaped tree of per-node bitmaps, and decodes it
* back by traversing that tree. Concrete implementations live under
* `<pixie/huffman/implementations.h>`.
*
* @see Marcin Zukowski, "PivCo-Huffman", v1.0 (2026).
*/

#include <cstddef>
#include <cstdint>
#include <span>
#include <vector>

namespace pixie {

/**
* @brief CRTP facade for Huffman entropy codecs.
*
* The contract is the source of truth for observable codec semantics. Each
* concrete implementation inherits `HuffmanBase<Impl>` and supplies the
* required `*_impl()` extension points. There is no virtual dispatch: the
* facade delegates statically through CRTP, mirroring the other Pixie families.
*
* Range and ownership conventions:
* - Symbol sequences are zero-based byte streams (`symbol_type`).
* - Compressed streams are byte-oriented views (`std::byte`).
* - The caller keeps any non-owning view alive for the codec lifetime.
*
* @tparam Impl Concrete codec type implementing the `*_impl()` contract.
*
* @see `<pixie/huffman/implementations.h>` for the available concrete
* implementations.
*/
template <class Impl>
class HuffmanBase {
public:
/** @brief Symbol type handled by the codec: one byte per symbol. */
using symbol_type = std::uint8_t;

/**
* @brief Number of symbols in the uncompressed input stream.
* @return Logical uncompressed symbol count.
*/
std::size_t uncompressed_size() const {
return impl().uncompressed_size_impl();
}

/**
* @brief Number of bytes in the compressed representation.
* @return Compressed stream size in bytes.
*/
std::size_t compressed_size() const { return impl().compressed_size_impl(); }

/**
* @brief Check whether the codec holds no data.
* @return `true` when `uncompressed_size() == 0`.
*/
bool empty() const { return uncompressed_size() == 0; }

/**
* @brief Read-only view of the compressed byte stream.
* @return Span over the serialized representation.
*
* @note The returned view is invalidated by codec destruction.
*/
std::span<const std::byte> compressed_data() const {
return impl().compressed_data_impl();
}

/**
* @brief Reconstruct the input symbol sequence.
* @return Decoded symbols of length `uncompressed_size()`.
*/
std::vector<symbol_type> decode() const { return impl().decode_impl(); }

private:
/** @brief Return this facade as its concrete CRTP implementation. */
const Impl& impl() const { return static_cast<const Impl&>(*this); }
};

} // namespace pixie
12 changes: 12 additions & 0 deletions include/pixie/huffman/implementations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

/**
* @file implementations.h
* @brief All Huffman codec implementations provided by Pixie.
*
* - `PivCoHuffman`: PivCo-Huffman file codec with scalar and
* architecture-specific SIMD kernels.
*/

#include <pixie/huffman.h>
#include <pixie/huffman/pivco_huffman.h>
Loading
Loading